diff --git a/.eslintignore b/.eslintignore
index 6de2f63d7e98adaa0c32f09013e16a7d57485681..b699cf19c45c19bee0ac8091aa741651b2589b85 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -1 +1,3 @@
-*.spec.ts
\ No newline at end of file
+*.spec.ts
+src/atlasComponents/sapi/schema.ts
+**/*.stories.ts
\ No newline at end of file
diff --git a/.eslintrc.js b/.eslintrc.js
index e01d3ce69821ff914b61facb9543909eab401ae9..5001d24d9b76175066ac3d5185d51ace55414a16 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -1,33 +1,27 @@
 module.exports = {
   root: true,
   parser: '@typescript-eslint/parser',
-  plugins: [
-    '@typescript-eslint',
-  ],
-  extends: [
-    'eslint:recommended',
-    'plugin:@typescript-eslint/eslint-recommended',
-    'plugin:@typescript-eslint/recommended',
-  ],
+  plugins: ['@typescript-eslint'],
+  extends: ["eslint:recommended", "plugin:@typescript-eslint/eslint-recommended", "plugin:@typescript-eslint/recommended", "plugin:storybook/recommended"],
   rules: {
     "@typescript-eslint/no-inferrable-types": "off",
-    "@typescript-eslint/interface-name-prefix":[0],
+    "@typescript-eslint/interface-name-prefix": [0],
     // "no-unused-vars": "off",
     "semi": "off",
-    "indent":["error", 2, {
-      "FunctionDeclaration":{
-        "body":1,
-        "parameters":2
+    "indent": ["error", 2, {
+      "FunctionDeclaration": {
+        "body": 1,
+        "parameters": 2
       }
     }],
     "@typescript-eslint/member-delimiter-style": [2, {
       "multiline": {
-          "delimiter": "none",
-          "requireLast": true
+        "delimiter": "none",
+        "requireLast": true
       },
       "singleline": {
-          "delimiter": "comma",
-          "requireLast": false
+        "delimiter": "comma",
+        "requireLast": false
       }
     }],
     "@typescript-eslint/no-unused-vars": ["warn", {
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000000000000000000000000000000000000..8acbc9780e8547d6bec7148ddbfcd05a02797ca8
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1 @@
+src/atlasComponents/sapi/schema.ts linguist-generated=true
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index c3c07d114df83ae2c2dc663da720cc5451914f31..78d9a7e7b59248e5b280835a47c7860b579061e2 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -15,10 +15,10 @@ jobs:
 
     steps:
     - uses: actions/checkout@v2
-    - name: Use Node.js 14.x for lint
+    - name: Use Node.js 16.x for lint
       uses: actions/setup-node@v1
       with:
-        node-version: '14.x'
+        node-version: '16.x'
     - run: npm i
     - run: npm run lint
 
@@ -28,7 +28,7 @@ jobs:
 
     strategy:
       matrix:
-        node-version: [12.x, 14.x, 16.x]
+        node-version: [16.x]
 
     env:
       NODE_ENV: test
@@ -47,7 +47,7 @@ jobs:
     runs-on: ubuntu-latest
     strategy:
       matrix:
-        node-version: [12.x, 14.x, 16.x]
+        node-version: [16.x]
 
     env:
       NODE_ENV: test
diff --git a/.github/workflows/docker_img.yml b/.github/workflows/docker_img.yml
index 495f946a603cb7cd81a61d4416fda31b560a64fb..47734fc27ba6540ee111646142553af53a365798 100644
--- a/.github/workflows/docker_img.yml
+++ b/.github/workflows/docker_img.yml
@@ -18,9 +18,9 @@ jobs:
       PRODUCTION: 'true'
       DOCKER_REGISTRY: 'docker-registry.ebrains.eu/siibra/'
 
-      SIIBRA_API_STABLE: 'https://siibra-api-stable.apps.hbp.eu/v1_0'
-      SIIBRA_API_RC: 'https://siibra-api-rc.apps.hbp.eu/v1_0'
-      SIIBRA_API_LATEST: 'https://siibra-api-latest.apps-dev.hbp.eu/v1_0'
+      SIIBRA_API_STABLE: 'https://siibra-api-stable.apps.hbp.eu/v2_0'
+      SIIBRA_API_RC: 'https://siibra-api-rc.apps.hbp.eu/v2_0'
+      SIIBRA_API_LATEST: 'https://siibra-api-latest.apps-dev.hbp.eu/v2_0'
 
 
     steps:
@@ -46,7 +46,7 @@ jobs:
           if [[ "$GITHUB_REF" == *hotfix* ]]
           then
             echo "Hotfix branch, using prod env..."
-            echo "BS_REST_URL=${{ env.SIIBRA_API_STABLE }}" >> $GITHUB_ENV
+            echo "BS_REST_URL=${{ env.SIIBRA_API_RC }}" >> $GITHUB_ENV
           else
             echo "Using dev env..."
             echo "BS_REST_URL=${{ env.SIIBRA_API_LATEST }}" >> $GITHUB_ENV
diff --git a/.gitignore b/.gitignore
index 05ba580503a02c6b0557b29836fa8bc2a42b4295..fadb807b38bbbade09582f298c5ace3d94b0c031 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+/.angular/cache
 node_modules
 dist
 src/res/raw
@@ -12,3 +13,5 @@ site
 *.log
 cachedKgDataset.json
 tmp
+storybook-static
+documentation.json
diff --git a/.storybook/main.js b/.storybook/main.js
new file mode 100644
index 0000000000000000000000000000000000000000..e420138441efbb7ec2a1d085d1f62e2a0ce847d2
--- /dev/null
+++ b/.storybook/main.js
@@ -0,0 +1,19 @@
+module.exports = {
+  "stories": [
+    "../src/**/*.stories.mdx",
+    "../src/**/*.stories.@(js|jsx|ts|tsx)"
+  ],
+  "addons": [
+    "storybook-dark-mode",
+    "@storybook/addon-links",
+    "@storybook/addon-essentials",
+    "@storybook/addon-interactions"
+  ],
+  "framework": "@storybook/angular",
+  "core": {
+    "builder": "webpack5"
+  },
+  features: {
+    interactionsDebugger: true,
+  },
+}
diff --git a/.storybook/preview-head.html b/.storybook/preview-head.html
new file mode 100644
index 0000000000000000000000000000000000000000..392e96571a4090735d8407bf9a93592d0344d34b
--- /dev/null
+++ b/.storybook/preview-head.html
@@ -0,0 +1,13 @@
+<script>
+  console.log("./storybook/preview-head.html working properly")
+</script>
+<script src="https://unpkg.com/kg-dataset-previewer@1.2.0/dist/kg-dataset-previewer/kg-dataset-previewer.js" defer></script>
+<script src="main.bundle.js" defer></script>
+<style>
+  body
+  {
+    overflow: scroll!important;
+  }
+</style>
+<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.1/css/all.css" integrity="sha384-50oBUHEmvpQ+1lW4y57PTFmhCaXp0ML5d60M1M7uH2+nqUivzIebhndOJK28anvf" crossorigin="anonymous">
+<script type="module" src="https://unpkg.com/hbp-connectivity-component@0.6.2/dist/connectivity-component/connectivity-component.js" defer></script>
diff --git a/.storybook/preview.js b/.storybook/preview.js
new file mode 100644
index 0000000000000000000000000000000000000000..957e7cbef107fa2c3191e57245241fb901983f17
--- /dev/null
+++ b/.storybook/preview.js
@@ -0,0 +1,20 @@
+import { setCompodocJson } from "@storybook/addon-docs/angular";
+import docJson from "../documentation.json";
+setCompodocJson(docJson);
+
+import 'src/theme.scss'
+
+export const parameters = {
+  actions: { argTypesRegex: "^on[A-Z].*" },
+  controls: {
+    matchers: {
+      color: /(background|color)$/i,
+      date: /Date$/,
+    },
+  },
+  docs: { inlineStories: true },
+  darkMode: {
+    // Set the initial theme
+    current: 'light'
+  }
+}
diff --git a/.storybook/tsconfig.json b/.storybook/tsconfig.json
new file mode 100644
index 0000000000000000000000000000000000000000..7aea16334431b3c5f37c10e2c9c71bc224562c21
--- /dev/null
+++ b/.storybook/tsconfig.json
@@ -0,0 +1,21 @@
+{
+  "extends": "../tsconfig.app.json",
+  "compilerOptions": {
+    "types": [
+      "node"
+    ],
+    "allowSyntheticDefaultImports": true
+  },
+  "exclude": [
+    "../src/test.ts",
+    "../src/**/*.spec.ts",
+    "../projects/**/*.spec.ts"
+  ],
+  "include": [
+    "../src/**/*",
+    "../projects/**/*"
+  ],
+  "files": [
+    "./typings.d.ts"
+  ]
+}
diff --git a/.storybook/typings.d.ts b/.storybook/typings.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f73d61b396c9c45ae43f02fbd587629524173156
--- /dev/null
+++ b/.storybook/typings.d.ts
@@ -0,0 +1,4 @@
+declare module '*.md' {
+  const content: string;
+  export default content;
+}
diff --git a/Dockerfile b/Dockerfile
index 14bfaf866d6b8db773443eebc37b32f54300432d..17e735232f80890da6b5b2b65db1b33c152c9765 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -3,9 +3,6 @@ FROM node:14 as builder
 ARG BACKEND_URL
 ENV BACKEND_URL=${BACKEND_URL}
 
-ARG DATASET_PREVIEW_URL
-ENV DATASET_PREVIEW_URL=${DATASET_PREVIEW_URL:-https://hbp-kg-dataset-previewer.apps.hbp.eu/v2}
-
 ARG BS_REST_URL
 ENV BS_REST_URL=${BS_REST_URL:-https://siibra-api-stable.apps.hbp.eu/v1_0}
 
@@ -41,7 +38,8 @@ RUN rm -rf ./node_modules
 
 
 RUN npm i
-RUN npm run build-aot
+RUN npm run build
+RUN npm run build-storybook
 
 # gzipping container
 FROM ubuntu:22.04 as compressor
@@ -49,12 +47,19 @@ RUN apt upgrade -y && apt update && apt install brotli
 
 RUN mkdir /iv
 COPY --from=builder /iv/dist/aot /iv
+COPY --from=builder /iv/storybook-static /iv/storybook-static
+
+# Remove duplicated assets. Use symlink instead.
+WORKDIR /iv/storybook-static
+RUN rm -rf ./assets
+RUN ln -s ../assets ./assets
+
 WORKDIR /iv
 
 RUN for f in $(find . -type f); do gzip < $f > $f.gz && brotli < $f > $f.br; done
 
 # prod container
-FROM node:12-alpine
+FROM node:14-alpine
 
 ENV NODE_ENV=production
 
diff --git a/README.md b/README.md
index 17600f0bf72bd06c4d1e823d71fce2d9644a5412..f54594f9017eb640d50275828d1d8924b8811846 100644
--- a/README.md
+++ b/README.md
@@ -13,11 +13,11 @@ Copyright 2020-2021, Forschungszentrum Jülich GmbH
 
 ## Getting Started
 
-A live version of the Interactive Atlas Viewer is available at [https://interactive-viewer.apps.hbp.eu](https://interactive-viewer.apps.hbp.eu). This section is useful for developers who would like to develop this project.
+A live version of the Interactive Atlas Viewer is available at [https://atlases.ebrains.eu/viewer/](https://atlases.ebrains.eu/viewer/). This section is useful for developers who would like to develop this project.
 
 ### General information
 
-Interactive atlas viewer is built with [Angular (v12.0)](https://angular.io/), [Bootstrap (v4)](http://getbootstrap.com/), and [fontawesome icons](https://fontawesome.com/). Some other notable packages used are [ngrx/store](https://github.com/ngrx/platform) for state management. 
+Interactive atlas viewer is built with [Angular (v13.0)](https://angular.io/), [Bootstrap (v4)](http://getbootstrap.com/), and [fontawesome icons](https://fontawesome.com/). Some other notable packages used are [ngrx/store](https://github.com/ngrx/platform) for state management. 
 
 Releases newer than [v0.2.9](https://github.com/HumanBrainProject/interactive-viewer/tree/v0.2.9) also uses a nodejs backend, which uses [passportjs](http://www.passportjs.org/) for user authentication, [express](https://expressjs.com/) as a http framework.
 
@@ -25,7 +25,7 @@ Releases newer than [v0.2.9](https://github.com/HumanBrainProject/interactive-vi
 
 #### Prerequisites
 
-- latest version of node 12.x.x or node 14.x.x
+- node 12.20.0 or later
 
 #### Environments
 
@@ -48,8 +48,8 @@ Please see [e2e_env.md](e2e_env.md)
 To run a dev server, run:
 
 ```bash
-$ git clone https://github.com/HumanBrainProject/interactive-viewer
-$ cd interactive-viewer
+$ git clone https://github.com/FZJ-INM1-BDA/siibra-explorer
+$ cd siibra-explorer
 $ npm i
 $ npm run dev-server
 ```
@@ -69,51 +69,7 @@ $ npm run build-aot
 
 ### Develop plugins
 
-Below demonstrates an example workflow for developing plugins:
-
-```bash
-
-$ # build aot version of the atlas viewer
-$ npm run build-aot
-$ cd deploy
-$ # run server with PLUGIN_URLS
-$ PLUGIN_URLS=http://localhost:3333/manifest.json;http://localhost:3334/manifest.json node server.js
-```
-
-Interactive Atlas Viewer attempts to fetch list of manifests:
-
-```
-GET {BACKEND_URL}/plugins
-```
-
-The response from this endpoint will be:
-
-```json
-[
-  "http://localhost:3333/manifest.json",
-  "http://localhost:3334/manifest.json"
-]
-```
-
-When user launches the viewer, the atlas viewer will attempt to fetch the metadata of the plugins:
-
-```
-GET http://localhost:3333/manifest.json
-GET http://localhost:3334/manifest.json
-```
-
-The response from these endpoints are expected to adhere to [manifests](src/plugin_examples/README.md#Manifest%20JSON).
-
-When the user launches the plugin, the viewer will fetch `templateUrl` and `scriptUrl`, if necessary.
-
-Plugin developers can start their own webserver, use [interactive-viewer-plugin-template](https://github.com/HumanBrainProject/interactive-viewer-plugin-template), or (coming soon) provide link to a github repository.
-
-
-[plugin readme](src/plugin_examples/README.md)
-
-[plugin api](src/plugin_examples/plugin_api.md)
-
-[plugin migration guide](src/plugin_examples/migrationGuide.md)
+Please see [src/plugin/README.md](src/plugin/README.md)
 
 ## Contributing
 
@@ -121,7 +77,7 @@ Feel free to raise an issue in this repo and/or file a PR.
 
 ## Versioning
 
-Commit history prior to v0.2.0 is available in the [legacy-v0.2.0](https://github.com/HumanBrainProject/interactive-viewer/tree/legacy-v0.2.0) branch. The repo was rewritten to remove its dependency on neuroglancer and nehuba. This allowed for simpler webpack config, faster build time and AOT compilation. 
+Commit history prior to v0.2.0 is available in the [legacy-v0.2.0](https://github.com/FZJ-INM1-BDA/siibra-explorer/tree/legacy-v0.2.0) branch. The repo was rewritten to remove its dependency on neuroglancer and nehuba. This allowed for simpler webpack config, faster build time and AOT compilation. 
 
 ## License
 
diff --git a/build_env.md b/build_env.md
index 1582bcdb0a487e3a91a88ecdcec45df445a687b9..1c29ea98614c3a40dbe91d7c620f9e6a5356b4e8 100644
--- a/build_env.md
+++ b/build_env.md
@@ -8,7 +8,6 @@ As interactive atlas viewer uses [webpack define plugin](https://webpack.js.org/
 | `PRODUCTION` | if the build is for production, toggles optimisations such as minification | `undefined` | true |
 | `BACKEND_URL` | backend that the viewer calls to fetch available template spaces, parcellations, plugins, datasets | `null` | https://interactive-viewer.apps.hbp.eu/ |
 | `BS_REST_URL` | [siibra-api](https://github.com/FZJ-INM1-BDA/siibra-api) used to fetch different resources | https://siibra-api-stable.apps.hbp.eu/v1_0 |
-| `DATASET_PREVIEW_URL` | dataset preview url used by component <https://github.com/fzj-inm1-bda/kg-dataset-previewer>. Useful for diagnosing issues with dataset previews.| https://hbp-kg-dataset-previewer.apps.hbp.eu/datasetPreview | http://localhost:1234/datasetPreview |
 | `MATOMO_URL` | base url for matomo analytics | `null` | https://example.com/matomo/ |
 | `MATOMO_ID` | application id for matomo analytics | `null` | 6 |
 | `STRICT_LOCAL` | hides **Explore** and **Download** buttons. Useful for offline demo's | `false` | `true` |
diff --git a/common/constants.js b/common/constants.js
index e36459f7a686904b39156256673842f9f8142814..94a42b81abf59142a3d50f5a175d64208e02fbbd 100644
--- a/common/constants.js
+++ b/common/constants.js
@@ -27,6 +27,7 @@
     NO_BULK_DOWNLOAD: `No datasets pinned`,
 
     // overlay/layout specific
+    TOGGLE_DELINEATION: `Toggle delineation [q]`,
     SELECT_ATLAS: 'Atlas',
     CONTEXT_MENU: `Viewer context menu`,
     TOGGLE_FRONTAL_OCTANT: `Toggle perspective view frontal octant`,
@@ -84,12 +85,32 @@
   }
 
   exports.CONST = {
+    KG_TOS: `The interactive viewer queries HBP Knowledge Graph Data Platform ("KG") for published datasets.
+
+
+Access to the data and metadata provided through KG requires that you cite and acknowledge said data and metadata according to the Terms and Conditions of the Platform.
+
+
+Citation requirements are outlined <https://www.humanbrainproject.eu/en/explore-the-brain/search-terms-of-use#citations> .
+
+
+Acknowledgement requirements are outlined <https://www.humanbrainproject.eu/en/explore-the-brain/search-terms-of-use#acknowledgements>
+
+
+These outlines are based on the authoritative Terms and Conditions are found <https://www.humanbrainproject.eu/en/explore-the-brain/search-terms-of-use>
+
+
+If you do not accept the Terms & Conditions you are not permitted to access or use the KG to search for, to submit, to post, or to download any materials found there-in.
+`,
+
     LOADING_TXT: `Loading ...`,
 
     CANNOT_DECIPHER_HEMISPHERE: 'Cannot decipher region hemisphere.',
     DOES_NOT_SUPPORT_MULTI_REGION_SELECTION: `Please only select a single region.`,
     MULTI_REGION_SELECTION: `Multi region selection`,
+    DESCRIPTION: 'Description',
     REGIONAL_FEATURES: 'Regional features',
+    CONNECTIVITY: 'Connectivity',
     NO_ADDIONTAL_INFO_AVAIL: `Currently, no additional information is linked to this region.`,
 
     ATLAS_NOT_FOUND: `Atlas not found. Maybe it is still loading. Try again in a few seconds?`,
diff --git a/common/util.js b/common/util.js
index 5f01f394ae6301fcca029647c3fef775b740bf74..198b3f0b6a0de651198849214120e918799e8af9 100644
--- a/common/util.js
+++ b/common/util.js
@@ -1,5 +1,7 @@
 (function(exports) {
 
+  exports.camelToSnake = s => s.replace(/[A-Z]/g, s => `_${s.toLowerCase()}`)
+
   const flattenReducer = (acc, curr) => acc.concat(curr)
   exports.flattenReducer = flattenReducer
 
@@ -157,6 +159,8 @@
 
   exports.flattenRegions = flattenRegions
 
+  exports.hexToRgb = hex => hex && hex.match(/[a-fA-F0-9]{2}/g).map(v => parseInt(v, 16))
+  exports.rgbToHex = rgb => rgb && `#${rgb.map(color => color.toString(16).padStart(2, '0')).join("")}`
   exports.getRandomHex = (digit = 1024 * 1024 * 1024 * 1024) => Math.round(Math.random() * digit).toString(16)
 
   /**
@@ -199,37 +203,6 @@
     )
   }
 
-  exports.serialiseParcellationRegion = ({ ngId, labelIndex }) => {
-    if (!ngId) {
-      throw new Error(`#serialiseParcellationRegion error: ngId must be defined`)
-    }
-
-    if (!labelIndex) {
-      throw new Error(`#serialiseParcellationRegion error labelIndex must be defined`)
-    }
-
-    return `${ngId}#${labelIndex}`
-  }
-
-  const deserialiseParcRegionId = labelIndexId => {
-    const _ = labelIndexId && labelIndexId.split && labelIndexId.split('#') || []
-    const ngId = _.length > 1
-      ? _[0]
-      : null
-    const labelIndex = _.length > 1
-      ? Number(_[1])
-      : _.length === 0
-        ? null
-        : Number(_[0])
-    return { labelIndex, ngId }
-  }
-
-  exports.deserialiseParcRegionId = deserialiseParcRegionId
-
-  exports.deserialiseParcellationRegion = ({ region, labelIndexId, inheritedNgId = 'root' }) => {
-    const { labelIndex, ngId } = deserialiseParcRegionId(labelIndexId)
-  }
-
   const getPad = ({ length, pad }) => {
     if (pad.length !== 1) throw new Error(`pad needs to be precisely 1 character`)
     return input => {
@@ -252,4 +225,19 @@
     return `${year}${pad2(month)}${pad2(date)}_${pad2(hr)}${pad2(min)}`
   }
 
+  function isVec4(input) {
+    if (!Array.isArray(input)) return false
+    if (input.length !== 4) return false
+    return input.every(v => typeof v === "number")
+  }
+  function isMat4(input) {
+    if (!Array.isArray(input)) return false
+    if (input.length !== 4) return false
+    return input.every(isVec4)
+  }
+
+  exports.isVec4 = isVec4
+  exports.isMat4 = isMat4
+
+
 })(typeof exports === 'undefined' ? module.exports : exports)
diff --git a/deploy/bkwdCompat/urlState.js b/deploy/bkwdCompat/urlState.js
index 66f92c3dc4f6b375fcfe0c70f16a685b43c30e0d..64093f9e035e19fc95c9ee8eea8b2caa2303912b 100644
--- a/deploy/bkwdCompat/urlState.js
+++ b/deploy/bkwdCompat/urlState.js
@@ -114,8 +114,12 @@ const WARNING_STRINGS = {
   REGION_SELECT_ERROR: 'Region selected cannot be processed properly.',
   TEMPLATE_ERROR: 'Template not found.',
 }
-
+const pliPreviewUrl = `/a:juelich:iav:atlas:v1.0.0:1/t:minds:core:referencespace:v1.0.0:a1655b99-82f1-420f-a3c2-fe80fd4c8588/p:juelich:iav:atlas:v1.0.0:4/@:0.0.0.-W000.._eCwg.2-FUe3._-s_W.2_evlu..7LIy..1qI1a.D31U~.i-Os~..HRE/f:siibra:features:voi:19c437087299dd62e7c507200f69aea6`
 module.exports = (query, _warningCb) => {
+
+  const HOST_PATHNAME = process.env.HOST_PATHNAME || ''
+  let redirectUrl = `${HOST_PATHNAME}/#`
+
   const {
     standaloneVolumes,
     niftiLayers, // deprecating - check if anyone calls this url
@@ -163,7 +167,17 @@ module.exports = (query, _warningCb) => {
       if (Array.isArray(parsedDsp)) {
         if (parsedDsp.length === 1) {
           const { datasetId, filename } = parsedDsp[0]
-          dsp = `/dsp:${encodeId(datasetId)}::${encodeURI(filename)}`
+          if (datasetId === "minds/core/dataset/v1.0.0/b08a7dbc-7c75-4ce7-905b-690b2b1e8957") {
+            /**
+             * if preview pli link, return hardcoded link
+             */
+            return `${HOST_PATHNAME}/#${pliPreviewUrl}`
+          } else {
+            /**
+             * TODO deprecate dsp
+             */
+            dsp = `/dsp:${encodeId(datasetId)}::${encodeURI(filename)}`
+          }
         } else {
           searchParam.set(`previewingDatasetFiles`, previewingDatasetFiles)
         }
@@ -206,8 +220,6 @@ module.exports = (query, _warningCb) => {
       // ignore region selected and move on
     }
   }
-  const HOST_PATHNAME = process.env.HOST_PATHNAME || ''
-  let redirectUrl = `${HOST_PATHNAME}/#`
   if (standaloneVolumes) {
     searchParam.set('standaloneVolumes', standaloneVolumes)
     if (nav) redirectUrl += nav
diff --git a/deploy/csp/index.js b/deploy/csp/index.js
index 0ed247afe51b6e6d361eb0ea09ecdc319ddc5ad5..898228ce845b95c747d027704c08ab96f8196c30 100644
--- a/deploy/csp/index.js
+++ b/deploy/csp/index.js
@@ -54,7 +54,9 @@ const connectSrc = [
   'object.cscs.ch',
 
   // required for dataset previews
-  'hbp-kg-dataset-previewer.apps.hbp.eu/v2/',
+
+  // spatial transform
+  "hbp-spatial-backend.apps.hbp.eu",
 
   // injected by env var
   ...CSP_CONNECT_SRC
@@ -102,26 +104,24 @@ module.exports = {
         ],
         imgSrc: [
           "'self'",
-          "hbp-kg-dataset-previewer.apps.hbp.eu/v2/"
         ],
         scriptSrc:[
           "'self'",
           ...userScriptSrc,
-          'code.jquery.com', // plugin load external library -> jquery v2 and v3
-          'cdnjs.cloudflare.com/ajax/libs/webcomponentsjs/', // plugin load external library -> web components
-          'cdnjs.cloudflare.com/ajax/libs/d3/', // plugin load external lib -> d3
-          'cdn.jsdelivr.net/npm/vue@2.5.16/', // plugin load external lib -> vue 2
-          'cdn.jsdelivr.net/npm/preact@8.4.2/', // plugin load external lib -> preact
-          'unpkg.com/react@16/umd/', // plugin load external lib -> react
           'unpkg.com/kg-dataset-previewer@1.2.0/', // preview component
+          'cdnjs.cloudflare.com/ajax/libs/d3/', // required for preview component
           'cdnjs.cloudflare.com/ajax/libs/mathjax/', // math jax
-          'https://unpkg.com/three-surfer@0.0.10/dist/bundle.js', // for threeSurfer (freesurfer support in browser)
+          'https://unpkg.com/three-surfer@0.0.11/dist/bundle.js', // for threeSurfer (freesurfer support in browser)
           'https://unpkg.com/ng-layer-tune@0.0.6/dist/ng-layer-tune/', // needed for ng layer control
+          'https://unpkg.com/hbp-connectivity-component@0.6.2/', // needed for connectivity component
           (req, res) => res.locals.nonce ? `'nonce-${res.locals.nonce}'` : null,
           ...SCRIPT_SRC,
           ...WHITE_LIST_SRC,
           ...defaultAllowedSites
         ],
+        frameSrc: [
+          "*"
+        ],
         reportUri: CSP_REPORT_URI || '/report-violation'
       },
       reportOnly
diff --git a/deploy/package-lock.json b/deploy/package-lock.json
index ba5432458b504ab7a4e11b768a0d695442113ee7..1dac062d78f8ca3d5a23d9456457a913ceef919c 100644
--- a/deploy/package-lock.json
+++ b/deploy/package-lock.json
@@ -151,77 +151,6 @@
         "picomatch": "^2.0.4"
       }
     },
-    "archiver": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/archiver/-/archiver-3.1.1.tgz",
-      "integrity": "sha512-5Hxxcig7gw5Jod/8Gq0OneVgLYET+oNHcxgWItq4TbhOzRLKNAFUb9edAftiMKXvXfCB0vbGrJdZDNq0dWMsxg==",
-      "requires": {
-        "archiver-utils": "^2.1.0",
-        "async": "^2.6.3",
-        "buffer-crc32": "^0.2.1",
-        "glob": "^7.1.4",
-        "readable-stream": "^3.4.0",
-        "tar-stream": "^2.1.0",
-        "zip-stream": "^2.1.2"
-      },
-      "dependencies": {
-        "glob": {
-          "version": "7.1.4",
-          "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz",
-          "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==",
-          "requires": {
-            "fs.realpath": "^1.0.0",
-            "inflight": "^1.0.4",
-            "inherits": "2",
-            "minimatch": "^3.0.4",
-            "once": "^1.3.0",
-            "path-is-absolute": "^1.0.0"
-          }
-        },
-        "readable-stream": {
-          "version": "3.4.0",
-          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz",
-          "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==",
-          "requires": {
-            "inherits": "^2.0.3",
-            "string_decoder": "^1.1.1",
-            "util-deprecate": "^1.0.1"
-          }
-        }
-      }
-    },
-    "archiver-utils": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz",
-      "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==",
-      "requires": {
-        "glob": "^7.1.4",
-        "graceful-fs": "^4.2.0",
-        "lazystream": "^1.0.0",
-        "lodash.defaults": "^4.2.0",
-        "lodash.difference": "^4.5.0",
-        "lodash.flatten": "^4.4.0",
-        "lodash.isplainobject": "^4.0.6",
-        "lodash.union": "^4.6.0",
-        "normalize-path": "^3.0.0",
-        "readable-stream": "^2.0.0"
-      },
-      "dependencies": {
-        "glob": {
-          "version": "7.1.4",
-          "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz",
-          "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==",
-          "requires": {
-            "fs.realpath": "^1.0.0",
-            "inflight": "^1.0.4",
-            "inherits": "2",
-            "minimatch": "^3.0.4",
-            "once": "^1.3.0",
-            "path-is-absolute": "^1.0.0"
-          }
-        }
-      }
-    },
     "argparse": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
@@ -252,21 +181,6 @@
       "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==",
       "dev": true
     },
-    "async": {
-      "version": "2.6.3",
-      "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
-      "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
-      "requires": {
-        "lodash": "^4.17.14"
-      },
-      "dependencies": {
-        "lodash": {
-          "version": "4.17.21",
-          "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
-          "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
-        }
-      }
-    },
     "asynckit": {
       "version": "0.4.0",
       "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
@@ -285,7 +199,8 @@
     "balanced-match": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
-      "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
+      "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
+      "dev": true
     },
     "bcrypt-pbkdf": {
       "version": "1.0.2",
@@ -301,26 +216,6 @@
       "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
       "dev": true
     },
-    "bl": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/bl/-/bl-3.0.1.tgz",
-      "integrity": "sha512-jrCW5ZhfQ/Vt07WX1Ngs+yn9BDqPL/gw28S7s9H6QK/gupnizNzJAss5akW20ISgOrbLTlXOOCTJeNUQqruAWQ==",
-      "requires": {
-        "readable-stream": "^3.0.1"
-      },
-      "dependencies": {
-        "readable-stream": {
-          "version": "3.6.0",
-          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
-          "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
-          "requires": {
-            "inherits": "^2.0.3",
-            "string_decoder": "^1.1.1",
-            "util-deprecate": "^1.0.1"
-          }
-        }
-      }
-    },
     "body-parser": {
       "version": "1.19.0",
       "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
@@ -393,6 +288,7 @@
       "version": "1.1.11",
       "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
       "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+      "dev": true,
       "requires": {
         "balanced-match": "^1.0.0",
         "concat-map": "0.0.1"
@@ -413,11 +309,6 @@
       "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==",
       "dev": true
     },
-    "buffer-crc32": {
-      "version": "0.2.13",
-      "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
-      "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI="
-    },
     "bytes": {
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
@@ -583,21 +474,11 @@
         "delayed-stream": "~1.0.0"
       }
     },
-    "compress-commons": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-2.1.1.tgz",
-      "integrity": "sha512-eVw6n7CnEMFzc3duyFVrQEuY1BlHR3rYsSztyG32ibGMW722i3C6IizEGMFmfMU+A+fALvBIwxN3czffTcdA+Q==",
-      "requires": {
-        "buffer-crc32": "^0.2.13",
-        "crc32-stream": "^3.0.1",
-        "normalize-path": "^3.0.0",
-        "readable-stream": "^2.3.6"
-      }
-    },
     "concat-map": {
       "version": "0.0.1",
       "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
-      "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
+      "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+      "dev": true
     },
     "connect-redis": {
       "version": "5.1.0",
@@ -661,27 +542,6 @@
       "resolved": "https://registry.npmjs.org/crc/-/crc-3.4.4.tgz",
       "integrity": "sha1-naHpgOO9RPxck79as9ozeNheRms="
     },
-    "crc32-stream": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-3.0.1.tgz",
-      "integrity": "sha512-mctvpXlbzsvK+6z8kJwSJ5crm7yBwrQMTybJzMw1O4lLGJqjlDCXY2Zw7KheiA6XBEcBmfLx1D88mjRGVJtY9w==",
-      "requires": {
-        "crc": "^3.4.4",
-        "readable-stream": "^3.4.0"
-      },
-      "dependencies": {
-        "readable-stream": {
-          "version": "3.4.0",
-          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz",
-          "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==",
-          "requires": {
-            "inherits": "^2.0.3",
-            "string_decoder": "^1.1.1",
-            "util-deprecate": "^1.0.1"
-          }
-        }
-      }
-    },
     "dashdash": {
       "version": "1.14.1",
       "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
@@ -1000,15 +860,11 @@
       "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
       "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
     },
-    "fs-constants": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
-      "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
-    },
     "fs.realpath": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
-      "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
+      "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+      "dev": true
     },
     "fsevents": {
       "version": "2.3.2",
@@ -1089,11 +945,6 @@
         "type-fest": "^0.9.0"
       }
     },
-    "graceful-fs": {
-      "version": "4.2.1",
-      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.1.tgz",
-      "integrity": "sha512-b9usnbDGnD928gJB3LrCmxoibr3VE4U2SMo5PBuBnokWyDADTqDPXg4YpwKF1trpH+UbGp7QLicO3+aWEy0+mw=="
-    },
     "growl": {
       "version": "1.10.5",
       "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz",
@@ -1191,6 +1042,7 @@
       "version": "1.0.6",
       "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
       "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+      "dev": true,
       "requires": {
         "once": "^1.3.0",
         "wrappy": "1"
@@ -1341,51 +1193,18 @@
         "json-buffer": "3.0.1"
       }
     },
-    "lazystream": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz",
-      "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=",
-      "requires": {
-        "readable-stream": "^2.0.5"
-      }
-    },
     "lodash": {
       "version": "4.17.21",
       "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
       "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
       "dev": true
     },
-    "lodash.defaults": {
-      "version": "4.2.0",
-      "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
-      "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw="
-    },
-    "lodash.difference": {
-      "version": "4.5.0",
-      "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz",
-      "integrity": "sha1-nMtOUF1Ia5FlE0V3KIWi3yf9AXw="
-    },
-    "lodash.flatten": {
-      "version": "4.4.0",
-      "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz",
-      "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8="
-    },
     "lodash.get": {
       "version": "4.4.2",
       "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
       "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=",
       "dev": true
     },
-    "lodash.isplainobject": {
-      "version": "4.0.6",
-      "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
-      "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs="
-    },
-    "lodash.union": {
-      "version": "4.6.0",
-      "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz",
-      "integrity": "sha1-SLtQiECfFvGCFmZkHETdGqrjzYg="
-    },
     "log-symbols": {
       "version": "4.1.0",
       "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
@@ -1498,6 +1317,7 @@
       "version": "3.0.4",
       "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
       "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+      "dev": true,
       "requires": {
         "brace-expansion": "^1.1.7"
       }
@@ -1686,7 +1506,8 @@
     "normalize-path": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
-      "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="
+      "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+      "dev": true
     },
     "normalize-url": {
       "version": "4.5.1",
@@ -1853,7 +1674,8 @@
     "path-is-absolute": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
-      "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
+      "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+      "dev": true
     },
     "path-to-regexp": {
       "version": "0.1.7",
@@ -2351,30 +2173,6 @@
         "has-flag": "^4.0.0"
       }
     },
-    "tar-stream": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.0.tgz",
-      "integrity": "sha512-+DAn4Nb4+gz6WZigRzKEZl1QuJVOLtAwwF+WUxy1fJ6X63CaGaUAxJRD2KEn1OMfcbCjySTYpNC6WmfQoIEOdw==",
-      "requires": {
-        "bl": "^3.0.0",
-        "end-of-stream": "^1.4.1",
-        "fs-constants": "^1.0.0",
-        "inherits": "^2.0.3",
-        "readable-stream": "^3.1.1"
-      },
-      "dependencies": {
-        "readable-stream": {
-          "version": "3.4.0",
-          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz",
-          "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==",
-          "requires": {
-            "inherits": "^2.0.3",
-            "string_decoder": "^1.1.1",
-            "util-deprecate": "^1.0.1"
-          }
-        }
-      }
-    },
     "through2": {
       "version": "3.0.1",
       "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz",
@@ -2619,28 +2417,6 @@
       "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
       "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
       "dev": true
-    },
-    "zip-stream": {
-      "version": "2.1.2",
-      "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-2.1.2.tgz",
-      "integrity": "sha512-ykebHGa2+uzth/R4HZLkZh3XFJzivhVsjJt8bN3GvBzLaqqrUdRacu+c4QtnUgjkkQfsOuNE1JgLKMCPNmkKgg==",
-      "requires": {
-        "archiver-utils": "^2.1.0",
-        "compress-commons": "^2.1.1",
-        "readable-stream": "^3.4.0"
-      },
-      "dependencies": {
-        "readable-stream": {
-          "version": "3.4.0",
-          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz",
-          "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==",
-          "requires": {
-            "inherits": "^2.0.3",
-            "string_decoder": "^1.1.1",
-            "util-deprecate": "^1.0.1"
-          }
-        }
-      }
     }
   }
 }
diff --git a/deploy/package.json b/deploy/package.json
index f96b15c4c77473c9ec9874e57fd6638bce1100e4..333568cdfc0b2804ed97319a6b00dd1577c59d50 100644
--- a/deploy/package.json
+++ b/deploy/package.json
@@ -12,7 +12,6 @@
   "author": "",
   "license": "ISC",
   "dependencies": {
-    "archiver": "^3.0.0",
     "body-parser": "^1.19.0",
     "connect-redis": "^5.0.0",
     "cookie-parser": "^1.4.5",
diff --git a/deploy/plugins/index.js b/deploy/plugins/index.js
index 4618bce4676259fb966d5770653aa03e3380db6d..028a0158d9a94b4a353242099c9cfed7eb8f867f 100644
--- a/deploy/plugins/index.js
+++ b/deploy/plugins/index.js
@@ -7,24 +7,26 @@ const express = require('express')
 const lruStore = require('../lruStore')
 const { race } = require("../../common/util")
 const got = require('got')
+const { URL } = require('url')
+const path = require("path")
 const router = express.Router()
-const DEV_PLUGINS = (() => {
+const V2_7_DEV_PLUGINS = (() => {
   try {
     return JSON.parse(
-      process.env.DEV_PLUGINS || `[]`
+      process.env.V2_7_DEV_PLUGINS || `[]`
     )
   } catch (e) {
     console.warn(`Parsing DEV_PLUGINS failed: ${e}`)
     return []
   }
 })()
-const PLUGIN_URLS = (process.env.PLUGIN_URLS && process.env.PLUGIN_URLS.split(';')) || []
-const STAGING_PLUGIN_URLS = (process.env.STAGING_PLUGIN_URLS && process.env.STAGING_PLUGIN_URLS.split(';')) || []
+const V2_7_PLUGIN_URLS = (process.env.V2_7_PLUGIN_URLS && process.env.V2_7_PLUGIN_URLS.split(';')) || []
+const V2_7_STAGING_PLUGIN_URLS = (process.env.V2_7_STAGING_PLUGIN_URLS && process.env.V2_7_STAGING_PLUGIN_URLS.split(';')) || []
 
 router.get('', (_req, res) => {
   return res.status(200).json([
-    ...PLUGIN_URLS,
-    ...STAGING_PLUGIN_URLS
+    ...V2_7_PLUGIN_URLS,
+    ...V2_7_STAGING_PLUGIN_URLS
   ])
 })
 
@@ -32,40 +34,50 @@ const getKey = url => `plugin:manifest-cache:${url}`
 
 router.get('/manifests', async (_req, res) => {
 
-  const output = []
-  for (const plugin of [ ...PLUGIN_URLS, ...STAGING_PLUGIN_URLS ]) {
-    try {
-      await race(async () => {
-        const key = getKey(plugin)
-        try {
-          const result = await race(async () => {
+  const allManifests = await Promise.all(
+    [...V2_7_PLUGIN_URLS, ...V2_7_STAGING_PLUGIN_URLS].map(async url => {
+      try {
+        return await race(
+          async () => {
+            const key = getKey(url)
+            
             await lruStore._initPr
             const { store } = lruStore
-            const storedManifest = await store.get(key)
-            if (storedManifest) {
-              return JSON.parse(storedManifest)
-            } else {
-              throw `not found`
+            
+            try {
+              const storedManifest = await store.get(key)
+              if (storedManifest) return JSON.parse(storedManifest)
+              else throw `not found`
+            } catch (e) {
+              const resp = await got(url)
+              const json = JSON.parse(resp.body)
+      
+              const { iframeUrl, 'siibra-explorer': flag } = json
+              if (!flag) return null
+              if (!iframeUrl) return null
+              const u = new URL(url)
+              
+              let replaceObj = {}
+              if (!/^https?:\/\//.test(iframeUrl)) {
+                u.pathname = path.resolve(path.dirname(u.pathname), iframeUrl)
+                replaceObj['iframeUrl'] = u.toString()
+              }
+              const returnObj = {...json, ...replaceObj}
+              await store.set(key, JSON.stringify(returnObj), { maxAge: 1000 * 60 * 60 })
+              return returnObj
             }
-          }, { timeout: 100 })
-          output.push(result)
-        } catch (e) {
-          const resp = await got(plugin)
-          const json = JSON.parse(resp.body)
-          
-          output.push(json)
-          store.set(key, JSON.stringify(json), { maxAge: 1000 * 60 * 60 })
-            .catch(e => console.error(`setting store value error`, e))
-        }
-      })
-    } catch (e) {
-      console.error(`racing to get manifest ${plugin} timed out or errored.`, e)
-    }
-  }
-  
+          },
+          { timeout: 1000 }
+        )
+      } catch (e) {
+        console.error(`fetching manifest error: ${e}`)
+        return null
+      }
+    })
+  )
 
   res.status(200).json(
-    [...DEV_PLUGINS, ...output]
+    [...V2_7_DEV_PLUGINS, ...allManifests.filter(v => !!v)]
   )
 })
 
diff --git a/deploy/quickstart/index.js b/deploy/quickstart/index.js
index 0ca183d324eb24ac413d16ba31633eee2e5cf4bb..ceef857a7d4db225eac74ae82d47a354f66e9d39 100644
--- a/deploy/quickstart/index.js
+++ b/deploy/quickstart/index.js
@@ -28,7 +28,7 @@ const getQuickStartMdPr = (async () => {
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <title>Interactive Atlas Viewer Quickstart</title>
 </head>
-<body class="p-4">
+<body class="sxplr-p-4">
   
 </body>
 <script>
diff --git a/deploy/saneUrl/index.js b/deploy/saneUrl/index.js
index 9f732525722a4f7d21c8475748f1db3874230ce4..3bc0bca9fe6e6eafad1075a0a4b16b2eddddd91e 100644
--- a/deploy/saneUrl/index.js
+++ b/deploy/saneUrl/index.js
@@ -66,7 +66,13 @@ router.get('/:name', async (req, res) => {
 
     if (redirectFlag) {
       if (queryString) return res.redirect(`${REAL_HOSTNAME}?${queryString}`)
-      if (hashPath) return res.redirect(`${REAL_HOSTNAME}#${hashPath}/${xtraRoutes.join('/')}`)
+      if (hashPath) {
+        let redirectUrl = `${REAL_HOSTNAME}#${hashPath}`
+        if (xtraRoutes.length > 0) {
+          redirectUrl += `/${xtraRoutes.join('/')}`
+        }
+        return res.redirect(redirectUrl)
+      }
     } else {
       return res.status(200).send(json)
     }
diff --git a/docs/releases/v2.7.0.md b/docs/releases/v2.7.0.md
new file mode 100644
index 0000000000000000000000000000000000000000..1ce6c8632654266ab4931d94f3f47a630db3f941
--- /dev/null
+++ b/docs/releases/v2.7.0.md
@@ -0,0 +1,14 @@
+# v2.7.0
+
+## Breaking changes
+
+- plugin in interactive atlas viewer has been completely redesigned
+
+## New feature
+
+- added storybook for component development
+- Add first implementation of fetching VOI from siibra-api
+
+## Under the hood
+
+- Major refactor to use new siibra-api schema
diff --git a/e2e/checklist.md b/e2e/checklist.md
index b51cd1cdc38ce354cf1bc0ca97a6d06e9b3896b4..b1a0c2ce262eb278c84724a0c680404b6c512fc6 100644
--- a/e2e/checklist.md
+++ b/e2e/checklist.md
@@ -72,3 +72,4 @@
 - [ ] [vipUrl](https://atlases.ebrains.eu/viewer-staging/mouse) redirects allen mouse 2017
 ## plugins
 - [ ] jugex plugin works
+- [ ] 1um section works
diff --git a/mkdocs.yml b/mkdocs.yml
index 3fb53b576cae2ce147774c62caf060968a2a8901..cf6e2cb584ed320eac04af6ce43ceed9753165f0 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -33,6 +33,7 @@ nav:
     - Fetching datasets: 'advanced/datasets.md'
     - Display non-atlas volumes: 'advanced/otherVolumes.md'
   - Release notes:
+    - v2.7.0: 'releases/v2.7.0.md'
     - v2.6.10: 'releases/v2.6.10.md'
     - v2.6.9: 'releases/v2.6.9.md'
     - v2.6.8: 'releases/v2.6.8.md'
diff --git a/package-lock.json b/package-lock.json
index d2cb14c3526da60907ff27c8338e60bddcd69e90..a430b1fb3e4bb0ca1d9daea0579ba57778c1315c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,9 +1,14 @@
 {
   "name": "interactive-viewer",
-  "version": "2.6.8",
-  "lockfileVersion": 1,
   "requires": true,
+  "lockfileVersion": 1,
   "dependencies": {
+    "@aduh95/viz.js": {
+      "version": "3.6.0",
+      "resolved": "https://registry.npmjs.org/@aduh95/viz.js/-/viz.js-3.6.0.tgz",
+      "integrity": "sha512-ywd9QYyLByJvkdxwIB4ve4ikF8T2AUXSIn0EBYSlbv1TxnAshgPXiE4JWk6d0LJHWDsxR7Piw7TwBcQ/YrgeCA==",
+      "dev": true
+    },
     "@ampproject/remapping": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-1.0.1.tgz",
@@ -15,26 +20,26 @@
       }
     },
     "@angular-devkit/architect": {
-      "version": "0.1202.13",
-      "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1202.13.tgz",
-      "integrity": "sha512-LXgiidXwBgyWPqqWK4xR1/kCPQTMTzG5w+s7+LvENUZwbcdl6CKrOMjfgjo6WPr6yeq+WWQvPCD4pZ6nXRTm7A==",
+      "version": "0.1202.17",
+      "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1202.17.tgz",
+      "integrity": "sha512-uUQcHcLbPvr9adALQSLU1MTDduVUR2kZAHi2e7SmL9ioel84pPVXBoD0WpSBeUMKwPiDs3TQDaxDB49hl0nBSQ==",
       "dev": true,
       "requires": {
-        "@angular-devkit/core": "12.2.13",
+        "@angular-devkit/core": "12.2.17",
         "rxjs": "6.6.7"
       }
     },
     "@angular-devkit/build-angular": {
-      "version": "12.2.13",
-      "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-12.2.13.tgz",
-      "integrity": "sha512-iJ1P/RZ1hk2n/HtEqB5ohXvHa+Hf0BXShYskSGrn6/klcTb0eJTCREsFxHk7mNEmRIGPWkjbLAslqpPgwiagXg==",
+      "version": "12.2.17",
+      "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-12.2.17.tgz",
+      "integrity": "sha512-uc3HGHVQyatqQ/M53oxYBvhz0jx0hgdc7WT+L56GLHvgz7Ct2VEbpWaMfwHkFfE1F1iHkIgnTKHKWacJl1yQIg==",
       "dev": true,
       "requires": {
         "@ampproject/remapping": "1.0.1",
-        "@angular-devkit/architect": "0.1202.13",
-        "@angular-devkit/build-optimizer": "0.1202.13",
-        "@angular-devkit/build-webpack": "0.1202.13",
-        "@angular-devkit/core": "12.2.13",
+        "@angular-devkit/architect": "0.1202.17",
+        "@angular-devkit/build-optimizer": "0.1202.17",
+        "@angular-devkit/build-webpack": "0.1202.17",
+        "@angular-devkit/core": "12.2.17",
         "@babel/core": "7.14.8",
         "@babel/generator": "7.14.8",
         "@babel/helper-annotate-as-pure": "7.14.5",
@@ -46,7 +51,7 @@
         "@babel/template": "7.14.5",
         "@discoveryjs/json-ext": "0.5.3",
         "@jsdevtools/coverage-istanbul-loader": "3.0.5",
-        "@ngtools/webpack": "12.2.13",
+        "@ngtools/webpack": "12.2.17",
         "ansi-colors": "4.1.1",
         "babel-loader": "8.2.2",
         "browserslist": "^4.9.1",
@@ -97,11 +102,76 @@
         "tslib": "2.3.0",
         "webpack": "5.50.0",
         "webpack-dev-middleware": "5.0.0",
-        "webpack-dev-server": "3.11.2",
+        "webpack-dev-server": "3.11.3",
         "webpack-merge": "5.8.0",
         "webpack-subresource-integrity": "1.5.2"
       },
       "dependencies": {
+        "@babel/core": {
+          "version": "7.14.8",
+          "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.14.8.tgz",
+          "integrity": "sha512-/AtaeEhT6ErpDhInbXmjHcUQXH0L0TEgscfcxk1qbOvLuKCa5aZT0SOOtDKFY96/CLROwbLSKyFor6idgNaU4Q==",
+          "dev": true,
+          "requires": {
+            "@babel/code-frame": "^7.14.5",
+            "@babel/generator": "^7.14.8",
+            "@babel/helper-compilation-targets": "^7.14.5",
+            "@babel/helper-module-transforms": "^7.14.8",
+            "@babel/helpers": "^7.14.8",
+            "@babel/parser": "^7.14.8",
+            "@babel/template": "^7.14.5",
+            "@babel/traverse": "^7.14.8",
+            "@babel/types": "^7.14.8",
+            "convert-source-map": "^1.7.0",
+            "debug": "^4.1.0",
+            "gensync": "^1.0.0-beta.2",
+            "json5": "^2.1.2",
+            "semver": "^6.3.0",
+            "source-map": "^0.5.0"
+          },
+          "dependencies": {
+            "semver": {
+              "version": "6.3.0",
+              "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+              "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+              "dev": true
+            }
+          }
+        },
+        "babel-loader": {
+          "version": "8.2.2",
+          "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.2.tgz",
+          "integrity": "sha512-JvTd0/D889PQBtUXJ2PXaKU/pjZDMtHA9V2ecm+eNRmmBCMR09a+fmpGTNwnJtFmFl5Ei7Vy47LjBb+L0wQ99g==",
+          "dev": true,
+          "requires": {
+            "find-cache-dir": "^3.3.1",
+            "loader-utils": "^1.4.0",
+            "make-dir": "^3.1.0",
+            "schema-utils": "^2.6.5"
+          },
+          "dependencies": {
+            "json5": {
+              "version": "1.0.1",
+              "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
+              "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
+              "dev": true,
+              "requires": {
+                "minimist": "^1.2.0"
+              }
+            },
+            "loader-utils": {
+              "version": "1.4.0",
+              "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
+              "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==",
+              "dev": true,
+              "requires": {
+                "big.js": "^5.2.2",
+                "emojis-list": "^3.0.0",
+                "json5": "^1.0.1"
+              }
+            }
+          }
+        },
         "esbuild": {
           "version": "0.13.8",
           "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.13.8.tgz",
@@ -150,6 +220,23 @@
             "source-map-js": "^0.6.2"
           }
         },
+        "schema-utils": {
+          "version": "2.7.1",
+          "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz",
+          "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==",
+          "dev": true,
+          "requires": {
+            "@types/json-schema": "^7.0.5",
+            "ajv": "^6.12.4",
+            "ajv-keywords": "^3.5.2"
+          }
+        },
+        "source-map": {
+          "version": "0.5.7",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+          "dev": true
+        },
         "source-map-js": {
           "version": "0.6.2",
           "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-0.6.2.tgz",
@@ -165,9 +252,9 @@
       }
     },
     "@angular-devkit/build-optimizer": {
-      "version": "0.1202.13",
-      "resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.1202.13.tgz",
-      "integrity": "sha512-XX6rX5+mAl+MiIJDvi5N5mBLWOoskhMJ5r/G1PEqv0CMMJSSw60zUTndjxfq/nrX0PtsV3j/aqHN4Sj0w/gumg==",
+      "version": "0.1202.17",
+      "resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.1202.17.tgz",
+      "integrity": "sha512-1qWGWw7cCNADB4LZ/zjiSK0GLmr2kebYyNG0KutCE8GNVxv2h6w6dJP6t1C/BgskRuBPCAhvE+lEKN8ljSutag==",
       "dev": true,
       "requires": {
         "source-map": "0.7.3",
@@ -184,19 +271,19 @@
       }
     },
     "@angular-devkit/build-webpack": {
-      "version": "0.1202.13",
-      "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1202.13.tgz",
-      "integrity": "sha512-KafzGyHuU2gBKaSICfMTFP2niTYZ/46DKU94TQ0lCILdJZsj0NE5M/288LSCbYgu2c7srJKz+Rvb+JcYGxIZ1g==",
+      "version": "0.1202.17",
+      "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1202.17.tgz",
+      "integrity": "sha512-z7FW43DJ4p8UZwbFRmMrh2ohqhI2Wtdg3+FZiTnl4opb3zYheGiNxPlTuiyKjG21JUkGCdthkkBLCNfaUU0U/Q==",
       "dev": true,
       "requires": {
-        "@angular-devkit/architect": "0.1202.13",
+        "@angular-devkit/architect": "0.1202.17",
         "rxjs": "6.6.7"
       }
     },
     "@angular-devkit/core": {
-      "version": "12.2.13",
-      "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-12.2.13.tgz",
-      "integrity": "sha512-9csMF0p+lTvlWnutxxTZ/+pDRMIbXk/TV4MGLbcqUPPfeG3dCRwErns73xLuMTwp9qO/KCLkFqNaM6cGOoqsDA==",
+      "version": "12.2.17",
+      "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-12.2.17.tgz",
+      "integrity": "sha512-PyOY7LGUPPd6rakxUYbfQN6zAdOCMCouVp5tERY1WTdMdEiuULOtHsPee8kNbh75pD59KbJNU+fwozPRMuIm5g==",
       "dev": true,
       "requires": {
         "ajv": "8.6.2",
@@ -228,20 +315,20 @@
       }
     },
     "@angular-devkit/schematics": {
-      "version": "12.2.13",
-      "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-12.2.13.tgz",
-      "integrity": "sha512-LQTv72R5Ma1uowMEeii2wIoDWI4bYQyZvunqPy9jRveBTjli2yVwwcOziGCVyttwlYs46bSdxThgeEvVIako2w==",
+      "version": "12.2.17",
+      "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-12.2.17.tgz",
+      "integrity": "sha512-c0eNu/nx1Mnu7KcZgYTYHP736H4Y9pSyLBSmLAHYZv3t3m0dIPbhifRcLQX7hHQ8fGT2ZFxmOpaQG5/DcIghSw==",
       "dev": true,
       "requires": {
-        "@angular-devkit/core": "12.2.13",
+        "@angular-devkit/core": "12.2.17",
         "ora": "5.4.1",
         "rxjs": "6.6.7"
       }
     },
     "@angular/animations": {
-      "version": "12.2.13",
-      "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-12.2.13.tgz",
-      "integrity": "sha512-qpdmvu+nxsKnimJ3Hc1joNuzK7xXYyE+VtNMk4K77Ao/9+5C/juGMce85DhqZCcu1xraZ3YYIrzYmL/GgdUK4g==",
+      "version": "12.2.16",
+      "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-12.2.16.tgz",
+      "integrity": "sha512-Kf6C7Ta+fCMq5DvT9JNVhBkcECrqFa3wumiC6ssGo5sNaEzXz+tlep9ZgEbqfxSn7gAN7L1DgsbS9u0O6tbUkg==",
       "requires": {
         "tslib": "^2.2.0"
       }
@@ -256,15 +343,15 @@
       }
     },
     "@angular/cli": {
-      "version": "12.2.13",
-      "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-12.2.13.tgz",
-      "integrity": "sha512-Yz6MuwdxxP6U2i8iRuCSNZeJxlLDPshT/joymCsFdjwSMGEH9Kk9DdvAfFYfzdHGdHbGrDdASJ4G+uALyNSLxw==",
+      "version": "12.2.17",
+      "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-12.2.17.tgz",
+      "integrity": "sha512-mubRPp5hRIK/q0J8q6kVAqbYYuBUKMMBljUCqT4fHsl+qXYD27rgG3EqNzycKBMHUIlykotrDSdy47voD+atOg==",
       "dev": true,
       "requires": {
-        "@angular-devkit/architect": "0.1202.13",
-        "@angular-devkit/core": "12.2.13",
-        "@angular-devkit/schematics": "12.2.13",
-        "@schematics/angular": "12.2.13",
+        "@angular-devkit/architect": "0.1202.17",
+        "@angular-devkit/core": "12.2.17",
+        "@angular-devkit/schematics": "12.2.17",
+        "@schematics/angular": "12.2.17",
         "@yarnpkg/lockfile": "1.1.0",
         "ansi-colors": "4.1.1",
         "debug": "4.3.2",
@@ -275,7 +362,7 @@
         "npm-pick-manifest": "6.1.1",
         "open": "8.2.1",
         "ora": "5.4.1",
-        "pacote": "11.3.5",
+        "pacote": "12.0.2",
         "resolve": "1.20.0",
         "semver": "7.3.5",
         "symbol-observable": "4.0.0",
@@ -291,34 +378,38 @@
             "ms": "2.1.2"
           }
         },
-        "uuid": {
-          "version": "8.3.2",
-          "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
-          "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
-          "dev": true
+        "resolve": {
+          "version": "1.20.0",
+          "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz",
+          "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==",
+          "dev": true,
+          "requires": {
+            "is-core-module": "^2.2.0",
+            "path-parse": "^1.0.6"
+          }
         }
       }
     },
     "@angular/common": {
-      "version": "12.2.13",
-      "resolved": "https://registry.npmjs.org/@angular/common/-/common-12.2.13.tgz",
-      "integrity": "sha512-I1t/jl9ojCwTJKT7PKHnk23D+vMHTHS/9qZ2nndCjzGusMK8029nn8l3tHAkwefvxZWSaLAk1MgsVcP+rHQNsQ==",
+      "version": "12.2.16",
+      "resolved": "https://registry.npmjs.org/@angular/common/-/common-12.2.16.tgz",
+      "integrity": "sha512-FEqTXTEsnbDInqV1yFlm97Tz1OFqZS5t0TUkm8gzXRgpIce/F/jLwAg0u1VQkgOsno6cNm0xTWPoZgu85NI4ug==",
       "requires": {
         "tslib": "^2.2.0"
       }
     },
     "@angular/compiler": {
-      "version": "12.2.13",
-      "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-12.2.13.tgz",
-      "integrity": "sha512-L0saTTJJtxldjhaGIL6b1BCfodPOEz4Wrev3pEUK5UcODooj5HLiE/aO6jiNb8M4XTbdqByKyqvZyWzGHeexVQ==",
+      "version": "12.2.16",
+      "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-12.2.16.tgz",
+      "integrity": "sha512-nsYEw+yu8QyeqPf9nAmG419i1mtGM4v8+U+S3eQHQFXTgJzLymMykWHYu2ETdjUpNSLK6xcIQDBWtWnWSfJjAA==",
       "requires": {
         "tslib": "^2.2.0"
       }
     },
     "@angular/compiler-cli": {
-      "version": "12.2.13",
-      "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-12.2.13.tgz",
-      "integrity": "sha512-qmnmihl3SCRooj/mCsNIZLtnQ6qbx1/L6aMIEQosPvQhMeGMt8GCYvQPE8IZ+sahv7fih95HCWNa9TeLpOylug==",
+      "version": "12.2.16",
+      "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-12.2.16.tgz",
+      "integrity": "sha512-tlalh8SJvdCWbUPRUR5GamaP+wSc/GuCsoUZpSbcczGKgSlbaEVXUYtVXm8/wuT6Slk2sSEbRs7tXGF2i7qxVw==",
       "dev": true,
       "requires": {
         "@babel/core": "^7.8.6",
@@ -337,12 +428,6 @@
         "yargs": "^17.0.0"
       },
       "dependencies": {
-        "ansi-regex": {
-          "version": "5.0.1",
-          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
-          "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
-          "dev": true
-        },
         "ansi-styles": {
           "version": "4.3.0",
           "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
@@ -378,44 +463,12 @@
           "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
           "dev": true
         },
-        "emoji-regex": {
-          "version": "8.0.0",
-          "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
-          "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
-          "dev": true
-        },
-        "is-fullwidth-code-point": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
-          "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
-          "dev": true
-        },
         "source-map": {
           "version": "0.6.1",
           "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
           "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
           "dev": true
         },
-        "string-width": {
-          "version": "4.2.3",
-          "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
-          "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
-          "dev": true,
-          "requires": {
-            "emoji-regex": "^8.0.0",
-            "is-fullwidth-code-point": "^3.0.0",
-            "strip-ansi": "^6.0.1"
-          }
-        },
-        "strip-ansi": {
-          "version": "6.0.1",
-          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
-          "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
-          "dev": true,
-          "requires": {
-            "ansi-regex": "^5.0.1"
-          }
-        },
         "wrap-ansi": {
           "version": "7.0.0",
           "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
@@ -434,40 +487,40 @@
           "dev": true
         },
         "yargs": {
-          "version": "17.2.1",
-          "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.2.1.tgz",
-          "integrity": "sha512-XfR8du6ua4K6uLGm5S6fA+FIJom/MdJcFNVY8geLlp2v8GYbOXD4EB1tPNZsRn4vBzKGMgb5DRZMeWuFc2GO8Q==",
+          "version": "17.5.1",
+          "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz",
+          "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==",
           "dev": true,
           "requires": {
             "cliui": "^7.0.2",
             "escalade": "^3.1.1",
             "get-caller-file": "^2.0.5",
             "require-directory": "^2.1.1",
-            "string-width": "^4.2.0",
+            "string-width": "^4.2.3",
             "y18n": "^5.0.5",
-            "yargs-parser": "^20.2.2"
+            "yargs-parser": "^21.0.0"
           }
         },
         "yargs-parser": {
-          "version": "20.2.9",
-          "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
-          "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
+          "version": "21.0.1",
+          "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz",
+          "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==",
           "dev": true
         }
       }
     },
     "@angular/core": {
-      "version": "12.2.13",
-      "resolved": "https://registry.npmjs.org/@angular/core/-/core-12.2.13.tgz",
-      "integrity": "sha512-tZ5nAnmOi418JDaJIFiiP9z2JrluMJZvUvXO4QDUs52BXaL2yuP7MJ2LczWOVJXrBLZXeZGfjDjZmaFPA24grg==",
+      "version": "12.2.16",
+      "resolved": "https://registry.npmjs.org/@angular/core/-/core-12.2.16.tgz",
+      "integrity": "sha512-jsmvaRdAfng99z2a9mAmkfcsCE1wm+tBYVDxnc5JquSXznwtncjzcoc2X0J0dzrkCDvzFfpTsZ9vehylytBc+A==",
       "requires": {
         "tslib": "^2.2.0"
       }
     },
     "@angular/forms": {
-      "version": "12.2.13",
-      "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-12.2.13.tgz",
-      "integrity": "sha512-wa7I5R8sck2q+VWNL262GJTVxtpEHMys8Bt6oE+lyHB5zlZAgOXwAb8GE4XVwuc+BZU1Gvrocn21P/8KvDY1uw==",
+      "version": "12.2.16",
+      "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-12.2.16.tgz",
+      "integrity": "sha512-sb+gpNun5aN7CZfHXS6X7vJcd/0A1P/gRBZpYtQTzBYnqEFCOFIvR62eb05aHQ4JhgKaSPpIXrbz/bAwY/njZw==",
       "requires": {
         "tslib": "^2.2.0"
       }
@@ -481,25 +534,25 @@
       }
     },
     "@angular/platform-browser": {
-      "version": "12.2.13",
-      "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-12.2.13.tgz",
-      "integrity": "sha512-Ua8m2GtG2msqz/Mr/MK7s8RXud554x8cup2THVAwetyTaY5th/1LOZX0hhDIhfsxBCLHnC53LRhMbSsI0cikOg==",
+      "version": "12.2.16",
+      "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-12.2.16.tgz",
+      "integrity": "sha512-T855ppLeQO6hRHi7lGf5fwPoUVt+c0h2rgkV5jHElc3ylaGnhecmZc6fnWLX4pw82TMJUgUV88CY8JCFabJWwg==",
       "requires": {
         "tslib": "^2.2.0"
       }
     },
     "@angular/platform-browser-dynamic": {
-      "version": "12.2.13",
-      "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-12.2.13.tgz",
-      "integrity": "sha512-a7y7R3vOXhMAk9uQWK5/23DefahuF0UYJSFM/wKeVo5zR+qOCVCTfafkJMcWbuZWTrSDbVafJ1xbcWnu3+TkCg==",
+      "version": "12.2.16",
+      "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-12.2.16.tgz",
+      "integrity": "sha512-XGxoACAMW/bc3atiVRpaiYwU4LkobYwVzwlxTT/BxOfsdt8ILb5wU8Fx1TMKNECOQHSGdK0qqhch4pTBZ3cb2g==",
       "requires": {
         "tslib": "^2.2.0"
       }
     },
     "@angular/router": {
-      "version": "12.2.13",
-      "resolved": "https://registry.npmjs.org/@angular/router/-/router-12.2.13.tgz",
-      "integrity": "sha512-HZL/Pzp6I7fQiMLrzfEzhnqhgNcGcFjBgMMOoLp5IA1IV26rp1NU6zYJzPrXtopBx7XLl8BECehAwFqrJsu/PQ==",
+      "version": "12.2.16",
+      "resolved": "https://registry.npmjs.org/@angular/router/-/router-12.2.16.tgz",
+      "integrity": "sha512-LuFXSMIvX/VrB4jbYhigG2Y2pGQ9ULsSBUwDWwQCf4kr0eVI37LBJ2Vr74GBEznjgQ0UmWE89E+XYI80UhERTw==",
       "requires": {
         "tslib": "^2.2.0"
       }
@@ -511,54 +564,103 @@
       "dev": true
     },
     "@babel/code-frame": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.0.tgz",
-      "integrity": "sha512-IF4EOMEV+bfYwOmNxGzSnjR2EmQod7f1UXOpZM3l4i4o4QNwzjtJAu/HxdjHq0aYBvdqMuQEY1eg0nqW9ZPORA==",
+      "version": "7.16.7",
+      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz",
+      "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==",
       "dev": true,
       "requires": {
-        "@babel/highlight": "^7.16.0"
+        "@babel/highlight": "^7.16.7"
       }
     },
     "@babel/compat-data": {
-      "version": "7.16.4",
-      "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.16.4.tgz",
-      "integrity": "sha512-1o/jo7D+kC9ZjHX5v+EHrdjl3PhxMrLSOTGsOdHJ+KL8HCaEK6ehrVL2RS6oHDZp+L7xLirLrPmQtEng769J/Q==",
+      "version": "7.17.10",
+      "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.17.10.tgz",
+      "integrity": "sha512-GZt/TCsG70Ms19gfZO1tM4CVnXsPgEPBCpJu+Qz3L0LUDsY5nZqFZglIoPC1kIYOtNBZlrnFT+klg12vFGZXrw==",
       "dev": true
     },
     "@babel/core": {
-      "version": "7.14.8",
-      "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.14.8.tgz",
-      "integrity": "sha512-/AtaeEhT6ErpDhInbXmjHcUQXH0L0TEgscfcxk1qbOvLuKCa5aZT0SOOtDKFY96/CLROwbLSKyFor6idgNaU4Q==",
-      "dev": true,
-      "requires": {
-        "@babel/code-frame": "^7.14.5",
-        "@babel/generator": "^7.14.8",
-        "@babel/helper-compilation-targets": "^7.14.5",
-        "@babel/helper-module-transforms": "^7.14.8",
-        "@babel/helpers": "^7.14.8",
-        "@babel/parser": "^7.14.8",
-        "@babel/template": "^7.14.5",
-        "@babel/traverse": "^7.14.8",
-        "@babel/types": "^7.14.8",
+      "version": "7.18.0",
+      "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.0.tgz",
+      "integrity": "sha512-Xyw74OlJwDijToNi0+6BBI5mLLR5+5R3bcSH80LXzjzEGEUlvNzujEE71BaD/ApEZHAvFI/Mlmp4M5lIkdeeWw==",
+      "dev": true,
+      "requires": {
+        "@ampproject/remapping": "^2.1.0",
+        "@babel/code-frame": "^7.16.7",
+        "@babel/generator": "^7.18.0",
+        "@babel/helper-compilation-targets": "^7.17.10",
+        "@babel/helper-module-transforms": "^7.18.0",
+        "@babel/helpers": "^7.18.0",
+        "@babel/parser": "^7.18.0",
+        "@babel/template": "^7.16.7",
+        "@babel/traverse": "^7.18.0",
+        "@babel/types": "^7.18.0",
         "convert-source-map": "^1.7.0",
         "debug": "^4.1.0",
         "gensync": "^1.0.0-beta.2",
-        "json5": "^2.1.2",
-        "semver": "^6.3.0",
-        "source-map": "^0.5.0"
+        "json5": "^2.2.1",
+        "semver": "^6.3.0"
       },
       "dependencies": {
+        "@ampproject/remapping": {
+          "version": "2.2.0",
+          "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz",
+          "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==",
+          "dev": true,
+          "requires": {
+            "@jridgewell/gen-mapping": "^0.1.0",
+            "@jridgewell/trace-mapping": "^0.3.9"
+          }
+        },
+        "@babel/generator": {
+          "version": "7.18.0",
+          "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.0.tgz",
+          "integrity": "sha512-81YO9gGx6voPXlvYdZBliFXAZU8vZ9AZ6z+CjlmcnaeOcYSFbMTpdeDUO9xD9dh/68Vq03I8ZspfUTPfitcDHg==",
+          "dev": true,
+          "requires": {
+            "@babel/types": "^7.18.0",
+            "@jridgewell/gen-mapping": "^0.3.0",
+            "jsesc": "^2.5.1"
+          },
+          "dependencies": {
+            "@jridgewell/gen-mapping": {
+              "version": "0.3.1",
+              "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.1.tgz",
+              "integrity": "sha512-GcHwniMlA2z+WFPWuY8lp3fsza0I8xPFMWL5+n8LYyP6PSvPrXf4+n8stDHZY2DM0zy9sVkRDy1jDI4XGzYVqg==",
+              "dev": true,
+              "requires": {
+                "@jridgewell/set-array": "^1.0.0",
+                "@jridgewell/sourcemap-codec": "^1.4.10",
+                "@jridgewell/trace-mapping": "^0.3.9"
+              }
+            }
+          }
+        },
+        "@babel/template": {
+          "version": "7.16.7",
+          "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz",
+          "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==",
+          "dev": true,
+          "requires": {
+            "@babel/code-frame": "^7.16.7",
+            "@babel/parser": "^7.16.7",
+            "@babel/types": "^7.16.7"
+          }
+        },
+        "@jridgewell/gen-mapping": {
+          "version": "0.1.1",
+          "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz",
+          "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==",
+          "dev": true,
+          "requires": {
+            "@jridgewell/set-array": "^1.0.0",
+            "@jridgewell/sourcemap-codec": "^1.4.10"
+          }
+        },
         "semver": {
           "version": "6.3.0",
           "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
           "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
           "dev": true
-        },
-        "source-map": {
-          "version": "0.5.7",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
-          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
-          "dev": true
         }
       }
     },
@@ -591,24 +693,24 @@
       }
     },
     "@babel/helper-builder-binary-assignment-operator-visitor": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.16.0.tgz",
-      "integrity": "sha512-9KuleLT0e77wFUku6TUkqZzCEymBdtuQQ27MhEKzf9UOOJu3cYj98kyaDAzxpC7lV6DGiZFuC8XqDsq8/Kl6aQ==",
+      "version": "7.16.7",
+      "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.16.7.tgz",
+      "integrity": "sha512-C6FdbRaxYjwVu/geKW4ZeQ0Q31AftgRcdSnZ5/jsH6BzCJbtvXvhpfkbkThYSuutZA7nCXpPR6AD9zd1dprMkA==",
       "dev": true,
       "requires": {
-        "@babel/helper-explode-assignable-expression": "^7.16.0",
-        "@babel/types": "^7.16.0"
+        "@babel/helper-explode-assignable-expression": "^7.16.7",
+        "@babel/types": "^7.16.7"
       }
     },
     "@babel/helper-compilation-targets": {
-      "version": "7.16.3",
-      "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.3.tgz",
-      "integrity": "sha512-vKsoSQAyBmxS35JUOOt+07cLc6Nk/2ljLIHwmq2/NM6hdioUaqEXq/S+nXvbvXbZkNDlWOymPanJGOc4CBjSJA==",
+      "version": "7.17.10",
+      "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.10.tgz",
+      "integrity": "sha512-gh3RxjWbauw/dFiU/7whjd0qN9K6nPJMqe6+Er7rOavFh0CQUSwhAE3IcTho2rywPJFxej6TUUHDkWcYI6gGqQ==",
       "dev": true,
       "requires": {
-        "@babel/compat-data": "^7.16.0",
-        "@babel/helper-validator-option": "^7.14.5",
-        "browserslist": "^4.17.5",
+        "@babel/compat-data": "^7.17.10",
+        "@babel/helper-validator-option": "^7.16.7",
+        "browserslist": "^4.20.2",
         "semver": "^6.3.0"
       },
       "dependencies": {
@@ -621,47 +723,48 @@
       }
     },
     "@babel/helper-create-class-features-plugin": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.16.0.tgz",
-      "integrity": "sha512-XLwWvqEaq19zFlF5PTgOod4bUA+XbkR4WLQBct1bkzmxJGB0ZEJaoKF4c8cgH9oBtCDuYJ8BP5NB9uFiEgO5QA==",
-      "dev": true,
-      "requires": {
-        "@babel/helper-annotate-as-pure": "^7.16.0",
-        "@babel/helper-function-name": "^7.16.0",
-        "@babel/helper-member-expression-to-functions": "^7.16.0",
-        "@babel/helper-optimise-call-expression": "^7.16.0",
-        "@babel/helper-replace-supers": "^7.16.0",
-        "@babel/helper-split-export-declaration": "^7.16.0"
+      "version": "7.18.0",
+      "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.18.0.tgz",
+      "integrity": "sha512-Kh8zTGR9de3J63e5nS0rQUdRs/kbtwoeQQ0sriS0lItjC96u8XXZN6lKpuyWd2coKSU13py/y+LTmThLuVX0Pg==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-annotate-as-pure": "^7.16.7",
+        "@babel/helper-environment-visitor": "^7.16.7",
+        "@babel/helper-function-name": "^7.17.9",
+        "@babel/helper-member-expression-to-functions": "^7.17.7",
+        "@babel/helper-optimise-call-expression": "^7.16.7",
+        "@babel/helper-replace-supers": "^7.16.7",
+        "@babel/helper-split-export-declaration": "^7.16.7"
       },
       "dependencies": {
         "@babel/helper-annotate-as-pure": {
-          "version": "7.16.0",
-          "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.0.tgz",
-          "integrity": "sha512-ItmYF9vR4zA8cByDocY05o0LGUkp1zhbTQOH1NFyl5xXEqlTJQCEJjieriw+aFpxo16swMxUnUiKS7a/r4vtHg==",
+          "version": "7.16.7",
+          "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz",
+          "integrity": "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==",
           "dev": true,
           "requires": {
-            "@babel/types": "^7.16.0"
+            "@babel/types": "^7.16.7"
           }
         }
       }
     },
     "@babel/helper-create-regexp-features-plugin": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.16.0.tgz",
-      "integrity": "sha512-3DyG0zAFAZKcOp7aVr33ddwkxJ0Z0Jr5V99y3I690eYLpukJsJvAbzTy1ewoCqsML8SbIrjH14Jc/nSQ4TvNPA==",
+      "version": "7.17.12",
+      "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.17.12.tgz",
+      "integrity": "sha512-b2aZrV4zvutr9AIa6/gA3wsZKRwTKYoDxYiFKcESS3Ug2GTXzwBEvMuuFLhCQpEnRXs1zng4ISAXSUxxKBIcxw==",
       "dev": true,
       "requires": {
-        "@babel/helper-annotate-as-pure": "^7.16.0",
-        "regexpu-core": "^4.7.1"
+        "@babel/helper-annotate-as-pure": "^7.16.7",
+        "regexpu-core": "^5.0.1"
       },
       "dependencies": {
         "@babel/helper-annotate-as-pure": {
-          "version": "7.16.0",
-          "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.0.tgz",
-          "integrity": "sha512-ItmYF9vR4zA8cByDocY05o0LGUkp1zhbTQOH1NFyl5xXEqlTJQCEJjieriw+aFpxo16swMxUnUiKS7a/r4vtHg==",
+          "version": "7.16.7",
+          "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz",
+          "integrity": "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==",
           "dev": true,
           "requires": {
-            "@babel/types": "^7.16.0"
+            "@babel/types": "^7.16.7"
           }
         }
       }
@@ -690,160 +793,160 @@
         }
       }
     },
+    "@babel/helper-environment-visitor": {
+      "version": "7.16.7",
+      "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz",
+      "integrity": "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==",
+      "dev": true,
+      "requires": {
+        "@babel/types": "^7.16.7"
+      }
+    },
     "@babel/helper-explode-assignable-expression": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.0.tgz",
-      "integrity": "sha512-Hk2SLxC9ZbcOhLpg/yMznzJ11W++lg5GMbxt1ev6TXUiJB0N42KPC+7w8a+eWGuqDnUYuwStJoZHM7RgmIOaGQ==",
+      "version": "7.16.7",
+      "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.7.tgz",
+      "integrity": "sha512-KyUenhWMC8VrxzkGP0Jizjo4/Zx+1nNZhgocs+gLzyZyB8SHidhoq9KK/8Ato4anhwsivfkBLftky7gvzbZMtQ==",
       "dev": true,
       "requires": {
-        "@babel/types": "^7.16.0"
+        "@babel/types": "^7.16.7"
       }
     },
     "@babel/helper-function-name": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.0.tgz",
-      "integrity": "sha512-BZh4mEk1xi2h4HFjWUXRQX5AEx4rvaZxHgax9gcjdLWdkjsY7MKt5p0otjsg5noXw+pB+clMCjw+aEVYADMjog==",
+      "version": "7.17.9",
+      "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.17.9.tgz",
+      "integrity": "sha512-7cRisGlVtiVqZ0MW0/yFB4atgpGLWEHUVYnb448hZK4x+vih0YO5UoS11XIYtZYqHd0dIPMdUSv8q5K4LdMnIg==",
       "dev": true,
       "requires": {
-        "@babel/helper-get-function-arity": "^7.16.0",
-        "@babel/template": "^7.16.0",
-        "@babel/types": "^7.16.0"
+        "@babel/template": "^7.16.7",
+        "@babel/types": "^7.17.0"
       },
       "dependencies": {
         "@babel/template": {
-          "version": "7.16.0",
-          "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.0.tgz",
-          "integrity": "sha512-MnZdpFD/ZdYhXwiunMqqgyZyucaYsbL0IrjoGjaVhGilz+x8YB++kRfygSOIj1yOtWKPlx7NBp+9I1RQSgsd5A==",
+          "version": "7.16.7",
+          "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz",
+          "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==",
           "dev": true,
           "requires": {
-            "@babel/code-frame": "^7.16.0",
-            "@babel/parser": "^7.16.0",
-            "@babel/types": "^7.16.0"
+            "@babel/code-frame": "^7.16.7",
+            "@babel/parser": "^7.16.7",
+            "@babel/types": "^7.16.7"
           }
         }
       }
     },
-    "@babel/helper-get-function-arity": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.0.tgz",
-      "integrity": "sha512-ASCquNcywC1NkYh/z7Cgp3w31YW8aojjYIlNg4VeJiHkqyP4AzIvr4qx7pYDb4/s8YcsZWqqOSxgkvjUz1kpDQ==",
-      "dev": true,
-      "requires": {
-        "@babel/types": "^7.16.0"
-      }
-    },
     "@babel/helper-hoist-variables": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.0.tgz",
-      "integrity": "sha512-1AZlpazjUR0EQZQv3sgRNfM9mEVWPK3M6vlalczA+EECcPz3XPh6VplbErL5UoMpChhSck5wAJHthlj1bYpcmg==",
+      "version": "7.16.7",
+      "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz",
+      "integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==",
       "dev": true,
       "requires": {
-        "@babel/types": "^7.16.0"
+        "@babel/types": "^7.16.7"
       }
     },
     "@babel/helper-member-expression-to-functions": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.16.0.tgz",
-      "integrity": "sha512-bsjlBFPuWT6IWhl28EdrQ+gTvSvj5tqVP5Xeftp07SEuz5pLnsXZuDkDD3Rfcxy0IsHmbZ+7B2/9SHzxO0T+sQ==",
+      "version": "7.17.7",
+      "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.17.7.tgz",
+      "integrity": "sha512-thxXgnQ8qQ11W2wVUObIqDL4p148VMxkt5T/qpN5k2fboRyzFGFmKsTGViquyM5QHKUy48OZoca8kw4ajaDPyw==",
       "dev": true,
       "requires": {
-        "@babel/types": "^7.16.0"
+        "@babel/types": "^7.17.0"
       }
     },
     "@babel/helper-module-imports": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.0.tgz",
-      "integrity": "sha512-kkH7sWzKPq0xt3H1n+ghb4xEMP8k0U7XV3kkB+ZGy69kDk2ySFW1qPi06sjKzFY3t1j6XbJSqr4mF9L7CYVyhg==",
+      "version": "7.16.7",
+      "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz",
+      "integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==",
       "dev": true,
       "requires": {
-        "@babel/types": "^7.16.0"
+        "@babel/types": "^7.16.7"
       }
     },
     "@babel/helper-module-transforms": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.16.0.tgz",
-      "integrity": "sha512-My4cr9ATcaBbmaEa8M0dZNA74cfI6gitvUAskgDtAFmAqyFKDSHQo5YstxPbN+lzHl2D9l/YOEFqb2mtUh4gfA==",
-      "dev": true,
-      "requires": {
-        "@babel/helper-module-imports": "^7.16.0",
-        "@babel/helper-replace-supers": "^7.16.0",
-        "@babel/helper-simple-access": "^7.16.0",
-        "@babel/helper-split-export-declaration": "^7.16.0",
-        "@babel/helper-validator-identifier": "^7.15.7",
-        "@babel/template": "^7.16.0",
-        "@babel/traverse": "^7.16.0",
-        "@babel/types": "^7.16.0"
+      "version": "7.18.0",
+      "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.18.0.tgz",
+      "integrity": "sha512-kclUYSUBIjlvnzN2++K9f2qzYKFgjmnmjwL4zlmU5f8ZtzgWe8s0rUPSTGy2HmK4P8T52MQsS+HTQAgZd3dMEA==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-environment-visitor": "^7.16.7",
+        "@babel/helper-module-imports": "^7.16.7",
+        "@babel/helper-simple-access": "^7.17.7",
+        "@babel/helper-split-export-declaration": "^7.16.7",
+        "@babel/helper-validator-identifier": "^7.16.7",
+        "@babel/template": "^7.16.7",
+        "@babel/traverse": "^7.18.0",
+        "@babel/types": "^7.18.0"
       },
       "dependencies": {
         "@babel/template": {
-          "version": "7.16.0",
-          "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.0.tgz",
-          "integrity": "sha512-MnZdpFD/ZdYhXwiunMqqgyZyucaYsbL0IrjoGjaVhGilz+x8YB++kRfygSOIj1yOtWKPlx7NBp+9I1RQSgsd5A==",
+          "version": "7.16.7",
+          "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz",
+          "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==",
           "dev": true,
           "requires": {
-            "@babel/code-frame": "^7.16.0",
-            "@babel/parser": "^7.16.0",
-            "@babel/types": "^7.16.0"
+            "@babel/code-frame": "^7.16.7",
+            "@babel/parser": "^7.16.7",
+            "@babel/types": "^7.16.7"
           }
         }
       }
     },
     "@babel/helper-optimise-call-expression": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.0.tgz",
-      "integrity": "sha512-SuI467Gi2V8fkofm2JPnZzB/SUuXoJA5zXe/xzyPP2M04686RzFKFHPK6HDVN6JvWBIEW8tt9hPR7fXdn2Lgpw==",
+      "version": "7.16.7",
+      "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.7.tgz",
+      "integrity": "sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w==",
       "dev": true,
       "requires": {
-        "@babel/types": "^7.16.0"
+        "@babel/types": "^7.16.7"
       }
     },
     "@babel/helper-plugin-utils": {
-      "version": "7.14.5",
-      "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz",
-      "integrity": "sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ==",
+      "version": "7.17.12",
+      "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.17.12.tgz",
+      "integrity": "sha512-JDkf04mqtN3y4iAbO1hv9U2ARpPyPL1zqyWs/2WG1pgSq9llHFjStX5jdxb84himgJm+8Ng+x0oiWF/nw/XQKA==",
       "dev": true
     },
     "@babel/helper-remap-async-to-generator": {
-      "version": "7.16.4",
-      "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.4.tgz",
-      "integrity": "sha512-vGERmmhR+s7eH5Y/cp8PCVzj4XEjerq8jooMfxFdA5xVtAk9Sh4AQsrWgiErUEBjtGrBtOFKDUcWQFW4/dFwMA==",
+      "version": "7.16.8",
+      "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.8.tgz",
+      "integrity": "sha512-fm0gH7Flb8H51LqJHy3HJ3wnE1+qtYR2A99K06ahwrawLdOFsCEWjZOrYricXJHoPSudNKxrMBUPEIPxiIIvBw==",
       "dev": true,
       "requires": {
-        "@babel/helper-annotate-as-pure": "^7.16.0",
-        "@babel/helper-wrap-function": "^7.16.0",
-        "@babel/types": "^7.16.0"
+        "@babel/helper-annotate-as-pure": "^7.16.7",
+        "@babel/helper-wrap-function": "^7.16.8",
+        "@babel/types": "^7.16.8"
       },
       "dependencies": {
         "@babel/helper-annotate-as-pure": {
-          "version": "7.16.0",
-          "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.0.tgz",
-          "integrity": "sha512-ItmYF9vR4zA8cByDocY05o0LGUkp1zhbTQOH1NFyl5xXEqlTJQCEJjieriw+aFpxo16swMxUnUiKS7a/r4vtHg==",
+          "version": "7.16.7",
+          "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz",
+          "integrity": "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==",
           "dev": true,
           "requires": {
-            "@babel/types": "^7.16.0"
+            "@babel/types": "^7.16.7"
           }
         }
       }
     },
     "@babel/helper-replace-supers": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.16.0.tgz",
-      "integrity": "sha512-TQxuQfSCdoha7cpRNJvfaYxxxzmbxXw/+6cS7V02eeDYyhxderSoMVALvwupA54/pZcOTtVeJ0xccp1nGWladA==",
+      "version": "7.16.7",
+      "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.16.7.tgz",
+      "integrity": "sha512-y9vsWilTNaVnVh6xiJfABzsNpgDPKev9HnAgz6Gb1p6UUwf9NepdlsV7VXGCftJM+jqD5f7JIEubcpLjZj5dBw==",
       "dev": true,
       "requires": {
-        "@babel/helper-member-expression-to-functions": "^7.16.0",
-        "@babel/helper-optimise-call-expression": "^7.16.0",
-        "@babel/traverse": "^7.16.0",
-        "@babel/types": "^7.16.0"
+        "@babel/helper-environment-visitor": "^7.16.7",
+        "@babel/helper-member-expression-to-functions": "^7.16.7",
+        "@babel/helper-optimise-call-expression": "^7.16.7",
+        "@babel/traverse": "^7.16.7",
+        "@babel/types": "^7.16.7"
       }
     },
     "@babel/helper-simple-access": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.16.0.tgz",
-      "integrity": "sha512-o1rjBT/gppAqKsYfUdfHq5Rk03lMQrkPHG1OWzHWpLgVXRH4HnMM9Et9CVdIqwkCQlobnGHEJMsgWP/jE1zUiw==",
+      "version": "7.17.7",
+      "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.17.7.tgz",
+      "integrity": "sha512-txyMCGroZ96i+Pxr3Je3lzEJjqwaRC9buMUgtomcrLe5Nd0+fk1h0LLA+ixUF5OW7AhHuQ7Es1WcQJZmZsz2XA==",
       "dev": true,
       "requires": {
-        "@babel/types": "^7.16.0"
+        "@babel/types": "^7.17.0"
       }
     },
     "@babel/helper-skip-transparent-expression-wrappers": {
@@ -856,101 +959,110 @@
       }
     },
     "@babel/helper-split-export-declaration": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.0.tgz",
-      "integrity": "sha512-0YMMRpuDFNGTHNRiiqJX19GjNXA4H0E8jZ2ibccfSxaCogbm3am5WN/2nQNj0YnQwGWM1J06GOcQ2qnh3+0paw==",
+      "version": "7.16.7",
+      "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz",
+      "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==",
       "dev": true,
       "requires": {
-        "@babel/types": "^7.16.0"
+        "@babel/types": "^7.16.7"
       }
     },
     "@babel/helper-validator-identifier": {
-      "version": "7.15.7",
-      "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz",
-      "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==",
+      "version": "7.16.7",
+      "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz",
+      "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==",
       "dev": true
     },
     "@babel/helper-validator-option": {
-      "version": "7.14.5",
-      "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz",
-      "integrity": "sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow==",
+      "version": "7.16.7",
+      "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz",
+      "integrity": "sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==",
       "dev": true
     },
     "@babel/helper-wrap-function": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.16.0.tgz",
-      "integrity": "sha512-VVMGzYY3vkWgCJML+qVLvGIam902mJW0FvT7Avj1zEe0Gn7D93aWdLblYARTxEw+6DhZmtzhBM2zv0ekE5zg1g==",
+      "version": "7.16.8",
+      "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.16.8.tgz",
+      "integrity": "sha512-8RpyRVIAW1RcDDGTA+GpPAwV22wXCfKOoM9bet6TLkGIFTkRQSkH1nMQ5Yet4MpoXe1ZwHPVtNasc2w0uZMqnw==",
       "dev": true,
       "requires": {
-        "@babel/helper-function-name": "^7.16.0",
-        "@babel/template": "^7.16.0",
-        "@babel/traverse": "^7.16.0",
-        "@babel/types": "^7.16.0"
+        "@babel/helper-function-name": "^7.16.7",
+        "@babel/template": "^7.16.7",
+        "@babel/traverse": "^7.16.8",
+        "@babel/types": "^7.16.8"
       },
       "dependencies": {
         "@babel/template": {
-          "version": "7.16.0",
-          "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.0.tgz",
-          "integrity": "sha512-MnZdpFD/ZdYhXwiunMqqgyZyucaYsbL0IrjoGjaVhGilz+x8YB++kRfygSOIj1yOtWKPlx7NBp+9I1RQSgsd5A==",
+          "version": "7.16.7",
+          "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz",
+          "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==",
           "dev": true,
           "requires": {
-            "@babel/code-frame": "^7.16.0",
-            "@babel/parser": "^7.16.0",
-            "@babel/types": "^7.16.0"
+            "@babel/code-frame": "^7.16.7",
+            "@babel/parser": "^7.16.7",
+            "@babel/types": "^7.16.7"
           }
         }
       }
     },
     "@babel/helpers": {
-      "version": "7.16.3",
-      "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.16.3.tgz",
-      "integrity": "sha512-Xn8IhDlBPhvYTvgewPKawhADichOsbkZuzN7qz2BusOM0brChsyXMDJvldWaYMMUNiCQdQzNEioXTp3sC8Nt8w==",
+      "version": "7.18.0",
+      "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.18.0.tgz",
+      "integrity": "sha512-AE+HMYhmlMIbho9nbvicHyxFwhrO+xhKB6AhRxzl8w46Yj0VXTZjEsAoBVC7rB2I0jzX+yWyVybnO08qkfx6kg==",
       "dev": true,
       "requires": {
-        "@babel/template": "^7.16.0",
-        "@babel/traverse": "^7.16.3",
-        "@babel/types": "^7.16.0"
+        "@babel/template": "^7.16.7",
+        "@babel/traverse": "^7.18.0",
+        "@babel/types": "^7.18.0"
       },
       "dependencies": {
         "@babel/template": {
-          "version": "7.16.0",
-          "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.0.tgz",
-          "integrity": "sha512-MnZdpFD/ZdYhXwiunMqqgyZyucaYsbL0IrjoGjaVhGilz+x8YB++kRfygSOIj1yOtWKPlx7NBp+9I1RQSgsd5A==",
+          "version": "7.16.7",
+          "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz",
+          "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==",
           "dev": true,
           "requires": {
-            "@babel/code-frame": "^7.16.0",
-            "@babel/parser": "^7.16.0",
-            "@babel/types": "^7.16.0"
+            "@babel/code-frame": "^7.16.7",
+            "@babel/parser": "^7.16.7",
+            "@babel/types": "^7.16.7"
           }
         }
       }
     },
     "@babel/highlight": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.0.tgz",
-      "integrity": "sha512-t8MH41kUQylBtu2+4IQA3atqevA2lRgqA2wyVB/YiWmsDSuylZZuXOUy9ric30hfzauEFfdsuk/eXTRrGrfd0g==",
+      "version": "7.17.12",
+      "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.17.12.tgz",
+      "integrity": "sha512-7yykMVF3hfZY2jsHZEEgLc+3x4o1O+fYyULu11GynEUQNwB6lua+IIQn1FiJxNucd5UlyJryrwsOh8PL9Sn8Qg==",
       "dev": true,
       "requires": {
-        "@babel/helper-validator-identifier": "^7.15.7",
+        "@babel/helper-validator-identifier": "^7.16.7",
         "chalk": "^2.0.0",
         "js-tokens": "^4.0.0"
       }
     },
     "@babel/parser": {
-      "version": "7.16.4",
-      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.4.tgz",
-      "integrity": "sha512-6V0qdPUaiVHH3RtZeLIsc+6pDhbYzHR8ogA8w+f+Wc77DuXto19g2QUwveINoS34Uw+W8/hQDGJCx+i4n7xcng==",
+      "version": "7.18.0",
+      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.0.tgz",
+      "integrity": "sha512-AqDccGC+m5O/iUStSJy3DGRIUFu7WbY/CppZYwrEUB4N0tZlnI8CSTsgL7v5fHVFmUbRv2sd+yy27o8Ydt4MGg==",
       "dev": true
     },
+    "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": {
+      "version": "7.17.12",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.17.12.tgz",
+      "integrity": "sha512-xCJQXl4EeQ3J9C4yOmpTrtVGmzpm2iSzyxbkZHw7UCnZBftHpF/hpII80uWVyVrc40ytIClHjgWGTG1g/yB+aw==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.17.12"
+      }
+    },
     "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.16.0.tgz",
-      "integrity": "sha512-4tcFwwicpWTrpl9qjf7UsoosaArgImF85AxqCRZlgc3IQDvkUHjJpruXAL58Wmj+T6fypWTC/BakfEkwIL/pwA==",
+      "version": "7.17.12",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.17.12.tgz",
+      "integrity": "sha512-/vt0hpIw0x4b6BLKUkwlvEoiGZYYLNZ96CzyHYPbtG2jZGz6LBe7/V+drYrc/d+ovrF9NBi0pmtvmNb/FsWtRQ==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.14.5",
+        "@babel/helper-plugin-utils": "^7.17.12",
         "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0",
-        "@babel/plugin-proposal-optional-chaining": "^7.16.0"
+        "@babel/plugin-proposal-optional-chaining": "^7.17.12"
       }
     },
     "@babel/plugin-proposal-async-generator-functions": {
@@ -965,161 +1077,185 @@
       }
     },
     "@babel/plugin-proposal-class-properties": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.0.tgz",
-      "integrity": "sha512-mCF3HcuZSY9Fcx56Lbn+CGdT44ioBMMvjNVldpKtj8tpniETdLjnxdHI1+sDWXIM1nNt+EanJOZ3IG9lzVjs7A==",
+      "version": "7.17.12",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.17.12.tgz",
+      "integrity": "sha512-U0mI9q8pW5Q9EaTHFPwSVusPMV/DV9Mm8p7csqROFLtIE9rBF5piLqyrBGigftALrBcsBGu4m38JneAe7ZDLXw==",
       "dev": true,
       "requires": {
-        "@babel/helper-create-class-features-plugin": "^7.16.0",
-        "@babel/helper-plugin-utils": "^7.14.5"
+        "@babel/helper-create-class-features-plugin": "^7.17.12",
+        "@babel/helper-plugin-utils": "^7.17.12"
       }
     },
     "@babel/plugin-proposal-class-static-block": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.16.0.tgz",
-      "integrity": "sha512-mAy3sdcY9sKAkf3lQbDiv3olOfiLqI51c9DR9b19uMoR2Z6r5pmGl7dfNFqEvqOyqbf1ta4lknK4gc5PJn3mfA==",
+      "version": "7.18.0",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.18.0.tgz",
+      "integrity": "sha512-t+8LsRMMDE74c6sV7KShIw13sqbqd58tlqNrsWoWBTIMw7SVQ0cZ905wLNS/FBCy/3PyooRHLFFlfrUNyyz5lA==",
       "dev": true,
       "requires": {
-        "@babel/helper-create-class-features-plugin": "^7.16.0",
-        "@babel/helper-plugin-utils": "^7.14.5",
+        "@babel/helper-create-class-features-plugin": "^7.18.0",
+        "@babel/helper-plugin-utils": "^7.17.12",
         "@babel/plugin-syntax-class-static-block": "^7.14.5"
       }
     },
+    "@babel/plugin-proposal-decorators": {
+      "version": "7.17.12",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.17.12.tgz",
+      "integrity": "sha512-gL0qSSeIk/VRfTDgtQg/EtejENssN/r3p5gJsPie1UacwiHibprpr19Z0pcK3XKuqQvjGVxsQ37Tl1MGfXzonA==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-create-class-features-plugin": "^7.17.12",
+        "@babel/helper-plugin-utils": "^7.17.12",
+        "@babel/helper-replace-supers": "^7.16.7",
+        "@babel/helper-split-export-declaration": "^7.16.7",
+        "@babel/plugin-syntax-decorators": "^7.17.12",
+        "charcodes": "^0.2.0"
+      }
+    },
     "@babel/plugin-proposal-dynamic-import": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.16.0.tgz",
-      "integrity": "sha512-QGSA6ExWk95jFQgwz5GQ2Dr95cf7eI7TKutIXXTb7B1gCLTCz5hTjFTQGfLFBBiC5WSNi7udNwWsqbbMh1c4yQ==",
+      "version": "7.16.7",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.16.7.tgz",
+      "integrity": "sha512-I8SW9Ho3/8DRSdmDdH3gORdyUuYnk1m4cMxUAdu5oy4n3OfN8flDEH+d60iG7dUfi0KkYwSvoalHzzdRzpWHTg==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.14.5",
+        "@babel/helper-plugin-utils": "^7.16.7",
         "@babel/plugin-syntax-dynamic-import": "^7.8.3"
       }
     },
+    "@babel/plugin-proposal-export-default-from": {
+      "version": "7.17.12",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.17.12.tgz",
+      "integrity": "sha512-LpsTRw725eBAXXKUOnJJct+SEaOzwR78zahcLuripD2+dKc2Sj+8Q2DzA+GC/jOpOu/KlDXuxrzG214o1zTauQ==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.17.12",
+        "@babel/plugin-syntax-export-default-from": "^7.16.7"
+      }
+    },
     "@babel/plugin-proposal-export-namespace-from": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.16.0.tgz",
-      "integrity": "sha512-CjI4nxM/D+5wCnhD11MHB1AwRSAYeDT+h8gCdcVJZ/OK7+wRzFsf7PFPWVpVpNRkHMmMkQWAHpTq+15IXQ1diA==",
+      "version": "7.17.12",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.17.12.tgz",
+      "integrity": "sha512-j7Ye5EWdwoXOpRmo5QmRyHPsDIe6+u70ZYZrd7uz+ebPYFKfRcLcNu3Ro0vOlJ5zuv8rU7xa+GttNiRzX56snQ==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.14.5",
+        "@babel/helper-plugin-utils": "^7.17.12",
         "@babel/plugin-syntax-export-namespace-from": "^7.8.3"
       }
     },
     "@babel/plugin-proposal-json-strings": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.16.0.tgz",
-      "integrity": "sha512-kouIPuiv8mSi5JkEhzApg5Gn6hFyKPnlkO0a9YSzqRurH8wYzSlf6RJdzluAsbqecdW5pBvDJDfyDIUR/vLxvg==",
+      "version": "7.17.12",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.17.12.tgz",
+      "integrity": "sha512-rKJ+rKBoXwLnIn7n6o6fulViHMrOThz99ybH+hKHcOZbnN14VuMnH9fo2eHE69C8pO4uX1Q7t2HYYIDmv8VYkg==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.14.5",
+        "@babel/helper-plugin-utils": "^7.17.12",
         "@babel/plugin-syntax-json-strings": "^7.8.3"
       }
     },
     "@babel/plugin-proposal-logical-assignment-operators": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.16.0.tgz",
-      "integrity": "sha512-pbW0fE30sVTYXXm9lpVQQ/Vc+iTeQKiXlaNRZPPN2A2VdlWyAtsUrsQ3xydSlDW00TFMK7a8m3cDTkBF5WnV3Q==",
+      "version": "7.17.12",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.17.12.tgz",
+      "integrity": "sha512-EqFo2s1Z5yy+JeJu7SFfbIUtToJTVlC61/C7WLKDntSw4Sz6JNAIfL7zQ74VvirxpjB5kz/kIx0gCcb+5OEo2Q==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.14.5",
+        "@babel/helper-plugin-utils": "^7.17.12",
         "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4"
       }
     },
     "@babel/plugin-proposal-nullish-coalescing-operator": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.16.0.tgz",
-      "integrity": "sha512-3bnHA8CAFm7cG93v8loghDYyQ8r97Qydf63BeYiGgYbjKKB/XP53W15wfRC7dvKfoiJ34f6Rbyyx2btExc8XsQ==",
+      "version": "7.17.12",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.17.12.tgz",
+      "integrity": "sha512-ws/g3FSGVzv+VH86+QvgtuJL/kR67xaEIF2x0iPqdDfYW6ra6JF3lKVBkWynRLcNtIC1oCTfDRVxmm2mKzy+ag==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.14.5",
+        "@babel/helper-plugin-utils": "^7.17.12",
         "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3"
       }
     },
     "@babel/plugin-proposal-numeric-separator": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.16.0.tgz",
-      "integrity": "sha512-FAhE2I6mjispy+vwwd6xWPyEx3NYFS13pikDBWUAFGZvq6POGs5eNchw8+1CYoEgBl9n11I3NkzD7ghn25PQ9Q==",
+      "version": "7.16.7",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.16.7.tgz",
+      "integrity": "sha512-vQgPMknOIgiuVqbokToyXbkY/OmmjAzr/0lhSIbG/KmnzXPGwW/AdhdKpi+O4X/VkWiWjnkKOBiqJrTaC98VKw==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.14.5",
+        "@babel/helper-plugin-utils": "^7.16.7",
         "@babel/plugin-syntax-numeric-separator": "^7.10.4"
       }
     },
     "@babel/plugin-proposal-object-rest-spread": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.16.0.tgz",
-      "integrity": "sha512-LU/+jp89efe5HuWJLmMmFG0+xbz+I2rSI7iLc1AlaeSMDMOGzWlc5yJrMN1d04osXN4sSfpo4O+azkBNBes0jg==",
+      "version": "7.18.0",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.18.0.tgz",
+      "integrity": "sha512-nbTv371eTrFabDfHLElkn9oyf9VG+VKK6WMzhY2o4eHKaG19BToD9947zzGMO6I/Irstx9d8CwX6njPNIAR/yw==",
       "dev": true,
       "requires": {
-        "@babel/compat-data": "^7.16.0",
-        "@babel/helper-compilation-targets": "^7.16.0",
-        "@babel/helper-plugin-utils": "^7.14.5",
+        "@babel/compat-data": "^7.17.10",
+        "@babel/helper-compilation-targets": "^7.17.10",
+        "@babel/helper-plugin-utils": "^7.17.12",
         "@babel/plugin-syntax-object-rest-spread": "^7.8.3",
-        "@babel/plugin-transform-parameters": "^7.16.0"
+        "@babel/plugin-transform-parameters": "^7.17.12"
       }
     },
     "@babel/plugin-proposal-optional-catch-binding": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.16.0.tgz",
-      "integrity": "sha512-kicDo0A/5J0nrsCPbn89mTG3Bm4XgYi0CZtvex9Oyw7gGZE3HXGD0zpQNH+mo+tEfbo8wbmMvJftOwpmPy7aVw==",
+      "version": "7.16.7",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.16.7.tgz",
+      "integrity": "sha512-eMOH/L4OvWSZAE1VkHbr1vckLG1WUcHGJSLqqQwl2GaUqG6QjddvrOaTUMNYiv77H5IKPMZ9U9P7EaHwvAShfA==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.14.5",
+        "@babel/helper-plugin-utils": "^7.16.7",
         "@babel/plugin-syntax-optional-catch-binding": "^7.8.3"
       }
     },
     "@babel/plugin-proposal-optional-chaining": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.16.0.tgz",
-      "integrity": "sha512-Y4rFpkZODfHrVo70Uaj6cC1JJOt3Pp0MdWSwIKtb8z1/lsjl9AmnB7ErRFV+QNGIfcY1Eruc2UMx5KaRnXjMyg==",
+      "version": "7.17.12",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.17.12.tgz",
+      "integrity": "sha512-7wigcOs/Z4YWlK7xxjkvaIw84vGhDv/P1dFGQap0nHkc8gFKY/r+hXc8Qzf5k1gY7CvGIcHqAnOagVKJJ1wVOQ==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.14.5",
+        "@babel/helper-plugin-utils": "^7.17.12",
         "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0",
         "@babel/plugin-syntax-optional-chaining": "^7.8.3"
       }
     },
     "@babel/plugin-proposal-private-methods": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.16.0.tgz",
-      "integrity": "sha512-IvHmcTHDFztQGnn6aWq4t12QaBXTKr1whF/dgp9kz84X6GUcwq9utj7z2wFCUfeOup/QKnOlt2k0zxkGFx9ubg==",
+      "version": "7.17.12",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.17.12.tgz",
+      "integrity": "sha512-SllXoxo19HmxhDWm3luPz+cPhtoTSKLJE9PXshsfrOzBqs60QP0r8OaJItrPhAj0d7mZMnNF0Y1UUggCDgMz1A==",
       "dev": true,
       "requires": {
-        "@babel/helper-create-class-features-plugin": "^7.16.0",
-        "@babel/helper-plugin-utils": "^7.14.5"
+        "@babel/helper-create-class-features-plugin": "^7.17.12",
+        "@babel/helper-plugin-utils": "^7.17.12"
       }
     },
     "@babel/plugin-proposal-private-property-in-object": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.16.0.tgz",
-      "integrity": "sha512-3jQUr/HBbMVZmi72LpjQwlZ55i1queL8KcDTQEkAHihttJnAPrcvG9ZNXIfsd2ugpizZo595egYV6xy+pv4Ofw==",
+      "version": "7.17.12",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.17.12.tgz",
+      "integrity": "sha512-/6BtVi57CJfrtDNKfK5b66ydK2J5pXUKBKSPD2G1whamMuEnZWgoOIfO8Vf9F/DoD4izBLD/Au4NMQfruzzykg==",
       "dev": true,
       "requires": {
-        "@babel/helper-annotate-as-pure": "^7.16.0",
-        "@babel/helper-create-class-features-plugin": "^7.16.0",
-        "@babel/helper-plugin-utils": "^7.14.5",
+        "@babel/helper-annotate-as-pure": "^7.16.7",
+        "@babel/helper-create-class-features-plugin": "^7.17.12",
+        "@babel/helper-plugin-utils": "^7.17.12",
         "@babel/plugin-syntax-private-property-in-object": "^7.14.5"
       },
       "dependencies": {
         "@babel/helper-annotate-as-pure": {
-          "version": "7.16.0",
-          "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.0.tgz",
-          "integrity": "sha512-ItmYF9vR4zA8cByDocY05o0LGUkp1zhbTQOH1NFyl5xXEqlTJQCEJjieriw+aFpxo16swMxUnUiKS7a/r4vtHg==",
+          "version": "7.16.7",
+          "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz",
+          "integrity": "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==",
           "dev": true,
           "requires": {
-            "@babel/types": "^7.16.0"
+            "@babel/types": "^7.16.7"
           }
         }
       }
     },
     "@babel/plugin-proposal-unicode-property-regex": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.16.0.tgz",
-      "integrity": "sha512-ti7IdM54NXv29cA4+bNNKEMS4jLMCbJgl+Drv+FgYy0erJLAxNAIXcNjNjrRZEcWq0xJHsNVwQezskMFpF8N9g==",
+      "version": "7.17.12",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.17.12.tgz",
+      "integrity": "sha512-Wb9qLjXf3ZazqXA7IvI7ozqRIXIGPtSo+L5coFmEkhTQK18ao4UDDD0zdTGAarmbLj2urpRwrc6893cu5Bfh0A==",
       "dev": true,
       "requires": {
-        "@babel/helper-create-regexp-features-plugin": "^7.16.0",
-        "@babel/helper-plugin-utils": "^7.14.5"
+        "@babel/helper-create-regexp-features-plugin": "^7.17.12",
+        "@babel/helper-plugin-utils": "^7.17.12"
       }
     },
     "@babel/plugin-syntax-async-generators": {
@@ -1149,6 +1285,15 @@
         "@babel/helper-plugin-utils": "^7.14.5"
       }
     },
+    "@babel/plugin-syntax-decorators": {
+      "version": "7.17.12",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.17.12.tgz",
+      "integrity": "sha512-D1Hz0qtGTza8K2xGyEdVNCYLdVHukAcbQr4K3/s6r/esadyEriZovpJimQOpu8ju4/jV8dW/1xdaE0UpDroidw==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.17.12"
+      }
+    },
     "@babel/plugin-syntax-dynamic-import": {
       "version": "7.8.3",
       "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz",
@@ -1158,6 +1303,15 @@
         "@babel/helper-plugin-utils": "^7.8.0"
       }
     },
+    "@babel/plugin-syntax-export-default-from": {
+      "version": "7.16.7",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-default-from/-/plugin-syntax-export-default-from-7.16.7.tgz",
+      "integrity": "sha512-4C3E4NsrLOgftKaTYTULhHsuQrGv3FHrBzOMDiS7UYKIpgGBkAdawg4h+EI8zPeK9M0fiIIh72hIwsI24K7MbA==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.16.7"
+      }
+    },
     "@babel/plugin-syntax-export-namespace-from": {
       "version": "7.8.3",
       "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz",
@@ -1167,6 +1321,15 @@
         "@babel/helper-plugin-utils": "^7.8.3"
       }
     },
+    "@babel/plugin-syntax-import-assertions": {
+      "version": "7.17.12",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.17.12.tgz",
+      "integrity": "sha512-n/loy2zkq9ZEM8tEOwON9wTQSTNDTDEz6NujPtJGLU7qObzT1N4c4YZZf8E6ATB2AjNQg/Ib2AIpO03EZaCehw==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.17.12"
+      }
+    },
     "@babel/plugin-syntax-json-strings": {
       "version": "7.8.3",
       "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz",
@@ -1176,6 +1339,15 @@
         "@babel/helper-plugin-utils": "^7.8.0"
       }
     },
+    "@babel/plugin-syntax-jsx": {
+      "version": "7.17.12",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.17.12.tgz",
+      "integrity": "sha512-spyY3E3AURfxh/RHtjx5j6hs8am5NbUBGfcZ2vB3uShSpZdQyXSf5rR5Mk76vbtlAZOelyVQ71Fg0x9SG4fsog==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.17.12"
+      }
+    },
     "@babel/plugin-syntax-logical-assignment-operators": {
       "version": "7.10.4",
       "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz",
@@ -1248,13 +1420,22 @@
         "@babel/helper-plugin-utils": "^7.14.5"
       }
     },
+    "@babel/plugin-syntax-typescript": {
+      "version": "7.17.12",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.17.12.tgz",
+      "integrity": "sha512-TYY0SXFiO31YXtNg3HtFwNJHjLsAyIIhAhNWkQ5whPPS7HWUFlg9z0Ta4qAQNjQbP1wsSt/oKkmZ/4/WWdMUpw==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.17.12"
+      }
+    },
     "@babel/plugin-transform-arrow-functions": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.16.0.tgz",
-      "integrity": "sha512-vIFb5250Rbh7roWARvCLvIJ/PtAU5Lhv7BtZ1u24COwpI9Ypjsh+bZcKk6rlIyalK+r0jOc1XQ8I4ovNxNrWrA==",
+      "version": "7.17.12",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.17.12.tgz",
+      "integrity": "sha512-PHln3CNi/49V+mza4xMwrg+WGYevSF1oaiXaC2EQfdp4HWlSjRsrDXWJiQBKpP7749u6vQ9mcry2uuFOv5CXvA==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.14.5"
+        "@babel/helper-plugin-utils": "^7.17.12"
       }
     },
     "@babel/plugin-transform-async-to-generator": {
@@ -1269,241 +1450,308 @@
       }
     },
     "@babel/plugin-transform-block-scoped-functions": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.0.tgz",
-      "integrity": "sha512-V14As3haUOP4ZWrLJ3VVx5rCnrYhMSHN/jX7z6FAt5hjRkLsb0snPCmJwSOML5oxkKO4FNoNv7V5hw/y2bjuvg==",
+      "version": "7.16.7",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.7.tgz",
+      "integrity": "sha512-JUuzlzmF40Z9cXyytcbZEZKckgrQzChbQJw/5PuEHYeqzCsvebDx0K0jWnIIVcmmDOAVctCgnYs0pMcrYj2zJg==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.14.5"
+        "@babel/helper-plugin-utils": "^7.16.7"
       }
     },
     "@babel/plugin-transform-block-scoping": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.16.0.tgz",
-      "integrity": "sha512-27n3l67/R3UrXfizlvHGuTwsRIFyce3D/6a37GRxn28iyTPvNXaW4XvznexRh1zUNLPjbLL22Id0XQElV94ruw==",
+      "version": "7.17.12",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.17.12.tgz",
+      "integrity": "sha512-jw8XW/B1i7Lqwqj2CbrViPcZijSxfguBWZP2aN59NHgxUyO/OcO1mfdCxH13QhN5LbWhPkX+f+brKGhZTiqtZQ==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.14.5"
+        "@babel/helper-plugin-utils": "^7.17.12"
       }
     },
     "@babel/plugin-transform-classes": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.16.0.tgz",
-      "integrity": "sha512-HUxMvy6GtAdd+GKBNYDWCIA776byUQH8zjnfjxwT1P1ARv/wFu8eBDpmXQcLS/IwRtrxIReGiplOwMeyO7nsDQ==",
-      "dev": true,
-      "requires": {
-        "@babel/helper-annotate-as-pure": "^7.16.0",
-        "@babel/helper-function-name": "^7.16.0",
-        "@babel/helper-optimise-call-expression": "^7.16.0",
-        "@babel/helper-plugin-utils": "^7.14.5",
-        "@babel/helper-replace-supers": "^7.16.0",
-        "@babel/helper-split-export-declaration": "^7.16.0",
+      "version": "7.17.12",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.17.12.tgz",
+      "integrity": "sha512-cvO7lc7pZat6BsvH6l/EGaI8zpl8paICaoGk+7x7guvtfak/TbIf66nYmJOH13EuG0H+Xx3M+9LQDtSvZFKXKw==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-annotate-as-pure": "^7.16.7",
+        "@babel/helper-environment-visitor": "^7.16.7",
+        "@babel/helper-function-name": "^7.17.9",
+        "@babel/helper-optimise-call-expression": "^7.16.7",
+        "@babel/helper-plugin-utils": "^7.17.12",
+        "@babel/helper-replace-supers": "^7.16.7",
+        "@babel/helper-split-export-declaration": "^7.16.7",
         "globals": "^11.1.0"
       },
       "dependencies": {
         "@babel/helper-annotate-as-pure": {
-          "version": "7.16.0",
-          "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.0.tgz",
-          "integrity": "sha512-ItmYF9vR4zA8cByDocY05o0LGUkp1zhbTQOH1NFyl5xXEqlTJQCEJjieriw+aFpxo16swMxUnUiKS7a/r4vtHg==",
+          "version": "7.16.7",
+          "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz",
+          "integrity": "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==",
           "dev": true,
           "requires": {
-            "@babel/types": "^7.16.0"
+            "@babel/types": "^7.16.7"
           }
         }
       }
     },
     "@babel/plugin-transform-computed-properties": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.16.0.tgz",
-      "integrity": "sha512-63l1dRXday6S8V3WFY5mXJwcRAnPYxvFfTlt67bwV1rTyVTM5zrp0DBBb13Kl7+ehkCVwIZPumPpFP/4u70+Tw==",
+      "version": "7.17.12",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.17.12.tgz",
+      "integrity": "sha512-a7XINeplB5cQUWMg1E/GI1tFz3LfK021IjV1rj1ypE+R7jHm+pIHmHl25VNkZxtx9uuYp7ThGk8fur1HHG7PgQ==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.14.5"
+        "@babel/helper-plugin-utils": "^7.17.12"
       }
     },
     "@babel/plugin-transform-destructuring": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.16.0.tgz",
-      "integrity": "sha512-Q7tBUwjxLTsHEoqktemHBMtb3NYwyJPTJdM+wDwb0g8PZ3kQUIzNvwD5lPaqW/p54TXBc/MXZu9Jr7tbUEUM8Q==",
+      "version": "7.18.0",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.18.0.tgz",
+      "integrity": "sha512-Mo69klS79z6KEfrLg/1WkmVnB8javh75HX4pi2btjvlIoasuxilEyjtsQW6XPrubNd7AQy0MMaNIaQE4e7+PQw==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.14.5"
+        "@babel/helper-plugin-utils": "^7.17.12"
       }
     },
     "@babel/plugin-transform-dotall-regex": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.16.0.tgz",
-      "integrity": "sha512-FXlDZfQeLILfJlC6I1qyEwcHK5UpRCFkaoVyA1nk9A1L1Yu583YO4un2KsLBsu3IJb4CUbctZks8tD9xPQubLw==",
+      "version": "7.16.7",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.16.7.tgz",
+      "integrity": "sha512-Lyttaao2SjZF6Pf4vk1dVKv8YypMpomAbygW+mU5cYP3S5cWTfCJjG8xV6CFdzGFlfWK81IjL9viiTvpb6G7gQ==",
       "dev": true,
       "requires": {
-        "@babel/helper-create-regexp-features-plugin": "^7.16.0",
-        "@babel/helper-plugin-utils": "^7.14.5"
+        "@babel/helper-create-regexp-features-plugin": "^7.16.7",
+        "@babel/helper-plugin-utils": "^7.16.7"
       }
     },
     "@babel/plugin-transform-duplicate-keys": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.16.0.tgz",
-      "integrity": "sha512-LIe2kcHKAZOJDNxujvmp6z3mfN6V9lJxubU4fJIGoQCkKe3Ec2OcbdlYP+vW++4MpxwG0d1wSDOJtQW5kLnkZQ==",
+      "version": "7.17.12",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.17.12.tgz",
+      "integrity": "sha512-EA5eYFUG6xeerdabina/xIoB95jJ17mAkR8ivx6ZSu9frKShBjpOGZPn511MTDTkiCO+zXnzNczvUM69YSf3Zw==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.14.5"
+        "@babel/helper-plugin-utils": "^7.17.12"
       }
     },
     "@babel/plugin-transform-exponentiation-operator": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.16.0.tgz",
-      "integrity": "sha512-OwYEvzFI38hXklsrbNivzpO3fh87skzx8Pnqi4LoSYeav0xHlueSoCJrSgTPfnbyzopo5b3YVAJkFIcUpK2wsw==",
+      "version": "7.16.7",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.16.7.tgz",
+      "integrity": "sha512-8UYLSlyLgRixQvlYH3J2ekXFHDFLQutdy7FfFAMm3CPZ6q9wHCwnUyiXpQCe3gVVnQlHc5nsuiEVziteRNTXEA==",
       "dev": true,
       "requires": {
-        "@babel/helper-builder-binary-assignment-operator-visitor": "^7.16.0",
-        "@babel/helper-plugin-utils": "^7.14.5"
+        "@babel/helper-builder-binary-assignment-operator-visitor": "^7.16.7",
+        "@babel/helper-plugin-utils": "^7.16.7"
       }
     },
     "@babel/plugin-transform-for-of": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.16.0.tgz",
-      "integrity": "sha512-5QKUw2kO+GVmKr2wMYSATCTTnHyscl6sxFRAY+rvN7h7WB0lcG0o4NoV6ZQU32OZGVsYUsfLGgPQpDFdkfjlJQ==",
+      "version": "7.18.1",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.1.tgz",
+      "integrity": "sha512-+TTB5XwvJ5hZbO8xvl2H4XaMDOAK57zF4miuC9qQJgysPNEAZZ9Z69rdF5LJkozGdZrjBIUAIyKUWRMmebI7vg==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.14.5"
+        "@babel/helper-plugin-utils": "^7.17.12"
       }
     },
     "@babel/plugin-transform-function-name": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.0.tgz",
-      "integrity": "sha512-lBzMle9jcOXtSOXUpc7tvvTpENu/NuekNJVova5lCCWCV9/U1ho2HH2y0p6mBg8fPm/syEAbfaaemYGOHCY3mg==",
+      "version": "7.16.7",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.7.tgz",
+      "integrity": "sha512-SU/C68YVwTRxqWj5kgsbKINakGag0KTgq9f2iZEXdStoAbOzLHEBRYzImmA6yFo8YZhJVflvXmIHUO7GWHmxxA==",
       "dev": true,
       "requires": {
-        "@babel/helper-function-name": "^7.16.0",
-        "@babel/helper-plugin-utils": "^7.14.5"
+        "@babel/helper-compilation-targets": "^7.16.7",
+        "@babel/helper-function-name": "^7.16.7",
+        "@babel/helper-plugin-utils": "^7.16.7"
       }
     },
     "@babel/plugin-transform-literals": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.16.0.tgz",
-      "integrity": "sha512-gQDlsSF1iv9RU04clgXqRjrPyyoJMTclFt3K1cjLmTKikc0s/6vE3hlDeEVC71wLTRu72Fq7650kABrdTc2wMQ==",
+      "version": "7.17.12",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.17.12.tgz",
+      "integrity": "sha512-8iRkvaTjJciWycPIZ9k9duu663FT7VrBdNqNgxnVXEFwOIp55JWcZd23VBRySYbnS3PwQ3rGiabJBBBGj5APmQ==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.14.5"
+        "@babel/helper-plugin-utils": "^7.17.12"
       }
     },
     "@babel/plugin-transform-member-expression-literals": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.0.tgz",
-      "integrity": "sha512-WRpw5HL4Jhnxw8QARzRvwojp9MIE7Tdk3ez6vRyUk1MwgjJN0aNpRoXainLR5SgxmoXx/vsXGZ6OthP6t/RbUg==",
+      "version": "7.16.7",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.7.tgz",
+      "integrity": "sha512-mBruRMbktKQwbxaJof32LT9KLy2f3gH+27a5XSuXo6h7R3vqltl0PgZ80C8ZMKw98Bf8bqt6BEVi3svOh2PzMw==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.14.5"
+        "@babel/helper-plugin-utils": "^7.16.7"
       }
     },
     "@babel/plugin-transform-modules-amd": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.16.0.tgz",
-      "integrity": "sha512-rWFhWbCJ9Wdmzln1NmSCqn7P0RAD+ogXG/bd9Kg5c7PKWkJtkiXmYsMBeXjDlzHpVTJ4I/hnjs45zX4dEv81xw==",
+      "version": "7.18.0",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.18.0.tgz",
+      "integrity": "sha512-h8FjOlYmdZwl7Xm2Ug4iX2j7Qy63NANI+NQVWQzv6r25fqgg7k2dZl03p95kvqNclglHs4FZ+isv4p1uXMA+QA==",
       "dev": true,
       "requires": {
-        "@babel/helper-module-transforms": "^7.16.0",
-        "@babel/helper-plugin-utils": "^7.14.5",
+        "@babel/helper-module-transforms": "^7.18.0",
+        "@babel/helper-plugin-utils": "^7.17.12",
         "babel-plugin-dynamic-import-node": "^2.3.3"
       }
     },
     "@babel/plugin-transform-modules-commonjs": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.16.0.tgz",
-      "integrity": "sha512-Dzi+NWqyEotgzk/sb7kgQPJQf7AJkQBWsVp1N6JWc1lBVo0vkElUnGdr1PzUBmfsCCN5OOFya3RtpeHk15oLKQ==",
+      "version": "7.18.0",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.18.0.tgz",
+      "integrity": "sha512-cCeR0VZWtfxWS4YueAK2qtHtBPJRSaJcMlbS8jhSIm/A3E2Kpro4W1Dn4cqJtp59dtWfXjQwK7SPKF8ghs7rlw==",
       "dev": true,
       "requires": {
-        "@babel/helper-module-transforms": "^7.16.0",
-        "@babel/helper-plugin-utils": "^7.14.5",
-        "@babel/helper-simple-access": "^7.16.0",
+        "@babel/helper-module-transforms": "^7.18.0",
+        "@babel/helper-plugin-utils": "^7.17.12",
+        "@babel/helper-simple-access": "^7.17.7",
         "babel-plugin-dynamic-import-node": "^2.3.3"
       }
     },
     "@babel/plugin-transform-modules-systemjs": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.16.0.tgz",
-      "integrity": "sha512-yuGBaHS3lF1m/5R+6fjIke64ii5luRUg97N2wr+z1sF0V+sNSXPxXDdEEL/iYLszsN5VKxVB1IPfEqhzVpiqvg==",
+      "version": "7.18.0",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.18.0.tgz",
+      "integrity": "sha512-vwKpxdHnlM5tIrRt/eA0bzfbi7gUBLN08vLu38np1nZevlPySRe6yvuATJB5F/WPJ+ur4OXwpVYq9+BsxqAQuQ==",
       "dev": true,
       "requires": {
-        "@babel/helper-hoist-variables": "^7.16.0",
-        "@babel/helper-module-transforms": "^7.16.0",
-        "@babel/helper-plugin-utils": "^7.14.5",
-        "@babel/helper-validator-identifier": "^7.15.7",
+        "@babel/helper-hoist-variables": "^7.16.7",
+        "@babel/helper-module-transforms": "^7.18.0",
+        "@babel/helper-plugin-utils": "^7.17.12",
+        "@babel/helper-validator-identifier": "^7.16.7",
         "babel-plugin-dynamic-import-node": "^2.3.3"
       }
     },
     "@babel/plugin-transform-modules-umd": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.16.0.tgz",
-      "integrity": "sha512-nx4f6no57himWiHhxDM5pjwhae5vLpTK2zCnDH8+wNLJy0TVER/LJRHl2bkt6w9Aad2sPD5iNNoUpY3X9sTGDg==",
+      "version": "7.18.0",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.0.tgz",
+      "integrity": "sha512-d/zZ8I3BWli1tmROLxXLc9A6YXvGK8egMxHp+E/rRwMh1Kip0AP77VwZae3snEJ33iiWwvNv2+UIIhfalqhzZA==",
       "dev": true,
       "requires": {
-        "@babel/helper-module-transforms": "^7.16.0",
-        "@babel/helper-plugin-utils": "^7.14.5"
+        "@babel/helper-module-transforms": "^7.18.0",
+        "@babel/helper-plugin-utils": "^7.17.12"
       }
     },
     "@babel/plugin-transform-named-capturing-groups-regex": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.16.0.tgz",
-      "integrity": "sha512-LogN88uO+7EhxWc8WZuQ8vxdSyVGxhkh8WTC3tzlT8LccMuQdA81e9SGV6zY7kY2LjDhhDOFdQVxdGwPyBCnvg==",
+      "version": "7.17.12",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.17.12.tgz",
+      "integrity": "sha512-vWoWFM5CKaTeHrdUJ/3SIOTRV+MBVGybOC9mhJkaprGNt5demMymDW24yC74avb915/mIRe3TgNb/d8idvnCRA==",
       "dev": true,
       "requires": {
-        "@babel/helper-create-regexp-features-plugin": "^7.16.0"
+        "@babel/helper-create-regexp-features-plugin": "^7.17.12",
+        "@babel/helper-plugin-utils": "^7.17.12"
       }
     },
     "@babel/plugin-transform-new-target": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.16.0.tgz",
-      "integrity": "sha512-fhjrDEYv2DBsGN/P6rlqakwRwIp7rBGLPbrKxwh7oVt5NNkIhZVOY2GRV+ULLsQri1bDqwDWnU3vhlmx5B2aCw==",
+      "version": "7.17.12",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.17.12.tgz",
+      "integrity": "sha512-CaOtzk2fDYisbjAD4Sd1MTKGVIpRtx9bWLyj24Y/k6p4s4gQ3CqDGJauFJxt8M/LEx003d0i3klVqnN73qvK3w==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.14.5"
+        "@babel/helper-plugin-utils": "^7.17.12"
       }
     },
     "@babel/plugin-transform-object-super": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.0.tgz",
-      "integrity": "sha512-fds+puedQHn4cPLshoHcR1DTMN0q1V9ou0mUjm8whx9pGcNvDrVVrgw+KJzzCaiTdaYhldtrUps8DWVMgrSEyg==",
+      "version": "7.16.7",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.7.tgz",
+      "integrity": "sha512-14J1feiQVWaGvRxj2WjyMuXS2jsBkgB3MdSN5HuC2G5nRspa5RK9COcs82Pwy5BuGcjb+fYaUj94mYcOj7rCvw==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.14.5",
-        "@babel/helper-replace-supers": "^7.16.0"
+        "@babel/helper-plugin-utils": "^7.16.7",
+        "@babel/helper-replace-supers": "^7.16.7"
       }
     },
     "@babel/plugin-transform-parameters": {
-      "version": "7.16.3",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.16.3.tgz",
-      "integrity": "sha512-3MaDpJrOXT1MZ/WCmkOFo7EtmVVC8H4EUZVrHvFOsmwkk4lOjQj8rzv8JKUZV4YoQKeoIgk07GO+acPU9IMu/w==",
+      "version": "7.17.12",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.17.12.tgz",
+      "integrity": "sha512-6qW4rWo1cyCdq1FkYri7AHpauchbGLXpdwnYsfxFb+KtddHENfsY5JZb35xUwkK5opOLcJ3BNd2l7PhRYGlwIA==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.14.5"
+        "@babel/helper-plugin-utils": "^7.17.12"
       }
     },
     "@babel/plugin-transform-property-literals": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.0.tgz",
-      "integrity": "sha512-XLldD4V8+pOqX2hwfWhgwXzGdnDOThxaNTgqagOcpBgIxbUvpgU2FMvo5E1RyHbk756WYgdbS0T8y0Cj9FKkWQ==",
+      "version": "7.16.7",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.7.tgz",
+      "integrity": "sha512-z4FGr9NMGdoIl1RqavCqGG+ZuYjfZ/hkCIeuH6Do7tXmSm0ls11nYVSJqFEUOSJbDab5wC6lRE/w6YjVcr6Hqw==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.14.5"
+        "@babel/helper-plugin-utils": "^7.16.7"
+      }
+    },
+    "@babel/plugin-transform-react-display-name": {
+      "version": "7.16.7",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.16.7.tgz",
+      "integrity": "sha512-qgIg8BcZgd0G/Cz916D5+9kqX0c7nPZyXaP8R2tLNN5tkyIZdG5fEwBrxwplzSnjC1jvQmyMNVwUCZPcbGY7Pg==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.16.7"
+      }
+    },
+    "@babel/plugin-transform-react-jsx": {
+      "version": "7.17.12",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.17.12.tgz",
+      "integrity": "sha512-Lcaw8bxd1DKht3thfD4A12dqo1X16he1Lm8rIv8sTwjAYNInRS1qHa9aJoqvzpscItXvftKDCfaEQzwoVyXpEQ==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-annotate-as-pure": "^7.16.7",
+        "@babel/helper-module-imports": "^7.16.7",
+        "@babel/helper-plugin-utils": "^7.17.12",
+        "@babel/plugin-syntax-jsx": "^7.17.12",
+        "@babel/types": "^7.17.12"
+      },
+      "dependencies": {
+        "@babel/helper-annotate-as-pure": {
+          "version": "7.16.7",
+          "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz",
+          "integrity": "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==",
+          "dev": true,
+          "requires": {
+            "@babel/types": "^7.16.7"
+          }
+        }
+      }
+    },
+    "@babel/plugin-transform-react-jsx-development": {
+      "version": "7.16.7",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.16.7.tgz",
+      "integrity": "sha512-RMvQWvpla+xy6MlBpPlrKZCMRs2AGiHOGHY3xRwl0pEeim348dDyxeH4xBsMPbIMhujeq7ihE702eM2Ew0Wo+A==",
+      "dev": true,
+      "requires": {
+        "@babel/plugin-transform-react-jsx": "^7.16.7"
+      }
+    },
+    "@babel/plugin-transform-react-pure-annotations": {
+      "version": "7.18.0",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.18.0.tgz",
+      "integrity": "sha512-6+0IK6ouvqDn9bmEG7mEyF/pwlJXVj5lwydybpyyH3D0A7Hftk+NCTdYjnLNZksn261xaOV5ksmp20pQEmc2RQ==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-annotate-as-pure": "^7.16.7",
+        "@babel/helper-plugin-utils": "^7.17.12"
+      },
+      "dependencies": {
+        "@babel/helper-annotate-as-pure": {
+          "version": "7.16.7",
+          "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz",
+          "integrity": "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==",
+          "dev": true,
+          "requires": {
+            "@babel/types": "^7.16.7"
+          }
+        }
       }
     },
     "@babel/plugin-transform-regenerator": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.16.0.tgz",
-      "integrity": "sha512-JAvGxgKuwS2PihiSFaDrp94XOzzTUeDeOQlcKzVAyaPap7BnZXK/lvMDiubkPTdotPKOIZq9xWXWnggUMYiExg==",
+      "version": "7.18.0",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.18.0.tgz",
+      "integrity": "sha512-C8YdRw9uzx25HSIzwA7EM7YP0FhCe5wNvJbZzjVNHHPGVcDJ3Aie+qGYYdS1oVQgn+B3eAIJbWFLrJ4Jipv7nw==",
       "dev": true,
       "requires": {
-        "regenerator-transform": "^0.14.2"
+        "@babel/helper-plugin-utils": "^7.17.12",
+        "regenerator-transform": "^0.15.0"
       }
     },
     "@babel/plugin-transform-reserved-words": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.16.0.tgz",
-      "integrity": "sha512-Dgs8NNCehHSvXdhEhln8u/TtJxfVwGYCgP2OOr5Z3Ar+B+zXicEOKNTyc+eca2cuEOMtjW6m9P9ijOt8QdqWkg==",
+      "version": "7.17.12",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.17.12.tgz",
+      "integrity": "sha512-1KYqwbJV3Co03NIi14uEHW8P50Md6KqFgt0FfpHdK6oyAHQVTosgPuPSiWud1HX0oYJ1hGRRlk0fP87jFpqXZA==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.14.5"
+        "@babel/helper-plugin-utils": "^7.17.12"
       }
     },
     "@babel/plugin-transform-runtime": {
@@ -1529,68 +1777,79 @@
       }
     },
     "@babel/plugin-transform-shorthand-properties": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.0.tgz",
-      "integrity": "sha512-iVb1mTcD8fuhSv3k99+5tlXu5N0v8/DPm2mO3WACLG6al1CGZH7v09HJyUb1TtYl/Z+KrM6pHSIJdZxP5A+xow==",
+      "version": "7.16.7",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.7.tgz",
+      "integrity": "sha512-hah2+FEnoRoATdIb05IOXf+4GzXYTq75TVhIn1PewihbpyrNWUt2JbudKQOETWw6QpLe+AIUpJ5MVLYTQbeeUg==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.14.5"
+        "@babel/helper-plugin-utils": "^7.16.7"
       }
     },
     "@babel/plugin-transform-spread": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.16.0.tgz",
-      "integrity": "sha512-Ao4MSYRaLAQczZVp9/7E7QHsCuK92yHRrmVNRe/SlEJjhzivq0BSn8mEraimL8wizHZ3fuaHxKH0iwzI13GyGg==",
+      "version": "7.17.12",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.17.12.tgz",
+      "integrity": "sha512-9pgmuQAtFi3lpNUstvG9nGfk9DkrdmWNp9KeKPFmuZCpEnxRzYlS8JgwPjYj+1AWDOSvoGN0H30p1cBOmT/Svg==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.14.5",
+        "@babel/helper-plugin-utils": "^7.17.12",
         "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0"
       }
     },
     "@babel/plugin-transform-sticky-regex": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.16.0.tgz",
-      "integrity": "sha512-/ntT2NljR9foobKk4E/YyOSwcGUXtYWv5tinMK/3RkypyNBNdhHUaq6Orw5DWq9ZcNlS03BIlEALFeQgeVAo4Q==",
+      "version": "7.16.7",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.16.7.tgz",
+      "integrity": "sha512-NJa0Bd/87QV5NZZzTuZG5BPJjLYadeSZ9fO6oOUoL4iQx+9EEuw/eEM92SrsT19Yc2jgB1u1hsjqDtH02c3Drw==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.14.5"
+        "@babel/helper-plugin-utils": "^7.16.7"
       }
     },
     "@babel/plugin-transform-template-literals": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.16.0.tgz",
-      "integrity": "sha512-Rd4Ic89hA/f7xUSJQk5PnC+4so50vBoBfxjdQAdvngwidM8jYIBVxBZ/sARxD4e0yMXRbJVDrYf7dyRtIIKT6Q==",
+      "version": "7.17.12",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.17.12.tgz",
+      "integrity": "sha512-kAKJ7DX1dSRa2s7WN1xUAuaQmkTpN+uig4wCKWivVXIObqGbVTUlSavHyfI2iZvz89GFAMGm9p2DBJ4Y1Tp0hw==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.14.5"
+        "@babel/helper-plugin-utils": "^7.17.12"
       }
     },
     "@babel/plugin-transform-typeof-symbol": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.16.0.tgz",
-      "integrity": "sha512-++V2L8Bdf4vcaHi2raILnptTBjGEFxn5315YU+e8+EqXIucA+q349qWngCLpUYqqv233suJ6NOienIVUpS9cqg==",
+      "version": "7.17.12",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.17.12.tgz",
+      "integrity": "sha512-Q8y+Jp7ZdtSPXCThB6zjQ74N3lj0f6TDh1Hnf5B+sYlzQ8i5Pjp8gW0My79iekSpT4WnI06blqP6DT0OmaXXmw==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.14.5"
+        "@babel/helper-plugin-utils": "^7.17.12"
+      }
+    },
+    "@babel/plugin-transform-typescript": {
+      "version": "7.18.1",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.18.1.tgz",
+      "integrity": "sha512-F+RJmL479HJmC0KeqqwEGZMg1P7kWArLGbAKfEi9yPthJyMNjF+DjxFF/halfQvq1Q9GFM4TUbYDNV8xe4Ctqg==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-create-class-features-plugin": "^7.18.0",
+        "@babel/helper-plugin-utils": "^7.17.12",
+        "@babel/plugin-syntax-typescript": "^7.17.12"
       }
     },
     "@babel/plugin-transform-unicode-escapes": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.16.0.tgz",
-      "integrity": "sha512-VFi4dhgJM7Bpk8lRc5CMaRGlKZ29W9C3geZjt9beuzSUrlJxsNwX7ReLwaL6WEvsOf2EQkyIJEPtF8EXjB/g2A==",
+      "version": "7.16.7",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.16.7.tgz",
+      "integrity": "sha512-TAV5IGahIz3yZ9/Hfv35TV2xEm+kaBDaZQCn2S/hG9/CZ0DktxJv9eKfPc7yYCvOYR4JGx1h8C+jcSOvgaaI/Q==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.14.5"
+        "@babel/helper-plugin-utils": "^7.16.7"
       }
     },
     "@babel/plugin-transform-unicode-regex": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.16.0.tgz",
-      "integrity": "sha512-jHLK4LxhHjvCeZDWyA9c+P9XH1sOxRd1RO9xMtDVRAOND/PczPqizEtVdx4TQF/wyPaewqpT+tgQFYMnN/P94A==",
+      "version": "7.16.7",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.16.7.tgz",
+      "integrity": "sha512-oC5tYYKw56HO75KZVLQ+R/Nl3Hro9kf8iG0hXoaHP7tjAyCpvqBiSNe6vGrZni1Z6MggmUOC6A7VP7AVmw225Q==",
       "dev": true,
       "requires": {
-        "@babel/helper-create-regexp-features-plugin": "^7.16.0",
-        "@babel/helper-plugin-utils": "^7.14.5"
+        "@babel/helper-create-regexp-features-plugin": "^7.16.7",
+        "@babel/helper-plugin-utils": "^7.16.7"
       }
     },
     "@babel/preset-env": {
@@ -1695,6 +1954,116 @@
         "esutils": "^2.0.2"
       }
     },
+    "@babel/preset-react": {
+      "version": "7.17.12",
+      "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.17.12.tgz",
+      "integrity": "sha512-h5U+rwreXtZaRBEQhW1hOJLMq8XNJBQ/9oymXiCXTuT/0uOwpbT0gUt+sXeOqoXBgNuUKI7TaObVwoEyWkpFgA==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.17.12",
+        "@babel/helper-validator-option": "^7.16.7",
+        "@babel/plugin-transform-react-display-name": "^7.16.7",
+        "@babel/plugin-transform-react-jsx": "^7.17.12",
+        "@babel/plugin-transform-react-jsx-development": "^7.16.7",
+        "@babel/plugin-transform-react-pure-annotations": "^7.16.7"
+      }
+    },
+    "@babel/preset-typescript": {
+      "version": "7.17.12",
+      "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.17.12.tgz",
+      "integrity": "sha512-S1ViF8W2QwAKUGJXxP9NAfNaqGDdEBJKpYkxHf5Yy2C4NPPzXGeR3Lhk7G8xJaaLcFTRfNjVbtbVtm8Gb0mqvg==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.17.12",
+        "@babel/helper-validator-option": "^7.16.7",
+        "@babel/plugin-transform-typescript": "^7.17.12"
+      }
+    },
+    "@babel/register": {
+      "version": "7.17.7",
+      "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.17.7.tgz",
+      "integrity": "sha512-fg56SwvXRifootQEDQAu1mKdjh5uthPzdO0N6t358FktfL4XjAVXuH58ULoiW8mesxiOgNIrxiImqEwv0+hRRA==",
+      "dev": true,
+      "requires": {
+        "clone-deep": "^4.0.1",
+        "find-cache-dir": "^2.0.0",
+        "make-dir": "^2.1.0",
+        "pirates": "^4.0.5",
+        "source-map-support": "^0.5.16"
+      },
+      "dependencies": {
+        "find-cache-dir": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz",
+          "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==",
+          "dev": true,
+          "requires": {
+            "commondir": "^1.0.1",
+            "make-dir": "^2.0.0",
+            "pkg-dir": "^3.0.0"
+          }
+        },
+        "find-up": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
+          "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+          "dev": true,
+          "requires": {
+            "locate-path": "^3.0.0"
+          }
+        },
+        "locate-path": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
+          "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+          "dev": true,
+          "requires": {
+            "p-locate": "^3.0.0",
+            "path-exists": "^3.0.0"
+          }
+        },
+        "make-dir": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
+          "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
+          "dev": true,
+          "requires": {
+            "pify": "^4.0.1",
+            "semver": "^5.6.0"
+          }
+        },
+        "p-locate": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
+          "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+          "dev": true,
+          "requires": {
+            "p-limit": "^2.0.0"
+          }
+        },
+        "path-exists": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+          "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
+          "dev": true
+        },
+        "pkg-dir": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz",
+          "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==",
+          "dev": true,
+          "requires": {
+            "find-up": "^3.0.0"
+          }
+        },
+        "semver": {
+          "version": "5.7.1",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+          "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+          "dev": true
+        }
+      }
+    },
     "@babel/runtime": {
       "version": "7.14.8",
       "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.8.tgz",
@@ -1716,1003 +2085,1750 @@
       }
     },
     "@babel/traverse": {
-      "version": "7.16.3",
-      "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.3.tgz",
-      "integrity": "sha512-eolumr1vVMjqevCpwVO99yN/LoGL0EyHiLO5I043aYQvwOJ9eR5UsZSClHVCzfhBduMAsSzgA/6AyqPjNayJag==",
-      "dev": true,
-      "requires": {
-        "@babel/code-frame": "^7.16.0",
-        "@babel/generator": "^7.16.0",
-        "@babel/helper-function-name": "^7.16.0",
-        "@babel/helper-hoist-variables": "^7.16.0",
-        "@babel/helper-split-export-declaration": "^7.16.0",
-        "@babel/parser": "^7.16.3",
-        "@babel/types": "^7.16.0",
+      "version": "7.18.0",
+      "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.0.tgz",
+      "integrity": "sha512-oNOO4vaoIQoGjDQ84LgtF/IAlxlyqL4TUuoQ7xLkQETFaHkY1F7yazhB4Kt3VcZGL0ZF/jhrEpnXqUb0M7V3sw==",
+      "dev": true,
+      "requires": {
+        "@babel/code-frame": "^7.16.7",
+        "@babel/generator": "^7.18.0",
+        "@babel/helper-environment-visitor": "^7.16.7",
+        "@babel/helper-function-name": "^7.17.9",
+        "@babel/helper-hoist-variables": "^7.16.7",
+        "@babel/helper-split-export-declaration": "^7.16.7",
+        "@babel/parser": "^7.18.0",
+        "@babel/types": "^7.18.0",
         "debug": "^4.1.0",
         "globals": "^11.1.0"
       },
       "dependencies": {
         "@babel/generator": {
-          "version": "7.16.0",
-          "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.0.tgz",
-          "integrity": "sha512-RR8hUCfRQn9j9RPKEVXo9LiwoxLPYn6hNZlvUOR8tSnaxlD0p0+la00ZP9/SnRt6HchKr+X0fO2r8vrETiJGew==",
+          "version": "7.18.0",
+          "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.0.tgz",
+          "integrity": "sha512-81YO9gGx6voPXlvYdZBliFXAZU8vZ9AZ6z+CjlmcnaeOcYSFbMTpdeDUO9xD9dh/68Vq03I8ZspfUTPfitcDHg==",
           "dev": true,
           "requires": {
-            "@babel/types": "^7.16.0",
-            "jsesc": "^2.5.1",
-            "source-map": "^0.5.0"
+            "@babel/types": "^7.18.0",
+            "@jridgewell/gen-mapping": "^0.3.0",
+            "jsesc": "^2.5.1"
           }
-        },
-        "source-map": {
-          "version": "0.5.7",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
-          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
-          "dev": true
         }
       }
     },
     "@babel/types": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.0.tgz",
-      "integrity": "sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg==",
+      "version": "7.18.0",
+      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.0.tgz",
+      "integrity": "sha512-vhAmLPAiC8j9K2GnsnLPCIH5wCrPpYIVBCWRBFDCB7Y/BXLqi/O+1RSTTM2bsmg6U/551+FCf9PNPxjABmxHTw==",
       "dev": true,
       "requires": {
-        "@babel/helper-validator-identifier": "^7.15.7",
+        "@babel/helper-validator-identifier": "^7.16.7",
         "to-fast-properties": "^2.0.0"
       }
     },
-    "@csstools/convert-colors": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/@csstools/convert-colors/-/convert-colors-1.4.0.tgz",
-      "integrity": "sha512-5a6wqoJV/xEdbRNKVo6I4hO3VjyDq//8q2f9I6PBAvMesJHFauXDorcNCsr9RzvsZnaWi5NYCcfyqP1QeFHFbw==",
-      "dev": true
+    "@cnakazawa/watch": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.4.tgz",
+      "integrity": "sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ==",
+      "dev": true,
+      "requires": {
+        "exec-sh": "^0.3.2",
+        "minimist": "^1.2.0"
+      }
     },
-    "@discoveryjs/json-ext": {
-      "version": "0.5.3",
-      "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.3.tgz",
-      "integrity": "sha512-Fxt+AfXgjMoin2maPIYzFZnQjAXjAL0PHscM5pRTtatFqB+vZxAM9tLp2Optnuw3QOQC40jTNeGYFOMvyf7v9g==",
+    "@colors/colors": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz",
+      "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==",
       "dev": true
     },
-    "@eslint/eslintrc": {
-      "version": "0.4.3",
-      "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz",
-      "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==",
+    "@compodoc/compodoc": {
+      "version": "1.1.19",
+      "resolved": "https://registry.npmjs.org/@compodoc/compodoc/-/compodoc-1.1.19.tgz",
+      "integrity": "sha512-09vdSIgoAXWD1MiLZNhiljLNQ1XzHw/w5shw5IPcUImr/I+1Y52srUL46mEXN8AXo0hbHb5LZcgs70mmrOvY7Q==",
       "dev": true,
       "requires": {
-        "ajv": "^6.12.4",
-        "debug": "^4.1.1",
-        "espree": "^7.3.0",
-        "globals": "^13.9.0",
-        "ignore": "^4.0.6",
-        "import-fresh": "^3.2.1",
-        "js-yaml": "^3.13.1",
-        "minimatch": "^3.0.4",
-        "strip-json-comments": "^3.1.1"
+        "@angular-devkit/schematics": "^13.2.4",
+        "@babel/core": "^7.17.5",
+        "@babel/preset-env": "^7.16.11",
+        "@compodoc/live-server": "^1.2.3",
+        "@compodoc/ngd-transformer": "^2.1.0",
+        "chalk": "4.1.2",
+        "cheerio": "^1.0.0-rc.10",
+        "chokidar": "^3.5.3",
+        "colors": "1.4.0",
+        "commander": "^9.0.0",
+        "cosmiconfig": "^7.0.1",
+        "decache": "^4.6.1",
+        "fancy-log": "^2.0.0",
+        "findit2": "^2.2.3",
+        "fs-extra": "^10.0.1",
+        "glob": "^7.2.0",
+        "handlebars": "^4.7.7",
+        "html-entities": "^2.3.2",
+        "i18next": "^21.6.11",
+        "inside": "^1.0.0",
+        "json5": "^2.2.0",
+        "lodash": "^4.17.21",
+        "loglevel": "^1.8.0",
+        "loglevel-plugin-prefix": "^0.8.4",
+        "lunr": "^2.3.9",
+        "marked": "^4.0.12",
+        "minimist": "^1.2.5",
+        "opencollective-postinstall": "^2.0.3",
+        "os-name": "4.0.1",
+        "pdfjs-dist": "^2.12.313",
+        "pdfmake": "^0.2.4",
+        "semver": "^7.3.5",
+        "traverse": "^0.6.6",
+        "ts-morph": "^13.0.3",
+        "uuid": "^8.3.2"
       },
       "dependencies": {
-        "globals": {
-          "version": "13.12.0",
-          "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.0.tgz",
-          "integrity": "sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg==",
+        "@angular-devkit/core": {
+          "version": "13.3.6",
+          "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-13.3.6.tgz",
+          "integrity": "sha512-ZmD586B+RnM2CG5+jbXh2NVfIydTc/yKSjppYDDOv4I530YBm6vpfZMwClpiNk6XLbMv7KqX4Tlr4wfxlPYYbA==",
           "dev": true,
           "requires": {
-            "type-fest": "^0.20.2"
+            "ajv": "8.9.0",
+            "ajv-formats": "2.1.1",
+            "fast-json-stable-stringify": "2.1.0",
+            "magic-string": "0.25.7",
+            "rxjs": "6.6.7",
+            "source-map": "0.7.3"
           }
         },
-        "ignore": {
-          "version": "4.0.6",
-          "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
-          "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
-          "dev": true
-        },
-        "type-fest": {
-          "version": "0.20.2",
-          "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
-          "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
-          "dev": true
-        }
-      }
-    },
-    "@humanwhocodes/config-array": {
-      "version": "0.5.0",
-      "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz",
-      "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==",
-      "dev": true,
-      "requires": {
-        "@humanwhocodes/object-schema": "^1.2.0",
-        "debug": "^4.1.1",
-        "minimatch": "^3.0.4"
-      }
-    },
-    "@humanwhocodes/object-schema": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz",
-      "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
-      "dev": true
-    },
-    "@istanbuljs/schema": {
-      "version": "0.1.3",
-      "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz",
-      "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==",
-      "dev": true
-    },
-    "@jridgewell/resolve-uri": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-1.0.0.tgz",
-      "integrity": "sha512-9oLAnygRMi8Q5QkYEU4XWK04B+nuoXoxjRvRxgjuChkLZFBja0YPSgdZ7dZtwhncLBcQe/I/E+fLuk5qxcYVJA==",
-      "dev": true
-    },
-    "@jsdevtools/coverage-istanbul-loader": {
-      "version": "3.0.5",
-      "resolved": "https://registry.npmjs.org/@jsdevtools/coverage-istanbul-loader/-/coverage-istanbul-loader-3.0.5.tgz",
-      "integrity": "sha512-EUCPEkaRPvmHjWAAZkWMT7JDzpw7FKB00WTISaiXsbNOd5hCHg77XLA8sLYLFDo1zepYLo2w7GstN8YBqRXZfA==",
-      "dev": true,
-      "requires": {
-        "convert-source-map": "^1.7.0",
-        "istanbul-lib-instrument": "^4.0.3",
-        "loader-utils": "^2.0.0",
-        "merge-source-map": "^1.1.0",
-        "schema-utils": "^2.7.0"
-      },
-      "dependencies": {
-        "schema-utils": {
-          "version": "2.7.1",
-          "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz",
-          "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==",
+        "@angular-devkit/schematics": {
+          "version": "13.3.6",
+          "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-13.3.6.tgz",
+          "integrity": "sha512-yLh5xc92C/FiaAp27coPiKWpSUmwoXF7vMxbJYJTyOXlt0mUITAEAwtrZQNr4yAxW/yvgTdyg7PhXaveQNTUuQ==",
           "dev": true,
           "requires": {
-            "@types/json-schema": "^7.0.5",
-            "ajv": "^6.12.4",
-            "ajv-keywords": "^3.5.2"
+            "@angular-devkit/core": "13.3.6",
+            "jsonc-parser": "3.0.0",
+            "magic-string": "0.25.7",
+            "ora": "5.4.1",
+            "rxjs": "6.6.7"
           }
-        }
-      }
-    },
-    "@ngrx/effects": {
-      "version": "12.5.1",
-      "resolved": "https://registry.npmjs.org/@ngrx/effects/-/effects-12.5.1.tgz",
-      "integrity": "sha512-fVNGIIntYLRWW1XWe0os2XOv03L22S4WTkX0OPZ9O6ztwuaNq0yzxWN7UeAC6H385F+g0k76KwRV78zHyP0bfQ==",
-      "requires": {
-        "tslib": "^2.0.0"
+        },
+        "@babel/helper-define-polyfill-provider": {
+          "version": "0.3.1",
+          "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.1.tgz",
+          "integrity": "sha512-J9hGMpJQmtWmj46B3kBHmL38UhJGhYX7eqkcq+2gsstyYt341HmPeWspihX43yVRA0mS+8GGk2Gckc7bY/HCmA==",
+          "dev": true,
+          "requires": {
+            "@babel/helper-compilation-targets": "^7.13.0",
+            "@babel/helper-module-imports": "^7.12.13",
+            "@babel/helper-plugin-utils": "^7.13.0",
+            "@babel/traverse": "^7.13.0",
+            "debug": "^4.1.1",
+            "lodash.debounce": "^4.0.8",
+            "resolve": "^1.14.2",
+            "semver": "^6.1.2"
+          },
+          "dependencies": {
+            "semver": {
+              "version": "6.3.0",
+              "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+              "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+              "dev": true
+            }
+          }
+        },
+        "@babel/plugin-proposal-async-generator-functions": {
+          "version": "7.17.12",
+          "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.17.12.tgz",
+          "integrity": "sha512-RWVvqD1ooLKP6IqWTA5GyFVX2isGEgC5iFxKzfYOIy/QEFdxYyCybBDtIGjipHpb9bDWHzcqGqFakf+mVmBTdQ==",
+          "dev": true,
+          "requires": {
+            "@babel/helper-plugin-utils": "^7.17.12",
+            "@babel/helper-remap-async-to-generator": "^7.16.8",
+            "@babel/plugin-syntax-async-generators": "^7.8.4"
+          }
+        },
+        "@babel/plugin-transform-async-to-generator": {
+          "version": "7.17.12",
+          "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.17.12.tgz",
+          "integrity": "sha512-J8dbrWIOO3orDzir57NRsjg4uxucvhby0L/KZuGsWDj0g7twWK3g7JhJhOrXtuXiw8MeiSdJ3E0OW9H8LYEzLQ==",
+          "dev": true,
+          "requires": {
+            "@babel/helper-module-imports": "^7.16.7",
+            "@babel/helper-plugin-utils": "^7.17.12",
+            "@babel/helper-remap-async-to-generator": "^7.16.8"
+          }
+        },
+        "@babel/preset-env": {
+          "version": "7.18.0",
+          "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.18.0.tgz",
+          "integrity": "sha512-cP74OMs7ECLPeG1reiCQ/D/ypyOxgfm8uR6HRYV23vTJ7Lu1nbgj9DQDo/vH59gnn7GOAwtTDPPYV4aXzsMKHA==",
+          "dev": true,
+          "requires": {
+            "@babel/compat-data": "^7.17.10",
+            "@babel/helper-compilation-targets": "^7.17.10",
+            "@babel/helper-plugin-utils": "^7.17.12",
+            "@babel/helper-validator-option": "^7.16.7",
+            "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.17.12",
+            "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.17.12",
+            "@babel/plugin-proposal-async-generator-functions": "^7.17.12",
+            "@babel/plugin-proposal-class-properties": "^7.17.12",
+            "@babel/plugin-proposal-class-static-block": "^7.18.0",
+            "@babel/plugin-proposal-dynamic-import": "^7.16.7",
+            "@babel/plugin-proposal-export-namespace-from": "^7.17.12",
+            "@babel/plugin-proposal-json-strings": "^7.17.12",
+            "@babel/plugin-proposal-logical-assignment-operators": "^7.17.12",
+            "@babel/plugin-proposal-nullish-coalescing-operator": "^7.17.12",
+            "@babel/plugin-proposal-numeric-separator": "^7.16.7",
+            "@babel/plugin-proposal-object-rest-spread": "^7.18.0",
+            "@babel/plugin-proposal-optional-catch-binding": "^7.16.7",
+            "@babel/plugin-proposal-optional-chaining": "^7.17.12",
+            "@babel/plugin-proposal-private-methods": "^7.17.12",
+            "@babel/plugin-proposal-private-property-in-object": "^7.17.12",
+            "@babel/plugin-proposal-unicode-property-regex": "^7.17.12",
+            "@babel/plugin-syntax-async-generators": "^7.8.4",
+            "@babel/plugin-syntax-class-properties": "^7.12.13",
+            "@babel/plugin-syntax-class-static-block": "^7.14.5",
+            "@babel/plugin-syntax-dynamic-import": "^7.8.3",
+            "@babel/plugin-syntax-export-namespace-from": "^7.8.3",
+            "@babel/plugin-syntax-import-assertions": "^7.17.12",
+            "@babel/plugin-syntax-json-strings": "^7.8.3",
+            "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4",
+            "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3",
+            "@babel/plugin-syntax-numeric-separator": "^7.10.4",
+            "@babel/plugin-syntax-object-rest-spread": "^7.8.3",
+            "@babel/plugin-syntax-optional-catch-binding": "^7.8.3",
+            "@babel/plugin-syntax-optional-chaining": "^7.8.3",
+            "@babel/plugin-syntax-private-property-in-object": "^7.14.5",
+            "@babel/plugin-syntax-top-level-await": "^7.14.5",
+            "@babel/plugin-transform-arrow-functions": "^7.17.12",
+            "@babel/plugin-transform-async-to-generator": "^7.17.12",
+            "@babel/plugin-transform-block-scoped-functions": "^7.16.7",
+            "@babel/plugin-transform-block-scoping": "^7.17.12",
+            "@babel/plugin-transform-classes": "^7.17.12",
+            "@babel/plugin-transform-computed-properties": "^7.17.12",
+            "@babel/plugin-transform-destructuring": "^7.18.0",
+            "@babel/plugin-transform-dotall-regex": "^7.16.7",
+            "@babel/plugin-transform-duplicate-keys": "^7.17.12",
+            "@babel/plugin-transform-exponentiation-operator": "^7.16.7",
+            "@babel/plugin-transform-for-of": "^7.17.12",
+            "@babel/plugin-transform-function-name": "^7.16.7",
+            "@babel/plugin-transform-literals": "^7.17.12",
+            "@babel/plugin-transform-member-expression-literals": "^7.16.7",
+            "@babel/plugin-transform-modules-amd": "^7.18.0",
+            "@babel/plugin-transform-modules-commonjs": "^7.18.0",
+            "@babel/plugin-transform-modules-systemjs": "^7.18.0",
+            "@babel/plugin-transform-modules-umd": "^7.18.0",
+            "@babel/plugin-transform-named-capturing-groups-regex": "^7.17.12",
+            "@babel/plugin-transform-new-target": "^7.17.12",
+            "@babel/plugin-transform-object-super": "^7.16.7",
+            "@babel/plugin-transform-parameters": "^7.17.12",
+            "@babel/plugin-transform-property-literals": "^7.16.7",
+            "@babel/plugin-transform-regenerator": "^7.18.0",
+            "@babel/plugin-transform-reserved-words": "^7.17.12",
+            "@babel/plugin-transform-shorthand-properties": "^7.16.7",
+            "@babel/plugin-transform-spread": "^7.17.12",
+            "@babel/plugin-transform-sticky-regex": "^7.16.7",
+            "@babel/plugin-transform-template-literals": "^7.17.12",
+            "@babel/plugin-transform-typeof-symbol": "^7.17.12",
+            "@babel/plugin-transform-unicode-escapes": "^7.16.7",
+            "@babel/plugin-transform-unicode-regex": "^7.16.7",
+            "@babel/preset-modules": "^0.1.5",
+            "@babel/types": "^7.18.0",
+            "babel-plugin-polyfill-corejs2": "^0.3.0",
+            "babel-plugin-polyfill-corejs3": "^0.5.0",
+            "babel-plugin-polyfill-regenerator": "^0.3.0",
+            "core-js-compat": "^3.22.1",
+            "semver": "^6.3.0"
+          },
+          "dependencies": {
+            "semver": {
+              "version": "6.3.0",
+              "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+              "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+              "dev": true
+            }
+          }
+        },
+        "ajv": {
+          "version": "8.9.0",
+          "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz",
+          "integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==",
+          "dev": true,
+          "requires": {
+            "fast-deep-equal": "^3.1.1",
+            "json-schema-traverse": "^1.0.0",
+            "require-from-string": "^2.0.2",
+            "uri-js": "^4.2.2"
+          }
+        },
+        "ajv-formats": {
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz",
+          "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==",
+          "dev": true,
+          "requires": {
+            "ajv": "^8.0.0"
+          }
+        },
+        "ansi-styles": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+          "dev": true,
+          "requires": {
+            "color-convert": "^2.0.1"
+          }
+        },
+        "babel-plugin-polyfill-corejs2": {
+          "version": "0.3.1",
+          "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.1.tgz",
+          "integrity": "sha512-v7/T6EQcNfVLfcN2X8Lulb7DjprieyLWJK/zOWH5DUYcAgex9sP3h25Q+DLsX9TloXe3y1O8l2q2Jv9q8UVB9w==",
+          "dev": true,
+          "requires": {
+            "@babel/compat-data": "^7.13.11",
+            "@babel/helper-define-polyfill-provider": "^0.3.1",
+            "semver": "^6.1.1"
+          },
+          "dependencies": {
+            "semver": {
+              "version": "6.3.0",
+              "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+              "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+              "dev": true
+            }
+          }
+        },
+        "babel-plugin-polyfill-corejs3": {
+          "version": "0.5.2",
+          "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.2.tgz",
+          "integrity": "sha512-G3uJih0XWiID451fpeFaYGVuxHEjzKTHtc9uGFEjR6hHrvNzeS/PX+LLLcetJcytsB5m4j+K3o/EpXJNb/5IEQ==",
+          "dev": true,
+          "requires": {
+            "@babel/helper-define-polyfill-provider": "^0.3.1",
+            "core-js-compat": "^3.21.0"
+          }
+        },
+        "babel-plugin-polyfill-regenerator": {
+          "version": "0.3.1",
+          "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.1.tgz",
+          "integrity": "sha512-Y2B06tvgHYt1x0yz17jGkGeeMr5FeKUu+ASJ+N6nB5lQ8Dapfg42i0OVrf8PNGJ3zKL4A23snMi1IRwrqqND7A==",
+          "dev": true,
+          "requires": {
+            "@babel/helper-define-polyfill-provider": "^0.3.1"
+          }
+        },
+        "chalk": {
+          "version": "4.1.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+          "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "dev": true,
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
+        },
+        "glob": {
+          "version": "7.2.3",
+          "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+          "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+          "dev": true,
+          "requires": {
+            "fs.realpath": "^1.0.0",
+            "inflight": "^1.0.4",
+            "inherits": "2",
+            "minimatch": "^3.1.1",
+            "once": "^1.3.0",
+            "path-is-absolute": "^1.0.0"
+          }
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
+        "html-entities": {
+          "version": "2.3.3",
+          "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz",
+          "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==",
+          "dev": true
+        },
+        "json-schema-traverse": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+          "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+          "dev": true
+        },
+        "minimatch": {
+          "version": "3.1.2",
+          "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+          "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+          "dev": true,
+          "requires": {
+            "brace-expansion": "^1.1.7"
+          }
+        },
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
+        }
       }
     },
-    "@ngrx/store": {
-      "version": "12.5.1",
-      "resolved": "https://registry.npmjs.org/@ngrx/store/-/store-12.5.1.tgz",
-      "integrity": "sha512-NLVkHLVeZc7IboXSDZlFoq1QrupmwYTYKRHS6se7ZasAv/lrIjHWsVVdICKSVRBsHZYu3+dmCXmu+YgulP7iHw==",
+    "@compodoc/live-server": {
+      "version": "1.2.3",
+      "resolved": "https://registry.npmjs.org/@compodoc/live-server/-/live-server-1.2.3.tgz",
+      "integrity": "sha512-hDmntVCyjjaxuJzPzBx68orNZ7TW4BtHWMnXlIVn5dqhK7vuFF/11hspO1cMmc+2QTYgqde1TBcb3127S7Zrow==",
+      "dev": true,
       "requires": {
-        "tslib": "^2.0.0"
+        "chokidar": "^3.5.2",
+        "colors": "1.4.0",
+        "connect": "^3.7.0",
+        "cors": "^2.8.5",
+        "event-stream": "4.0.1",
+        "faye-websocket": "0.11.x",
+        "http-auth": "4.1.9",
+        "http-auth-connect": "^1.0.5",
+        "morgan": "^1.10.0",
+        "object-assign": "^4.1.1",
+        "open": "8.4.0",
+        "proxy-middleware": "^0.15.0",
+        "send": "^0.18.0",
+        "serve-index": "^1.9.1"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "2.6.9",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+          "dev": true,
+          "requires": {
+            "ms": "2.0.0"
+          },
+          "dependencies": {
+            "ms": {
+              "version": "2.0.0",
+              "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+              "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+              "dev": true
+            }
+          }
+        },
+        "ms": {
+          "version": "2.1.3",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+          "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+          "dev": true
+        },
+        "object-assign": {
+          "version": "4.1.1",
+          "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+          "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
+          "dev": true
+        },
+        "open": {
+          "version": "8.4.0",
+          "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz",
+          "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==",
+          "dev": true,
+          "requires": {
+            "define-lazy-prop": "^2.0.0",
+            "is-docker": "^2.1.1",
+            "is-wsl": "^2.2.0"
+          }
+        },
+        "send": {
+          "version": "0.18.0",
+          "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
+          "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
+          "dev": true,
+          "requires": {
+            "debug": "2.6.9",
+            "depd": "2.0.0",
+            "destroy": "1.2.0",
+            "encodeurl": "~1.0.2",
+            "escape-html": "~1.0.3",
+            "etag": "~1.8.1",
+            "fresh": "0.5.2",
+            "http-errors": "2.0.0",
+            "mime": "1.6.0",
+            "ms": "2.1.3",
+            "on-finished": "2.4.1",
+            "range-parser": "~1.2.1",
+            "statuses": "2.0.1"
+          }
+        }
       }
     },
-    "@ngtools/webpack": {
-      "version": "12.2.13",
-      "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-12.2.13.tgz",
-      "integrity": "sha512-krAwMyRqOaC1S0+IxAFid9F6A6ABip2DJ0tgCx26X+1Vw/d1GKtV9ZqDJFizMf5k1ywl9aBlhOazWpq5d6i+gw==",
-      "dev": true
+    "@compodoc/ngd-core": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/@compodoc/ngd-core/-/ngd-core-2.1.0.tgz",
+      "integrity": "sha512-nyBH7J7SJJ2AV6OeZhJ02kRtVB7ALnZJKgShjoL9CNmOFEj8AkdhP9qTBIgjaDrbsW5pF4nx32KQL2fT7RFnqw==",
+      "dev": true,
+      "requires": {
+        "ansi-colors": "^4.1.1",
+        "fancy-log": "^1.3.3",
+        "typescript": "^4.0.3"
+      },
+      "dependencies": {
+        "fancy-log": {
+          "version": "1.3.3",
+          "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz",
+          "integrity": "sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==",
+          "dev": true,
+          "requires": {
+            "ansi-gray": "^0.1.1",
+            "color-support": "^1.1.3",
+            "parse-node-version": "^1.0.0",
+            "time-stamp": "^1.0.0"
+          }
+        }
+      }
     },
-    "@nodelib/fs.scandir": {
-      "version": "2.1.5",
-      "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
-      "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+    "@compodoc/ngd-transformer": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/@compodoc/ngd-transformer/-/ngd-transformer-2.1.0.tgz",
+      "integrity": "sha512-Jo4VCMzIUtgIAdRmhHhOoRRE01gCjc5CyrUERRx0VgEzkkCm1Wmu/XHSsQP6tSpCYHBjERghqaDqH5DabkR2oQ==",
       "dev": true,
       "requires": {
-        "@nodelib/fs.stat": "2.0.5",
-        "run-parallel": "^1.1.9"
+        "@aduh95/viz.js": "^3.1.0",
+        "@compodoc/ngd-core": "~2.1.0",
+        "dot": "^1.1.3",
+        "fs-extra": "^9.0.1"
+      },
+      "dependencies": {
+        "fs-extra": {
+          "version": "9.1.0",
+          "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
+          "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
+          "dev": true,
+          "requires": {
+            "at-least-node": "^1.0.0",
+            "graceful-fs": "^4.2.0",
+            "jsonfile": "^6.0.1",
+            "universalify": "^2.0.0"
+          }
+        }
       }
     },
-    "@nodelib/fs.stat": {
-      "version": "2.0.5",
-      "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
-      "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+    "@csstools/convert-colors": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/@csstools/convert-colors/-/convert-colors-1.4.0.tgz",
+      "integrity": "sha512-5a6wqoJV/xEdbRNKVo6I4hO3VjyDq//8q2f9I6PBAvMesJHFauXDorcNCsr9RzvsZnaWi5NYCcfyqP1QeFHFbw==",
       "dev": true
     },
-    "@nodelib/fs.walk": {
-      "version": "1.2.8",
-      "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
-      "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+    "@design-systems/utils": {
+      "version": "2.12.0",
+      "resolved": "https://registry.npmjs.org/@design-systems/utils/-/utils-2.12.0.tgz",
+      "integrity": "sha512-Y/d2Zzr+JJfN6u1gbuBUb1ufBuLMJJRZQk+dRmw8GaTpqKx5uf7cGUYGTwN02dIb3I+Tf+cW8jcGBTRiFxdYFg==",
       "dev": true,
       "requires": {
-        "@nodelib/fs.scandir": "2.1.5",
-        "fastq": "^1.6.0"
+        "@babel/runtime": "^7.11.2",
+        "clsx": "^1.0.4",
+        "focus-lock": "^0.8.0",
+        "react-merge-refs": "^1.0.0"
       }
     },
-    "@npmcli/git": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-2.1.0.tgz",
-      "integrity": "sha512-/hBFX/QG1b+N7PZBFs0bi+evgRZcK9nWBxQKZkGoXUT5hJSwl5c4d7y8/hm+NQZRPhQ67RzFaj5UM9YeyKoryw==",
+    "@devtools-ds/object-inspector": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/@devtools-ds/object-inspector/-/object-inspector-1.2.0.tgz",
+      "integrity": "sha512-VztcwqVwScSvYdvJVZBJYsVO/2Pew3JPpFV3T9fuCHQLlHcLYOV3aU/kBS2ScuE2O1JN0ZbobLqFLa3vQF54Fw==",
       "dev": true,
       "requires": {
-        "@npmcli/promise-spawn": "^1.3.2",
-        "lru-cache": "^6.0.0",
-        "mkdirp": "^1.0.4",
-        "npm-pick-manifest": "^6.1.1",
-        "promise-inflight": "^1.0.1",
-        "promise-retry": "^2.0.1",
-        "semver": "^7.3.5",
-        "which": "^2.0.2"
+        "@babel/runtime": "7.7.2",
+        "@devtools-ds/object-parser": "^1.2.0",
+        "@devtools-ds/themes": "^1.2.0",
+        "@devtools-ds/tree": "^1.2.0",
+        "clsx": "1.1.0"
       },
       "dependencies": {
-        "which": {
-          "version": "2.0.2",
-          "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
-          "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+        "@babel/runtime": {
+          "version": "7.7.2",
+          "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.7.2.tgz",
+          "integrity": "sha512-JONRbXbTXc9WQE2mAZd1p0Z3DZ/6vaQIkgYMSTP3KjRCyd7rCZCcfhCyX+YjwcKxcZ82UrxbRD358bpExNgrjw==",
           "dev": true,
           "requires": {
-            "isexe": "^2.0.0"
+            "regenerator-runtime": "^0.13.2"
           }
         }
       }
     },
-    "@npmcli/installed-package-contents": {
-      "version": "1.0.7",
-      "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-1.0.7.tgz",
-      "integrity": "sha512-9rufe0wnJusCQoLpV9ZPKIVP55itrM5BxOXs10DmdbRfgWtHy1LDyskbwRnBghuB0PrF7pNPOqREVtpz4HqzKw==",
+    "@devtools-ds/object-parser": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/@devtools-ds/object-parser/-/object-parser-1.2.0.tgz",
+      "integrity": "sha512-SjGGyiFFY8dtUpiWXAvRSzRT+hE11EAAysrq2PsC/GVLf2ZLyT2nHlQO5kDStywyTz+fjw7S7pyDRj1HG9YTTA==",
       "dev": true,
       "requires": {
-        "npm-bundled": "^1.1.1",
-        "npm-normalize-package-bin": "^1.0.1"
+        "@babel/runtime": "~7.5.4"
+      },
+      "dependencies": {
+        "@babel/runtime": {
+          "version": "7.5.5",
+          "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.5.5.tgz",
+          "integrity": "sha512-28QvEGyQyNkB0/m2B4FU7IEZGK2NUrcMtT6BZEFALTguLk+AUT6ofsHtPk5QyjAdUkpMJ+/Em+quwz4HOt30AQ==",
+          "dev": true,
+          "requires": {
+            "regenerator-runtime": "^0.13.2"
+          }
+        }
       }
     },
-    "@npmcli/move-file": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz",
-      "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==",
+    "@devtools-ds/themes": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/@devtools-ds/themes/-/themes-1.2.0.tgz",
+      "integrity": "sha512-LimEITorE6yWZWWuMc6OiBfLQgPrQqWbyMEmfRUDPa3PHXoAY4SpDxczfg31fgyRDUNWnZhjaJH5bBbu8VEbIw==",
       "dev": true,
       "requires": {
-        "mkdirp": "^1.0.4",
-        "rimraf": "^3.0.2"
+        "@babel/runtime": "~7.5.4",
+        "@design-systems/utils": "2.12.0",
+        "clsx": "1.1.0"
+      },
+      "dependencies": {
+        "@babel/runtime": {
+          "version": "7.5.5",
+          "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.5.5.tgz",
+          "integrity": "sha512-28QvEGyQyNkB0/m2B4FU7IEZGK2NUrcMtT6BZEFALTguLk+AUT6ofsHtPk5QyjAdUkpMJ+/Em+quwz4HOt30AQ==",
+          "dev": true,
+          "requires": {
+            "regenerator-runtime": "^0.13.2"
+          }
+        }
       }
     },
-    "@npmcli/node-gyp": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-1.0.3.tgz",
-      "integrity": "sha512-fnkhw+fmX65kiLqk6E3BFLXNC26rUhK90zVwe2yncPliVT/Qos3xjhTLE59Df8KnPlcwIERXKVlU1bXoUQ+liA==",
-      "dev": true
-    },
-    "@npmcli/promise-spawn": {
-      "version": "1.3.2",
-      "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-1.3.2.tgz",
-      "integrity": "sha512-QyAGYo/Fbj4MXeGdJcFzZ+FkDkomfRBrPM+9QYJSg+PxgAUL+LU3FneQk37rKR2/zjqkCV1BLHccX98wRXG3Sg==",
+    "@devtools-ds/tree": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/@devtools-ds/tree/-/tree-1.2.0.tgz",
+      "integrity": "sha512-hC4g4ocuo2eg7jsnzKdauxH0sDQiPW3KSM2+uK3kRgcmr9PzpBD5Kob+Y/WFSVKswFleftOGKL4BQLuRv0sPxA==",
       "dev": true,
       "requires": {
-        "infer-owner": "^1.0.4"
+        "@babel/runtime": "7.7.2",
+        "@devtools-ds/themes": "^1.2.0",
+        "clsx": "1.1.0"
+      },
+      "dependencies": {
+        "@babel/runtime": {
+          "version": "7.7.2",
+          "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.7.2.tgz",
+          "integrity": "sha512-JONRbXbTXc9WQE2mAZd1p0Z3DZ/6vaQIkgYMSTP3KjRCyd7rCZCcfhCyX+YjwcKxcZ82UrxbRD358bpExNgrjw==",
+          "dev": true,
+          "requires": {
+            "regenerator-runtime": "^0.13.2"
+          }
+        }
       }
     },
-    "@npmcli/run-script": {
-      "version": "1.8.6",
-      "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-1.8.6.tgz",
-      "integrity": "sha512-e42bVZnC6VluBZBAFEr3YrdqSspG3bgilyg4nSLBJ7TRGNCzxHa92XAHxQBLYg0BmgwO4b2mf3h/l5EkEWRn3g==",
+    "@discoveryjs/json-ext": {
+      "version": "0.5.3",
+      "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.3.tgz",
+      "integrity": "sha512-Fxt+AfXgjMoin2maPIYzFZnQjAXjAL0PHscM5pRTtatFqB+vZxAM9tLp2Optnuw3QOQC40jTNeGYFOMvyf7v9g==",
+      "dev": true
+    },
+    "@eslint/eslintrc": {
+      "version": "0.4.3",
+      "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz",
+      "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==",
       "dev": true,
       "requires": {
-        "@npmcli/node-gyp": "^1.0.2",
-        "@npmcli/promise-spawn": "^1.3.2",
-        "node-gyp": "^7.1.0",
-        "read-package-json-fast": "^2.0.1"
+        "ajv": "^6.12.4",
+        "debug": "^4.1.1",
+        "espree": "^7.3.0",
+        "globals": "^13.9.0",
+        "ignore": "^4.0.6",
+        "import-fresh": "^3.2.1",
+        "js-yaml": "^3.13.1",
+        "minimatch": "^3.0.4",
+        "strip-json-comments": "^3.1.1"
+      },
+      "dependencies": {
+        "globals": {
+          "version": "13.15.0",
+          "resolved": "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz",
+          "integrity": "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==",
+          "dev": true,
+          "requires": {
+            "type-fest": "^0.20.2"
+          }
+        },
+        "ignore": {
+          "version": "4.0.6",
+          "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
+          "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
+          "dev": true
+        },
+        "type-fest": {
+          "version": "0.20.2",
+          "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+          "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+          "dev": true
+        }
       }
     },
-    "@schematics/angular": {
-      "version": "12.2.13",
-      "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-12.2.13.tgz",
-      "integrity": "sha512-TrigQ9TCmAedf1J5PSSSfTC+sScYrITeAUN8a9rlkjZNvff8hHVyQaiZmhqL+egKQL828mhkqpnFUDd4QsPBIw==",
+    "@foliojs-fork/fontkit": {
+      "version": "1.9.1",
+      "resolved": "https://registry.npmjs.org/@foliojs-fork/fontkit/-/fontkit-1.9.1.tgz",
+      "integrity": "sha512-U589voc2/ROnvx1CyH9aNzOQWJp127JGU1QAylXGQ7LoEAF6hMmahZLQ4eqAcgHUw+uyW4PjtCItq9qudPkK3A==",
       "dev": true,
       "requires": {
-        "@angular-devkit/core": "12.2.13",
-        "@angular-devkit/schematics": "12.2.13",
-        "jsonc-parser": "3.0.0"
+        "@foliojs-fork/restructure": "^2.0.2",
+        "brfs": "^2.0.0",
+        "brotli": "^1.2.0",
+        "browserify-optional": "^1.0.1",
+        "clone": "^1.0.4",
+        "deep-equal": "^1.0.0",
+        "dfa": "^1.2.0",
+        "tiny-inflate": "^1.0.2",
+        "unicode-properties": "^1.2.2",
+        "unicode-trie": "^2.0.0"
       }
     },
-    "@socket.io/base64-arraybuffer": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/@socket.io/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
-      "integrity": "sha512-dOlCBKnDw4iShaIsH/bxujKTM18+2TOAsYz+KSc11Am38H4q5Xw8Bbz97ZYdrVNM+um3p7w86Bvvmcn9q+5+eQ==",
-      "dev": true
-    },
-    "@stencil/core": {
-      "version": "1.17.4",
-      "resolved": "https://registry.npmjs.org/@stencil/core/-/core-1.17.4.tgz",
-      "integrity": "sha512-dmuNYM6fnHPvE2ptHoUBQtjcpXqrHnkDtdyUD6/JrZWcJt6jBtrykewObOxzpDCMLs+NT7668ussRagdVL03gQ==",
+    "@foliojs-fork/linebreak": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/@foliojs-fork/linebreak/-/linebreak-1.1.1.tgz",
+      "integrity": "sha512-pgY/+53GqGQI+mvDiyprvPWgkTlVBS8cxqee03ejm6gKAQNsR1tCYCIvN9FHy7otZajzMqCgPOgC4cHdt4JPig==",
+      "dev": true,
       "requires": {
-        "typescript": "3.9.7"
+        "base64-js": "1.3.1",
+        "brfs": "^2.0.2",
+        "unicode-trie": "^2.0.0"
       },
       "dependencies": {
-        "typescript": {
-          "version": "3.9.7",
-          "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz",
-          "integrity": "sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw=="
+        "base64-js": {
+          "version": "1.3.1",
+          "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
+          "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==",
+          "dev": true
         }
       }
     },
-    "@tootallnate/once": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
-      "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==",
-      "dev": true
-    },
-    "@trysound/sax": {
-      "version": "0.2.0",
-      "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz",
-      "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==",
-      "dev": true
-    },
-    "@types/component-emitter": {
-      "version": "1.2.11",
-      "resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.11.tgz",
-      "integrity": "sha512-SRXjM+tfsSlA9VuG8hGO2nft2p8zjXCK1VcC6N4NXbBbYbSia9kzCChYQajIjzIqOOOuh5Ock6MmV2oux4jDZQ==",
-      "dev": true
-    },
-    "@types/cookie": {
-      "version": "0.4.1",
-      "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz",
-      "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==",
-      "dev": true
-    },
-    "@types/cors": {
-      "version": "2.8.12",
-      "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz",
-      "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==",
-      "dev": true
-    },
-    "@types/eslint": {
-      "version": "8.2.0",
-      "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.2.0.tgz",
-      "integrity": "sha512-74hbvsnc+7TEDa1z5YLSe4/q8hGYB3USNvCuzHUJrjPV6hXaq8IXcngCrHkuvFt0+8rFz7xYXrHgNayIX0UZvQ==",
+    "@foliojs-fork/pdfkit": {
+      "version": "0.13.0",
+      "resolved": "https://registry.npmjs.org/@foliojs-fork/pdfkit/-/pdfkit-0.13.0.tgz",
+      "integrity": "sha512-YXeG1fml9k97YNC9K8e292Pj2JzGt9uOIiBFuQFxHsdQ45BlxW+JU3RQK6JAvXU7kjhjP8rCcYvpk36JLD33sQ==",
       "dev": true,
       "requires": {
-        "@types/estree": "*",
-        "@types/json-schema": "*"
+        "@foliojs-fork/fontkit": "^1.9.1",
+        "@foliojs-fork/linebreak": "^1.1.1",
+        "crypto-js": "^4.0.0",
+        "png-js": "^1.0.0"
       }
     },
-    "@types/eslint-scope": {
-      "version": "3.7.1",
-      "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.1.tgz",
-      "integrity": "sha512-SCFeogqiptms4Fg29WpOTk5nHIzfpKCemSN63ksBQYKTcXoJEmJagV+DhVmbapZzY4/5YaOV1nZwrsU79fFm1g==",
+    "@foliojs-fork/restructure": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/@foliojs-fork/restructure/-/restructure-2.0.2.tgz",
+      "integrity": "sha512-59SgoZ3EXbkfSX7b63tsou/SDGzwUEK6MuB5sKqgVK1/XE0fxmpsOb9DQI8LXW3KfGnAjImCGhhEb7uPPAUVNA==",
+      "dev": true
+    },
+    "@humanwhocodes/config-array": {
+      "version": "0.5.0",
+      "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz",
+      "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==",
       "dev": true,
       "requires": {
-        "@types/eslint": "*",
-        "@types/estree": "*"
+        "@humanwhocodes/object-schema": "^1.2.0",
+        "debug": "^4.1.1",
+        "minimatch": "^3.0.4"
       }
     },
-    "@types/estree": {
-      "version": "0.0.50",
-      "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.50.tgz",
-      "integrity": "sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==",
+    "@humanwhocodes/object-schema": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz",
+      "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
       "dev": true
     },
-    "@types/glob": {
-      "version": "7.2.0",
-      "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz",
-      "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==",
+    "@istanbuljs/load-nyc-config": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
+      "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==",
       "dev": true,
       "requires": {
-        "@types/minimatch": "*",
-        "@types/node": "*"
+        "camelcase": "^5.3.1",
+        "find-up": "^4.1.0",
+        "get-package-type": "^0.1.0",
+        "js-yaml": "^3.13.1",
+        "resolve-from": "^5.0.0"
+      },
+      "dependencies": {
+        "resolve-from": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
+          "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
+          "dev": true
+        }
       }
     },
-    "@types/jasmine": {
-      "version": "3.8.2",
-      "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-3.8.2.tgz",
-      "integrity": "sha512-u5h7dqzy2XpXTzhOzSNQUQpKGFvROF8ElNX9P/TJvsHnTg/JvsAseVsGWQAQQldqanYaM+5kwxW909BBFAUYsg==",
-      "dev": true
-    },
-    "@types/json-schema": {
-      "version": "7.0.9",
-      "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz",
-      "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ=="
-    },
-    "@types/minimatch": {
-      "version": "3.0.5",
-      "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz",
-      "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==",
-      "dev": true
-    },
-    "@types/node": {
-      "version": "12.20.37",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.37.tgz",
-      "integrity": "sha512-i1KGxqcvJaLQali+WuypQnXwcplhtNtjs66eNsZpp2P2FL/trJJxx/VWsM0YCL2iMoIJrbXje48lvIQAQ4p2ZA==",
-      "dev": true
-    },
-    "@types/parse-json": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
-      "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==",
-      "dev": true
-    },
-    "@types/source-list-map": {
-      "version": "0.1.2",
-      "resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz",
-      "integrity": "sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA==",
+    "@istanbuljs/schema": {
+      "version": "0.1.3",
+      "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz",
+      "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==",
       "dev": true
     },
-    "@types/webpack-sources": {
-      "version": "0.1.9",
-      "resolved": "https://registry.npmjs.org/@types/webpack-sources/-/webpack-sources-0.1.9.tgz",
-      "integrity": "sha512-bvzMnzqoK16PQIC8AYHNdW45eREJQMd6WG/msQWX5V2+vZmODCOPb4TJcbgRljTZZTwTM4wUMcsI8FftNA7new==",
+    "@jest/transform": {
+      "version": "26.6.2",
+      "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-26.6.2.tgz",
+      "integrity": "sha512-E9JjhUgNzvuQ+vVAL21vlyfy12gP0GhazGgJC4h6qUt1jSdUXGWJ1wfu/X7Sd8etSgxV4ovT1pb9v5D6QW4XgA==",
       "dev": true,
       "requires": {
-        "@types/node": "*",
-        "@types/source-list-map": "*",
-        "source-map": "^0.6.1"
+        "@babel/core": "^7.1.0",
+        "@jest/types": "^26.6.2",
+        "babel-plugin-istanbul": "^6.0.0",
+        "chalk": "^4.0.0",
+        "convert-source-map": "^1.4.0",
+        "fast-json-stable-stringify": "^2.0.0",
+        "graceful-fs": "^4.2.4",
+        "jest-haste-map": "^26.6.2",
+        "jest-regex-util": "^26.0.0",
+        "jest-util": "^26.6.2",
+        "micromatch": "^4.0.2",
+        "pirates": "^4.0.1",
+        "slash": "^3.0.0",
+        "source-map": "^0.6.1",
+        "write-file-atomic": "^3.0.0"
       },
       "dependencies": {
+        "ansi-styles": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+          "dev": true,
+          "requires": {
+            "color-convert": "^2.0.1"
+          }
+        },
+        "chalk": {
+          "version": "4.1.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+          "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "dev": true,
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
         "source-map": {
           "version": "0.6.1",
           "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
           "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
           "dev": true
+        },
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
         }
       }
     },
-    "@typescript-eslint/eslint-plugin": {
-      "version": "4.33.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.33.0.tgz",
-      "integrity": "sha512-aINiAxGVdOl1eJyVjaWn/YcVAq4Gi/Yo35qHGCnqbWVz61g39D0h23veY/MA0rFFGfxK7TySg2uwDeNv+JgVpg==",
-      "dev": true,
-      "requires": {
-        "@typescript-eslint/experimental-utils": "4.33.0",
-        "@typescript-eslint/scope-manager": "4.33.0",
-        "debug": "^4.3.1",
-        "functional-red-black-tree": "^1.0.1",
-        "ignore": "^5.1.8",
-        "regexpp": "^3.1.0",
-        "semver": "^7.3.5",
-        "tsutils": "^3.21.0"
-      }
-    },
-    "@typescript-eslint/experimental-utils": {
-      "version": "4.33.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.33.0.tgz",
-      "integrity": "sha512-zeQjOoES5JFjTnAhI5QY7ZviczMzDptls15GFsI6jyUOq0kOf9+WonkhtlIhh0RgHRnqj5gdNxW5j1EvAyYg6Q==",
+    "@jest/types": {
+      "version": "26.6.2",
+      "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz",
+      "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==",
       "dev": true,
       "requires": {
-        "@types/json-schema": "^7.0.7",
-        "@typescript-eslint/scope-manager": "4.33.0",
-        "@typescript-eslint/types": "4.33.0",
-        "@typescript-eslint/typescript-estree": "4.33.0",
-        "eslint-scope": "^5.1.1",
-        "eslint-utils": "^3.0.0"
-      }
-    },
-    "@typescript-eslint/parser": {
-      "version": "4.33.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.33.0.tgz",
-      "integrity": "sha512-ZohdsbXadjGBSK0/r+d87X0SBmKzOq4/S5nzK6SBgJspFo9/CUDJ7hjayuze+JK7CZQLDMroqytp7pOcFKTxZA==",
-      "dev": true,
-      "requires": {
-        "@typescript-eslint/scope-manager": "4.33.0",
-        "@typescript-eslint/types": "4.33.0",
-        "@typescript-eslint/typescript-estree": "4.33.0",
-        "debug": "^4.3.1"
+        "@types/istanbul-lib-coverage": "^2.0.0",
+        "@types/istanbul-reports": "^3.0.0",
+        "@types/node": "*",
+        "@types/yargs": "^15.0.0",
+        "chalk": "^4.0.0"
+      },
+      "dependencies": {
+        "ansi-styles": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+          "dev": true,
+          "requires": {
+            "color-convert": "^2.0.1"
+          }
+        },
+        "chalk": {
+          "version": "4.1.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+          "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "dev": true,
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
+        }
       }
     },
-    "@typescript-eslint/scope-manager": {
-      "version": "4.33.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.33.0.tgz",
-      "integrity": "sha512-5IfJHpgTsTZuONKbODctL4kKuQje/bzBRkwHE8UOZ4f89Zeddg+EGZs8PD8NcN4LdM3ygHWYB3ukPAYjvl/qbQ==",
+    "@jridgewell/gen-mapping": {
+      "version": "0.3.1",
+      "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.1.tgz",
+      "integrity": "sha512-GcHwniMlA2z+WFPWuY8lp3fsza0I8xPFMWL5+n8LYyP6PSvPrXf4+n8stDHZY2DM0zy9sVkRDy1jDI4XGzYVqg==",
       "dev": true,
       "requires": {
-        "@typescript-eslint/types": "4.33.0",
-        "@typescript-eslint/visitor-keys": "4.33.0"
+        "@jridgewell/set-array": "^1.0.0",
+        "@jridgewell/sourcemap-codec": "^1.4.10",
+        "@jridgewell/trace-mapping": "^0.3.9"
       }
     },
-    "@typescript-eslint/types": {
-      "version": "4.33.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.33.0.tgz",
-      "integrity": "sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ==",
+    "@jridgewell/resolve-uri": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-1.0.0.tgz",
+      "integrity": "sha512-9oLAnygRMi8Q5QkYEU4XWK04B+nuoXoxjRvRxgjuChkLZFBja0YPSgdZ7dZtwhncLBcQe/I/E+fLuk5qxcYVJA==",
       "dev": true
     },
-    "@typescript-eslint/typescript-estree": {
-      "version": "4.33.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz",
-      "integrity": "sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA==",
-      "dev": true,
-      "requires": {
-        "@typescript-eslint/types": "4.33.0",
-        "@typescript-eslint/visitor-keys": "4.33.0",
-        "debug": "^4.3.1",
-        "globby": "^11.0.3",
-        "is-glob": "^4.0.1",
-        "semver": "^7.3.5",
-        "tsutils": "^3.21.0"
-      }
+    "@jridgewell/set-array": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.1.tgz",
+      "integrity": "sha512-Ct5MqZkLGEXTVmQYbGtx9SVqD2fqwvdubdps5D3djjAkgkKwT918VNOz65pEHFaYTeWcukmJmH5SwsA9Tn2ObQ==",
+      "dev": true
     },
-    "@typescript-eslint/visitor-keys": {
-      "version": "4.33.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.33.0.tgz",
-      "integrity": "sha512-uqi/2aSz9g2ftcHWf8uLPJA70rUv6yuMW5Bohw+bwcuzaxQIHaKFZCKGoGXIrc9vkTJ3+0txM73K0Hq3d5wgIg==",
+    "@jridgewell/sourcemap-codec": {
+      "version": "1.4.13",
+      "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.13.tgz",
+      "integrity": "sha512-GryiOJmNcWbovBxTfZSF71V/mXbgcV3MewDe3kIMCLyIh5e7SKAeUZs+rMnJ8jkMolZ/4/VsdBmMrw3l+VdZ3w==",
+      "dev": true
+    },
+    "@jridgewell/trace-mapping": {
+      "version": "0.3.13",
+      "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.13.tgz",
+      "integrity": "sha512-o1xbKhp9qnIAoHJSWd6KlCZfqslL4valSF81H8ImioOAxluWYWOpWkpyktY2vnt4tbrX9XYaxovq6cgowaJp2w==",
       "dev": true,
       "requires": {
-        "@typescript-eslint/types": "4.33.0",
-        "eslint-visitor-keys": "^2.0.0"
+        "@jridgewell/resolve-uri": "^3.0.3",
+        "@jridgewell/sourcemap-codec": "^1.4.10"
+      },
+      "dependencies": {
+        "@jridgewell/resolve-uri": {
+          "version": "3.0.7",
+          "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.7.tgz",
+          "integrity": "sha512-8cXDaBBHOr2pQ7j77Y6Vp5VDT2sIqWyWQ56TjEq4ih/a4iST3dItRe8Q9fp0rrIl9DoKhWQtUQz/YpOxLkXbNA==",
+          "dev": true
+        }
       }
     },
-    "@webassemblyjs/ast": {
-      "version": "1.11.1",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz",
-      "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==",
+    "@jsdevtools/coverage-istanbul-loader": {
+      "version": "3.0.5",
+      "resolved": "https://registry.npmjs.org/@jsdevtools/coverage-istanbul-loader/-/coverage-istanbul-loader-3.0.5.tgz",
+      "integrity": "sha512-EUCPEkaRPvmHjWAAZkWMT7JDzpw7FKB00WTISaiXsbNOd5hCHg77XLA8sLYLFDo1zepYLo2w7GstN8YBqRXZfA==",
       "dev": true,
       "requires": {
-        "@webassemblyjs/helper-numbers": "1.11.1",
-        "@webassemblyjs/helper-wasm-bytecode": "1.11.1"
+        "convert-source-map": "^1.7.0",
+        "istanbul-lib-instrument": "^4.0.3",
+        "loader-utils": "^2.0.0",
+        "merge-source-map": "^1.1.0",
+        "schema-utils": "^2.7.0"
+      },
+      "dependencies": {
+        "schema-utils": {
+          "version": "2.7.1",
+          "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz",
+          "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==",
+          "dev": true,
+          "requires": {
+            "@types/json-schema": "^7.0.5",
+            "ajv": "^6.12.4",
+            "ajv-keywords": "^3.5.2"
+          }
+        }
       }
     },
-    "@webassemblyjs/floating-point-hex-parser": {
-      "version": "1.11.1",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz",
-      "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==",
-      "dev": true
+    "@mdx-js/mdx": {
+      "version": "1.6.22",
+      "resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-1.6.22.tgz",
+      "integrity": "sha512-AMxuLxPz2j5/6TpF/XSdKpQP1NlG0z11dFOlq+2IP/lSgl11GY8ji6S/rgsViN/L0BDvHvUMruRb7ub+24LUYA==",
+      "dev": true,
+      "requires": {
+        "@babel/core": "7.12.9",
+        "@babel/plugin-syntax-jsx": "7.12.1",
+        "@babel/plugin-syntax-object-rest-spread": "7.8.3",
+        "@mdx-js/util": "1.6.22",
+        "babel-plugin-apply-mdx-type-prop": "1.6.22",
+        "babel-plugin-extract-import-names": "1.6.22",
+        "camelcase-css": "2.0.1",
+        "detab": "2.0.4",
+        "hast-util-raw": "6.0.1",
+        "lodash.uniq": "4.5.0",
+        "mdast-util-to-hast": "10.0.1",
+        "remark-footnotes": "2.0.0",
+        "remark-mdx": "1.6.22",
+        "remark-parse": "8.0.3",
+        "remark-squeeze-paragraphs": "4.0.0",
+        "style-to-object": "0.3.0",
+        "unified": "9.2.0",
+        "unist-builder": "2.0.3",
+        "unist-util-visit": "2.0.3"
+      },
+      "dependencies": {
+        "@babel/core": {
+          "version": "7.12.9",
+          "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.9.tgz",
+          "integrity": "sha512-gTXYh3M5wb7FRXQy+FErKFAv90BnlOuNn1QkCK2lREoPAjrQCO49+HVSrFoe5uakFAF5eenS75KbO2vQiLrTMQ==",
+          "dev": true,
+          "requires": {
+            "@babel/code-frame": "^7.10.4",
+            "@babel/generator": "^7.12.5",
+            "@babel/helper-module-transforms": "^7.12.1",
+            "@babel/helpers": "^7.12.5",
+            "@babel/parser": "^7.12.7",
+            "@babel/template": "^7.12.7",
+            "@babel/traverse": "^7.12.9",
+            "@babel/types": "^7.12.7",
+            "convert-source-map": "^1.7.0",
+            "debug": "^4.1.0",
+            "gensync": "^1.0.0-beta.1",
+            "json5": "^2.1.2",
+            "lodash": "^4.17.19",
+            "resolve": "^1.3.2",
+            "semver": "^5.4.1",
+            "source-map": "^0.5.0"
+          }
+        },
+        "@babel/plugin-syntax-jsx": {
+          "version": "7.12.1",
+          "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.12.1.tgz",
+          "integrity": "sha512-1yRi7yAtB0ETgxdY9ti/p2TivUxJkTdhu/ZbF9MshVGqOx1TdB3b7xCXs49Fupgg50N45KcAsRP/ZqWjs9SRjg==",
+          "dev": true,
+          "requires": {
+            "@babel/helper-plugin-utils": "^7.10.4"
+          }
+        },
+        "semver": {
+          "version": "5.7.1",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+          "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+          "dev": true
+        },
+        "source-map": {
+          "version": "0.5.7",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+          "dev": true
+        }
+      }
     },
-    "@webassemblyjs/helper-api-error": {
-      "version": "1.11.1",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz",
-      "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==",
+    "@mdx-js/react": {
+      "version": "1.6.22",
+      "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-1.6.22.tgz",
+      "integrity": "sha512-TDoPum4SHdfPiGSAaRBw7ECyI8VaHpK8GJugbJIJuqyh6kzw9ZLJZW3HGL3NNrJGxcAixUvqROm+YuQOo5eXtg==",
       "dev": true
     },
-    "@webassemblyjs/helper-buffer": {
-      "version": "1.11.1",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz",
-      "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==",
+    "@mdx-js/util": {
+      "version": "1.6.22",
+      "resolved": "https://registry.npmjs.org/@mdx-js/util/-/util-1.6.22.tgz",
+      "integrity": "sha512-H1rQc1ZOHANWBvPcW+JpGwr+juXSxM8Q8YCkm3GhZd8REu1fHR3z99CErO1p9pkcfcxZnMdIZdIsXkOHY0NilA==",
       "dev": true
     },
-    "@webassemblyjs/helper-numbers": {
-      "version": "1.11.1",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz",
-      "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==",
+    "@mrmlnc/readdir-enhanced": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz",
+      "integrity": "sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g==",
       "dev": true,
       "requires": {
-        "@webassemblyjs/floating-point-hex-parser": "1.11.1",
-        "@webassemblyjs/helper-api-error": "1.11.1",
-        "@xtuc/long": "4.2.2"
+        "call-me-maybe": "^1.0.1",
+        "glob-to-regexp": "^0.3.0"
+      },
+      "dependencies": {
+        "glob-to-regexp": {
+          "version": "0.3.0",
+          "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz",
+          "integrity": "sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs=",
+          "dev": true
+        }
       }
     },
-    "@webassemblyjs/helper-wasm-bytecode": {
-      "version": "1.11.1",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz",
-      "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==",
-      "dev": true
-    },
-    "@webassemblyjs/helper-wasm-section": {
-      "version": "1.11.1",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz",
-      "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==",
-      "dev": true,
+    "@ngrx/effects": {
+      "version": "12.5.1",
+      "resolved": "https://registry.npmjs.org/@ngrx/effects/-/effects-12.5.1.tgz",
+      "integrity": "sha512-fVNGIIntYLRWW1XWe0os2XOv03L22S4WTkX0OPZ9O6ztwuaNq0yzxWN7UeAC6H385F+g0k76KwRV78zHyP0bfQ==",
       "requires": {
-        "@webassemblyjs/ast": "1.11.1",
-        "@webassemblyjs/helper-buffer": "1.11.1",
-        "@webassemblyjs/helper-wasm-bytecode": "1.11.1",
-        "@webassemblyjs/wasm-gen": "1.11.1"
+        "tslib": "^2.0.0"
       }
     },
-    "@webassemblyjs/ieee754": {
-      "version": "1.11.1",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz",
-      "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==",
-      "dev": true,
+    "@ngrx/store": {
+      "version": "12.5.1",
+      "resolved": "https://registry.npmjs.org/@ngrx/store/-/store-12.5.1.tgz",
+      "integrity": "sha512-NLVkHLVeZc7IboXSDZlFoq1QrupmwYTYKRHS6se7ZasAv/lrIjHWsVVdICKSVRBsHZYu3+dmCXmu+YgulP7iHw==",
       "requires": {
-        "@xtuc/ieee754": "^1.2.0"
+        "tslib": "^2.0.0"
       }
     },
-    "@webassemblyjs/leb128": {
-      "version": "1.11.1",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz",
-      "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==",
+    "@ngtools/webpack": {
+      "version": "12.2.17",
+      "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-12.2.17.tgz",
+      "integrity": "sha512-uaS+2YZgPDW3VmUuwh4/yfIFV1KRVGWefc6xLWIqKRKs6mlRYs65m3ib9dX7CTS4kQMCbhxkxMbpBO2yXlzfvA==",
+      "dev": true
+    },
+    "@nodelib/fs.scandir": {
+      "version": "2.1.5",
+      "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+      "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
       "dev": true,
       "requires": {
-        "@xtuc/long": "4.2.2"
+        "@nodelib/fs.stat": "2.0.5",
+        "run-parallel": "^1.1.9"
       }
     },
-    "@webassemblyjs/utf8": {
-      "version": "1.11.1",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz",
-      "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==",
+    "@nodelib/fs.stat": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+      "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
       "dev": true
     },
-    "@webassemblyjs/wasm-edit": {
-      "version": "1.11.1",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz",
-      "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==",
+    "@nodelib/fs.walk": {
+      "version": "1.2.8",
+      "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+      "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
       "dev": true,
       "requires": {
-        "@webassemblyjs/ast": "1.11.1",
-        "@webassemblyjs/helper-buffer": "1.11.1",
-        "@webassemblyjs/helper-wasm-bytecode": "1.11.1",
-        "@webassemblyjs/helper-wasm-section": "1.11.1",
-        "@webassemblyjs/wasm-gen": "1.11.1",
-        "@webassemblyjs/wasm-opt": "1.11.1",
-        "@webassemblyjs/wasm-parser": "1.11.1",
-        "@webassemblyjs/wast-printer": "1.11.1"
+        "@nodelib/fs.scandir": "2.1.5",
+        "fastq": "^1.6.0"
       }
     },
-    "@webassemblyjs/wasm-gen": {
-      "version": "1.11.1",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz",
-      "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==",
-      "dev": true,
-      "requires": {
-        "@webassemblyjs/ast": "1.11.1",
-        "@webassemblyjs/helper-wasm-bytecode": "1.11.1",
-        "@webassemblyjs/ieee754": "1.11.1",
-        "@webassemblyjs/leb128": "1.11.1",
-        "@webassemblyjs/utf8": "1.11.1"
-      }
-    },
-    "@webassemblyjs/wasm-opt": {
-      "version": "1.11.1",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz",
-      "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==",
+    "@npmcli/git": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-2.1.0.tgz",
+      "integrity": "sha512-/hBFX/QG1b+N7PZBFs0bi+evgRZcK9nWBxQKZkGoXUT5hJSwl5c4d7y8/hm+NQZRPhQ67RzFaj5UM9YeyKoryw==",
       "dev": true,
       "requires": {
-        "@webassemblyjs/ast": "1.11.1",
-        "@webassemblyjs/helper-buffer": "1.11.1",
-        "@webassemblyjs/wasm-gen": "1.11.1",
-        "@webassemblyjs/wasm-parser": "1.11.1"
+        "@npmcli/promise-spawn": "^1.3.2",
+        "lru-cache": "^6.0.0",
+        "mkdirp": "^1.0.4",
+        "npm-pick-manifest": "^6.1.1",
+        "promise-inflight": "^1.0.1",
+        "promise-retry": "^2.0.1",
+        "semver": "^7.3.5",
+        "which": "^2.0.2"
+      },
+      "dependencies": {
+        "which": {
+          "version": "2.0.2",
+          "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+          "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+          "dev": true,
+          "requires": {
+            "isexe": "^2.0.0"
+          }
+        }
       }
     },
-    "@webassemblyjs/wasm-parser": {
-      "version": "1.11.1",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz",
-      "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==",
+    "@npmcli/installed-package-contents": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-1.0.7.tgz",
+      "integrity": "sha512-9rufe0wnJusCQoLpV9ZPKIVP55itrM5BxOXs10DmdbRfgWtHy1LDyskbwRnBghuB0PrF7pNPOqREVtpz4HqzKw==",
       "dev": true,
       "requires": {
-        "@webassemblyjs/ast": "1.11.1",
-        "@webassemblyjs/helper-api-error": "1.11.1",
-        "@webassemblyjs/helper-wasm-bytecode": "1.11.1",
-        "@webassemblyjs/ieee754": "1.11.1",
-        "@webassemblyjs/leb128": "1.11.1",
-        "@webassemblyjs/utf8": "1.11.1"
+        "npm-bundled": "^1.1.1",
+        "npm-normalize-package-bin": "^1.0.1"
       }
     },
-    "@webassemblyjs/wast-printer": {
-      "version": "1.11.1",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz",
-      "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==",
+    "@npmcli/move-file": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz",
+      "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==",
       "dev": true,
       "requires": {
-        "@webassemblyjs/ast": "1.11.1",
-        "@xtuc/long": "4.2.2"
+        "mkdirp": "^1.0.4",
+        "rimraf": "^3.0.2"
       }
     },
-    "@xtuc/ieee754": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
-      "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==",
-      "dev": true
-    },
-    "@xtuc/long": {
-      "version": "4.2.2",
-      "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz",
-      "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
-      "dev": true
-    },
-    "@yarnpkg/lockfile": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz",
-      "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==",
-      "dev": true
-    },
-    "abab": {
-      "version": "2.0.5",
-      "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz",
-      "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==",
-      "dev": true
-    },
-    "abbrev": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
-      "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
+    "@npmcli/node-gyp": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-1.0.3.tgz",
+      "integrity": "sha512-fnkhw+fmX65kiLqk6E3BFLXNC26rUhK90zVwe2yncPliVT/Qos3xjhTLE59Df8KnPlcwIERXKVlU1bXoUQ+liA==",
       "dev": true
     },
-    "accepts": {
-      "version": "1.3.7",
-      "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
-      "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
+    "@npmcli/promise-spawn": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-1.3.2.tgz",
+      "integrity": "sha512-QyAGYo/Fbj4MXeGdJcFzZ+FkDkomfRBrPM+9QYJSg+PxgAUL+LU3FneQk37rKR2/zjqkCV1BLHccX98wRXG3Sg==",
       "dev": true,
       "requires": {
-        "mime-types": "~2.1.24",
-        "negotiator": "0.6.2"
+        "infer-owner": "^1.0.4"
       }
     },
-    "acorn": {
-      "version": "8.6.0",
-      "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.6.0.tgz",
-      "integrity": "sha512-U1riIR+lBSNi3IbxtaHOIKdH8sLFv3NYfNv8sg7ZsNhcfl4HF2++BfqqrNAxoCLQW1iiylOj76ecnaUxz+z9yw=="
-    },
-    "acorn-import-assertions": {
-      "version": "1.8.0",
-      "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz",
-      "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==",
-      "dev": true
-    },
-    "acorn-jsx": {
-      "version": "5.3.2",
-      "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
-      "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
-      "dev": true
-    },
-    "adjust-sourcemap-loader": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz",
-      "integrity": "sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==",
+    "@npmcli/run-script": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-2.0.0.tgz",
+      "integrity": "sha512-fSan/Pu11xS/TdaTpTB0MRn9guwGU8dye+x56mEVgBEd/QsybBbYcAL0phPXi8SGWFEChkQd6M9qL4y6VOpFig==",
       "dev": true,
       "requires": {
-        "loader-utils": "^2.0.0",
-        "regex-parser": "^2.2.11"
+        "@npmcli/node-gyp": "^1.0.2",
+        "@npmcli/promise-spawn": "^1.3.2",
+        "node-gyp": "^8.2.0",
+        "read-package-json-fast": "^2.0.1"
       }
     },
-    "agent-base": {
-      "version": "6.0.2",
-      "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
-      "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+    "@schematics/angular": {
+      "version": "12.2.17",
+      "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-12.2.17.tgz",
+      "integrity": "sha512-HM/4KkQu944KL5ebhIyy1Ot5OV6prHNW7kmGeMVeQefLSbbfMQCHLa1psB9UU9BoahwGhUBvleLylNSitOBCgg==",
       "dev": true,
       "requires": {
-        "debug": "4"
+        "@angular-devkit/core": "12.2.17",
+        "@angular-devkit/schematics": "12.2.17",
+        "jsonc-parser": "3.0.0"
       }
     },
-    "agentkeepalive": {
-      "version": "4.1.4",
-      "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.1.4.tgz",
-      "integrity": "sha512-+V/rGa3EuU74H6wR04plBb7Ks10FbtUQgRj/FQOG7uUIEuaINI+AiqJR1k6t3SVNs7o7ZjIdus6706qqzVq8jQ==",
+    "@storybook/addon-actions": {
+      "version": "6.5.4",
+      "resolved": "https://registry.npmjs.org/@storybook/addon-actions/-/addon-actions-6.5.4.tgz",
+      "integrity": "sha512-O70+brZOW78LBjbKsppXy37NQj4r3b2LQh3zUkIE1n37nb0bYhewr39UlT1/VhfDD4QA6PsBOqp0Mum8/V0GgQ==",
       "dev": true,
       "requires": {
-        "debug": "^4.1.0",
-        "depd": "^1.1.2",
-        "humanize-ms": "^1.2.1"
+        "@storybook/addons": "6.5.4",
+        "@storybook/api": "6.5.4",
+        "@storybook/client-logger": "6.5.4",
+        "@storybook/components": "6.5.4",
+        "@storybook/core-events": "6.5.4",
+        "@storybook/csf": "0.0.2--canary.4566f4d.1",
+        "@storybook/theming": "6.5.4",
+        "core-js": "^3.8.2",
+        "fast-deep-equal": "^3.1.3",
+        "global": "^4.4.0",
+        "lodash": "^4.17.21",
+        "polished": "^4.2.2",
+        "prop-types": "^15.7.2",
+        "react-inspector": "^5.1.0",
+        "regenerator-runtime": "^0.13.7",
+        "telejson": "^6.0.8",
+        "ts-dedent": "^2.0.0",
+        "util-deprecate": "^1.0.2",
+        "uuid-browser": "^3.1.0"
+      }
+    },
+    "@storybook/addon-backgrounds": {
+      "version": "6.5.4",
+      "resolved": "https://registry.npmjs.org/@storybook/addon-backgrounds/-/addon-backgrounds-6.5.4.tgz",
+      "integrity": "sha512-lajT9N7VpBQRPHJF1iAGUIxJNuMwqZcMjijVAMoWX4gOdnGvjK7MpwoYbD2xAPXYrugJUGiYRG608d4PgPZT4w==",
+      "dev": true,
+      "requires": {
+        "@storybook/addons": "6.5.4",
+        "@storybook/api": "6.5.4",
+        "@storybook/client-logger": "6.5.4",
+        "@storybook/components": "6.5.4",
+        "@storybook/core-events": "6.5.4",
+        "@storybook/csf": "0.0.2--canary.4566f4d.1",
+        "@storybook/theming": "6.5.4",
+        "core-js": "^3.8.2",
+        "global": "^4.4.0",
+        "memoizerific": "^1.11.3",
+        "regenerator-runtime": "^0.13.7",
+        "ts-dedent": "^2.0.0",
+        "util-deprecate": "^1.0.2"
       }
     },
-    "aggregate-error": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
-      "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==",
+    "@storybook/addon-controls": {
+      "version": "6.5.4",
+      "resolved": "https://registry.npmjs.org/@storybook/addon-controls/-/addon-controls-6.5.4.tgz",
+      "integrity": "sha512-+2s++u1yXGs94BoxMVOOa4Ld0u6ylV5KdS9cWigyQz/OP3ZeoXDx6kCaJfYhEe/7Ccn59g11XjG4W8pjHMdb4A==",
       "dev": true,
       "requires": {
-        "clean-stack": "^2.0.0",
-        "indent-string": "^4.0.0"
-      }
-    },
-    "ajv": {
-      "version": "6.12.6",
-      "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
-      "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
-      "requires": {
-        "fast-deep-equal": "^3.1.1",
-        "fast-json-stable-stringify": "^2.0.0",
-        "json-schema-traverse": "^0.4.1",
-        "uri-js": "^4.2.2"
+        "@storybook/addons": "6.5.4",
+        "@storybook/api": "6.5.4",
+        "@storybook/client-logger": "6.5.4",
+        "@storybook/components": "6.5.4",
+        "@storybook/core-common": "6.5.4",
+        "@storybook/csf": "0.0.2--canary.4566f4d.1",
+        "@storybook/node-logger": "6.5.4",
+        "@storybook/store": "6.5.4",
+        "@storybook/theming": "6.5.4",
+        "core-js": "^3.8.2",
+        "lodash": "^4.17.21",
+        "ts-dedent": "^2.0.0"
+      }
+    },
+    "@storybook/addon-docs": {
+      "version": "6.5.4",
+      "resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-6.5.4.tgz",
+      "integrity": "sha512-Sqx8XJN760rbFGlNXywKG1QtG24X6gHgWXMEdfA1yy6FbH8vXu8p9VVm6KV3tHcb7o+ihSK7pObKtSXNdumD/A==",
+      "dev": true,
+      "requires": {
+        "@babel/plugin-transform-react-jsx": "^7.12.12",
+        "@babel/preset-env": "^7.12.11",
+        "@jest/transform": "^26.6.2",
+        "@mdx-js/react": "^1.6.22",
+        "@storybook/addons": "6.5.4",
+        "@storybook/api": "6.5.4",
+        "@storybook/components": "6.5.4",
+        "@storybook/core-common": "6.5.4",
+        "@storybook/core-events": "6.5.4",
+        "@storybook/csf": "0.0.2--canary.4566f4d.1",
+        "@storybook/docs-tools": "6.5.4",
+        "@storybook/mdx1-csf": "^0.0.1-canary.1.eed86d7.0",
+        "@storybook/node-logger": "6.5.4",
+        "@storybook/postinstall": "6.5.4",
+        "@storybook/preview-web": "6.5.4",
+        "@storybook/source-loader": "6.5.4",
+        "@storybook/store": "6.5.4",
+        "@storybook/theming": "6.5.4",
+        "babel-loader": "^8.0.0",
+        "core-js": "^3.8.2",
+        "fast-deep-equal": "^3.1.3",
+        "global": "^4.4.0",
+        "lodash": "^4.17.21",
+        "regenerator-runtime": "^0.13.7",
+        "remark-external-links": "^8.0.0",
+        "remark-slug": "^6.0.0",
+        "ts-dedent": "^2.0.0",
+        "util-deprecate": "^1.0.2"
       }
     },
-    "ajv-errors": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz",
-      "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==",
-      "dev": true
-    },
-    "ajv-formats": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.0.tgz",
-      "integrity": "sha512-USH2jBb+C/hIpwD2iRjp0pe0k+MvzG0mlSn/FIdCgQhUb9ALPRjt2KIQdfZDS9r0ZIeUAg7gOu9KL0PFqGqr5Q==",
-      "dev": true,
-      "requires": {
-        "ajv": "^8.0.0"
+    "@storybook/addon-essentials": {
+      "version": "6.5.4",
+      "resolved": "https://registry.npmjs.org/@storybook/addon-essentials/-/addon-essentials-6.5.4.tgz",
+      "integrity": "sha512-/ayPl274Tdrfz0YG0oSovkrXJNbstFm/Zoz7KO4wNMFgxTYes3kk+ruwkFIZegunBB8yR77cSzWHKqS9pFD8tA==",
+      "dev": true,
+      "requires": {
+        "@storybook/addon-actions": "6.5.4",
+        "@storybook/addon-backgrounds": "6.5.4",
+        "@storybook/addon-controls": "6.5.4",
+        "@storybook/addon-docs": "6.5.4",
+        "@storybook/addon-measure": "6.5.4",
+        "@storybook/addon-outline": "6.5.4",
+        "@storybook/addon-toolbars": "6.5.4",
+        "@storybook/addon-viewport": "6.5.4",
+        "@storybook/addons": "6.5.4",
+        "@storybook/api": "6.5.4",
+        "@storybook/core-common": "6.5.4",
+        "@storybook/node-logger": "6.5.4",
+        "core-js": "^3.8.2",
+        "regenerator-runtime": "^0.13.7",
+        "ts-dedent": "^2.0.0"
+      }
+    },
+    "@storybook/addon-interactions": {
+      "version": "6.5.4",
+      "resolved": "https://registry.npmjs.org/@storybook/addon-interactions/-/addon-interactions-6.5.4.tgz",
+      "integrity": "sha512-5gSpJfrrfYWY01zNCTPBTJvMdZh4q4hi9aFkB/DQPYhcLJ7EsUTGQmqEF2WBujhsD06k5Z2HjbDxKceHFgM27g==",
+      "dev": true,
+      "requires": {
+        "@devtools-ds/object-inspector": "^1.1.2",
+        "@storybook/addons": "6.5.4",
+        "@storybook/api": "6.5.4",
+        "@storybook/client-logger": "6.5.4",
+        "@storybook/components": "6.5.4",
+        "@storybook/core-common": "6.5.4",
+        "@storybook/core-events": "6.5.4",
+        "@storybook/csf": "0.0.2--canary.4566f4d.1",
+        "@storybook/instrumenter": "6.5.4",
+        "@storybook/theming": "6.5.4",
+        "core-js": "^3.8.2",
+        "global": "^4.4.0",
+        "jest-mock": "^27.0.6",
+        "polished": "^4.2.2",
+        "ts-dedent": "^2.2.0"
+      }
+    },
+    "@storybook/addon-links": {
+      "version": "6.5.4",
+      "resolved": "https://registry.npmjs.org/@storybook/addon-links/-/addon-links-6.5.4.tgz",
+      "integrity": "sha512-mJcg4Gpo7P6TFdyWpE2DQcMwSjfotPST3y713Ircgc+sYOouIPRdrxGpdvhLWWncsC5wK/z2xZyYtBWPNiKN/g==",
+      "dev": true,
+      "requires": {
+        "@storybook/addons": "6.5.4",
+        "@storybook/client-logger": "6.5.4",
+        "@storybook/core-events": "6.5.4",
+        "@storybook/csf": "0.0.2--canary.4566f4d.1",
+        "@storybook/router": "6.5.4",
+        "@types/qs": "^6.9.5",
+        "core-js": "^3.8.2",
+        "global": "^4.4.0",
+        "prop-types": "^15.7.2",
+        "qs": "^6.10.0",
+        "regenerator-runtime": "^0.13.7",
+        "ts-dedent": "^2.0.0"
+      }
+    },
+    "@storybook/addon-measure": {
+      "version": "6.5.4",
+      "resolved": "https://registry.npmjs.org/@storybook/addon-measure/-/addon-measure-6.5.4.tgz",
+      "integrity": "sha512-yucb2WnQMUyPo0cnbKKeIMp4pb18bU2zE8AFLm1GcQYJ56IfYD5EJbfpP7o3KpcMRRrZFjITggqhdU8DtncZkQ==",
+      "dev": true,
+      "requires": {
+        "@storybook/addons": "6.5.4",
+        "@storybook/api": "6.5.4",
+        "@storybook/client-logger": "6.5.4",
+        "@storybook/components": "6.5.4",
+        "@storybook/core-events": "6.5.4",
+        "@storybook/csf": "0.0.2--canary.4566f4d.1",
+        "core-js": "^3.8.2",
+        "global": "^4.4.0"
+      }
+    },
+    "@storybook/addon-outline": {
+      "version": "6.5.4",
+      "resolved": "https://registry.npmjs.org/@storybook/addon-outline/-/addon-outline-6.5.4.tgz",
+      "integrity": "sha512-Qj9KfPTllcT0CxHqfIvKUbTxwu2ujIq/YfguFBecSQCTGwYTyTmLHzS1vwFHyEoVPgILLnQfsBLdLs57TaGl+A==",
+      "dev": true,
+      "requires": {
+        "@storybook/addons": "6.5.4",
+        "@storybook/api": "6.5.4",
+        "@storybook/client-logger": "6.5.4",
+        "@storybook/components": "6.5.4",
+        "@storybook/core-events": "6.5.4",
+        "@storybook/csf": "0.0.2--canary.4566f4d.1",
+        "core-js": "^3.8.2",
+        "global": "^4.4.0",
+        "regenerator-runtime": "^0.13.7",
+        "ts-dedent": "^2.0.0"
+      }
+    },
+    "@storybook/addon-toolbars": {
+      "version": "6.5.4",
+      "resolved": "https://registry.npmjs.org/@storybook/addon-toolbars/-/addon-toolbars-6.5.4.tgz",
+      "integrity": "sha512-cK80/EjDJ+Hr7/lqsEqasyZGHx5+j9pVQRWWhTEDzURcAsxh4HFSw9VSOnDk+EAgHrlvjm+PX2xeaHfT9ezo6Q==",
+      "dev": true,
+      "requires": {
+        "@storybook/addons": "6.5.4",
+        "@storybook/api": "6.5.4",
+        "@storybook/client-logger": "6.5.4",
+        "@storybook/components": "6.5.4",
+        "@storybook/theming": "6.5.4",
+        "core-js": "^3.8.2",
+        "regenerator-runtime": "^0.13.7"
+      }
+    },
+    "@storybook/addon-viewport": {
+      "version": "6.5.4",
+      "resolved": "https://registry.npmjs.org/@storybook/addon-viewport/-/addon-viewport-6.5.4.tgz",
+      "integrity": "sha512-K0DrUTwiGY9IeTVUgwgl0boEYzn7QbYz34N7C5CMEUFWIBNehc7FJu/Rc+d2ltNz1w3jtnQPAFpt0FQ8ZYvVGg==",
+      "dev": true,
+      "requires": {
+        "@storybook/addons": "6.5.4",
+        "@storybook/api": "6.5.4",
+        "@storybook/client-logger": "6.5.4",
+        "@storybook/components": "6.5.4",
+        "@storybook/core-events": "6.5.4",
+        "@storybook/theming": "6.5.4",
+        "core-js": "^3.8.2",
+        "global": "^4.4.0",
+        "memoizerific": "^1.11.3",
+        "prop-types": "^15.7.2",
+        "regenerator-runtime": "^0.13.7"
+      }
+    },
+    "@storybook/addons": {
+      "version": "6.5.4",
+      "resolved": "https://registry.npmjs.org/@storybook/addons/-/addons-6.5.4.tgz",
+      "integrity": "sha512-biPtPQ80HwVJeJl6ghF0yFMWZ9apuh+oxCWezeb9t0lr7EB8MLbZAGz8PbRerwDzV+mW4ZNV2RSvvnDD5MH/zg==",
+      "dev": true,
+      "requires": {
+        "@storybook/api": "6.5.4",
+        "@storybook/channels": "6.5.4",
+        "@storybook/client-logger": "6.5.4",
+        "@storybook/core-events": "6.5.4",
+        "@storybook/csf": "0.0.2--canary.4566f4d.1",
+        "@storybook/router": "6.5.4",
+        "@storybook/theming": "6.5.4",
+        "@types/webpack-env": "^1.16.0",
+        "core-js": "^3.8.2",
+        "global": "^4.4.0",
+        "regenerator-runtime": "^0.13.7"
+      }
+    },
+    "@storybook/angular": {
+      "version": "6.5.4",
+      "resolved": "https://registry.npmjs.org/@storybook/angular/-/angular-6.5.4.tgz",
+      "integrity": "sha512-nA/R+K9BYf/Lteu2Jbzww32Selw7E/yEfs0jtVLIqkLCAf84StD4s5Gnqb2hdGi8f9dI8vZVdTXWlj+ChDo9gw==",
+      "dev": true,
+      "requires": {
+        "@storybook/addons": "6.5.4",
+        "@storybook/api": "6.5.4",
+        "@storybook/client-logger": "6.5.4",
+        "@storybook/core": "6.5.4",
+        "@storybook/core-common": "6.5.4",
+        "@storybook/core-events": "6.5.4",
+        "@storybook/csf": "0.0.2--canary.4566f4d.1",
+        "@storybook/docs-tools": "6.5.4",
+        "@storybook/node-logger": "6.5.4",
+        "@storybook/semver": "^7.3.2",
+        "@storybook/store": "6.5.4",
+        "@types/node": "^14.14.20 || ^16.0.0",
+        "@types/react": "^16.14.23",
+        "@types/react-dom": "^16.9.14",
+        "@types/webpack-env": "^1.16.0",
+        "autoprefixer": "^9.8.6",
+        "core-js": "^3.8.2",
+        "find-up": "^5.0.0",
+        "fork-ts-checker-webpack-plugin": "^4.1.6",
+        "global": "^4.4.0",
+        "nanoid": "^3.1.23",
+        "p-limit": "^3.1.0",
+        "postcss": "^7.0.36",
+        "postcss-loader": "^4.2.0",
+        "raw-loader": "^4.0.2",
+        "react": "^16.14.0",
+        "react-dom": "^16.14.0",
+        "read-pkg-up": "^7.0.1",
+        "regenerator-runtime": "^0.13.7",
+        "sass-loader": "^10.1.0",
+        "telejson": "^6.0.8",
+        "ts-dedent": "^2.0.0",
+        "ts-loader": "^8.0.14",
+        "tsconfig-paths-webpack-plugin": "^3.3.0",
+        "util-deprecate": "^1.0.2",
+        "webpack": ">=4.0.0 <6.0.0"
       },
       "dependencies": {
-        "ajv": {
-          "version": "8.8.2",
-          "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.8.2.tgz",
-          "integrity": "sha512-x9VuX+R/jcFj1DHo/fCp99esgGDWiHENrKxaCENuCxpoMCmAt/COCGVDwA7kleEpEzJjDnvh3yGoOuLu0Dtllw==",
+        "@storybook/semver": {
+          "version": "7.3.2",
+          "resolved": "https://registry.npmjs.org/@storybook/semver/-/semver-7.3.2.tgz",
+          "integrity": "sha512-SWeszlsiPsMI0Ps0jVNtH64cI5c0UF3f7KgjVKJoNP30crQ6wUSddY2hsdeczZXEKVJGEn50Q60flcGsQGIcrg==",
           "dev": true,
           "requires": {
-            "fast-deep-equal": "^3.1.1",
-            "json-schema-traverse": "^1.0.0",
-            "require-from-string": "^2.0.2",
-            "uri-js": "^4.2.2"
+            "core-js": "^3.6.5",
+            "find-up": "^4.1.0"
+          },
+          "dependencies": {
+            "find-up": {
+              "version": "4.1.0",
+              "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+              "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+              "dev": true,
+              "requires": {
+                "locate-path": "^5.0.0",
+                "path-exists": "^4.0.0"
+              }
+            }
           }
         },
-        "json-schema-traverse": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
-          "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+        "@types/node": {
+          "version": "16.11.36",
+          "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.36.tgz",
+          "integrity": "sha512-FR5QJe+TaoZ2GsMHkjuwoNabr+UrJNRr2HNOo+r/7vhcuntM6Ee/pRPOnRhhL2XE9OOvX9VLEq+BcXl3VjNoWA==",
           "dev": true
-        }
-      }
-    },
-    "ajv-keywords": {
-      "version": "3.5.2",
-      "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
-      "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ=="
-    },
-    "alphanum-sort": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz",
-      "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=",
-      "dev": true
-    },
-    "ansi-colors": {
-      "version": "4.1.1",
-      "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
-      "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==",
-      "dev": true
-    },
-    "ansi-escapes": {
-      "version": "4.3.2",
-      "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
-      "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==",
-      "dev": true,
-      "requires": {
-        "type-fest": "^0.21.3"
-      }
-    },
-    "ansi-html": {
-      "version": "0.0.7",
-      "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz",
-      "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=",
-      "dev": true
-    },
-    "ansi-regex": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
-      "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
-      "dev": true
-    },
-    "ansi-styles": {
-      "version": "3.2.1",
-      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
-      "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
-      "dev": true,
-      "requires": {
-        "color-convert": "^1.9.0"
-      }
-    },
-    "anymatch": {
-      "version": "3.1.2",
-      "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
-      "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
-      "dev": true,
-      "requires": {
-        "normalize-path": "^3.0.0",
-        "picomatch": "^2.0.4"
-      }
-    },
-    "aproba": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
-      "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
-      "dev": true
-    },
-    "are-we-there-yet": {
-      "version": "1.1.7",
-      "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz",
-      "integrity": "sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==",
-      "dev": true,
-      "requires": {
-        "delegates": "^1.0.0",
-        "readable-stream": "^2.0.6"
-      }
-    },
-    "argparse": {
-      "version": "1.0.10",
-      "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
-      "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
-      "dev": true,
-      "requires": {
-        "sprintf-js": "~1.0.2"
-      }
-    },
-    "arr-diff": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
-      "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=",
-      "dev": true
-    },
-    "arr-flatten": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz",
-      "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==",
-      "dev": true
-    },
-    "arr-union": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
-      "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=",
-      "dev": true
-    },
-    "array-flatten": {
-      "version": "2.1.2",
-      "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz",
-      "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==",
-      "dev": true
-    },
-    "array-union": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
-      "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
-      "dev": true
-    },
-    "array-uniq": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz",
-      "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=",
-      "dev": true
-    },
-    "array-unique": {
-      "version": "0.3.2",
-      "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
-      "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
-      "dev": true
-    },
-    "asn1": {
-      "version": "0.2.6",
-      "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
-      "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
-      "dev": true,
-      "requires": {
-        "safer-buffer": "~2.1.0"
-      }
-    },
-    "assert-plus": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
-      "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
-      "dev": true
-    },
-    "assign-symbols": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
-      "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=",
-      "dev": true
-    },
-    "astral-regex": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz",
-      "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==",
-      "dev": true
-    },
-    "async": {
-      "version": "2.6.3",
-      "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
-      "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
-      "dev": true,
-      "requires": {
-        "lodash": "^4.17.14"
-      }
-    },
-    "async-each": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz",
-      "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==",
-      "dev": true
-    },
-    "async-limiter": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
-      "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==",
-      "dev": true
-    },
-    "asynckit": {
-      "version": "0.4.0",
-      "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
-      "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
-      "dev": true
-    },
-    "atob": {
-      "version": "2.1.2",
-      "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
-      "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
-      "dev": true
-    },
-    "autoprefixer": {
-      "version": "9.8.8",
-      "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.8.tgz",
-      "integrity": "sha512-eM9d/swFopRt5gdJ7jrpCwgvEMIayITpojhkkSMRsFHYuH5bkSQ4p/9qTEHtmNudUZh22Tehu7I6CxAW0IXTKA==",
-      "dev": true,
-      "requires": {
-        "browserslist": "^4.12.0",
-        "caniuse-lite": "^1.0.30001109",
-        "normalize-range": "^0.1.2",
-        "num2fraction": "^1.2.2",
-        "picocolors": "^0.2.1",
-        "postcss": "^7.0.32",
-        "postcss-value-parser": "^4.1.0"
-      },
-      "dependencies": {
+        },
+        "@types/react": {
+          "version": "16.14.26",
+          "resolved": "https://registry.npmjs.org/@types/react/-/react-16.14.26.tgz",
+          "integrity": "sha512-c/5CYyciOO4XdFcNhZW1O2woVx86k4T+DO2RorHZL7EhitkNQgSD/SgpdZJAUJa/qjVgOmTM44gHkAdZSXeQuQ==",
+          "dev": true,
+          "requires": {
+            "@types/prop-types": "*",
+            "@types/scheduler": "*",
+            "csstype": "^3.0.2"
+          }
+        },
+        "braces": {
+          "version": "2.3.2",
+          "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
+          "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
+          "dev": true,
+          "requires": {
+            "arr-flatten": "^1.1.0",
+            "array-unique": "^0.3.2",
+            "extend-shallow": "^2.0.1",
+            "fill-range": "^4.0.0",
+            "isobject": "^3.0.1",
+            "repeat-element": "^1.1.2",
+            "snapdragon": "^0.8.1",
+            "snapdragon-node": "^2.0.1",
+            "split-string": "^3.0.2",
+            "to-regex": "^3.0.1"
+          },
+          "dependencies": {
+            "extend-shallow": {
+              "version": "2.0.1",
+              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+              "dev": true,
+              "requires": {
+                "is-extendable": "^0.1.0"
+              }
+            }
+          }
+        },
+        "fill-range": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
+          "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
+          "dev": true,
+          "requires": {
+            "extend-shallow": "^2.0.1",
+            "is-number": "^3.0.0",
+            "repeat-string": "^1.6.1",
+            "to-regex-range": "^2.1.0"
+          },
+          "dependencies": {
+            "extend-shallow": {
+              "version": "2.0.1",
+              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+              "dev": true,
+              "requires": {
+                "is-extendable": "^0.1.0"
+              }
+            }
+          }
+        },
+        "find-up": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+          "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+          "dev": true,
+          "requires": {
+            "locate-path": "^6.0.0",
+            "path-exists": "^4.0.0"
+          },
+          "dependencies": {
+            "locate-path": {
+              "version": "6.0.0",
+              "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+              "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+              "dev": true,
+              "requires": {
+                "p-locate": "^5.0.0"
+              }
+            }
+          }
+        },
+        "fork-ts-checker-webpack-plugin": {
+          "version": "4.1.6",
+          "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-4.1.6.tgz",
+          "integrity": "sha512-DUxuQaKoqfNne8iikd14SAkh5uw4+8vNifp6gmA73yYNS6ywLIWSLD/n/mBzHQRpW3J7rbATEakmiA8JvkTyZw==",
+          "dev": true,
+          "requires": {
+            "@babel/code-frame": "^7.5.5",
+            "chalk": "^2.4.1",
+            "micromatch": "^3.1.10",
+            "minimatch": "^3.0.4",
+            "semver": "^5.6.0",
+            "tapable": "^1.0.0",
+            "worker-rpc": "^0.1.0"
+          },
+          "dependencies": {
+            "semver": {
+              "version": "5.7.1",
+              "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+              "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+              "dev": true
+            }
+          }
+        },
+        "is-number": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
+          "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
+          "dev": true,
+          "requires": {
+            "kind-of": "^3.0.2"
+          },
+          "dependencies": {
+            "kind-of": {
+              "version": "3.2.2",
+              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+              "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+              "dev": true,
+              "requires": {
+                "is-buffer": "^1.1.5"
+              }
+            }
+          }
+        },
+        "micromatch": {
+          "version": "3.1.10",
+          "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
+          "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
+          "dev": true,
+          "requires": {
+            "arr-diff": "^4.0.0",
+            "array-unique": "^0.3.2",
+            "braces": "^2.3.1",
+            "define-property": "^2.0.2",
+            "extend-shallow": "^3.0.2",
+            "extglob": "^2.0.4",
+            "fragment-cache": "^0.2.1",
+            "kind-of": "^6.0.2",
+            "nanomatch": "^1.2.9",
+            "object.pick": "^1.3.0",
+            "regex-not": "^1.0.0",
+            "snapdragon": "^0.8.1",
+            "to-regex": "^3.0.2"
+          }
+        },
+        "p-limit": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+          "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+          "dev": true,
+          "requires": {
+            "yocto-queue": "^0.1.0"
+          }
+        },
+        "p-locate": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+          "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+          "dev": true,
+          "requires": {
+            "p-limit": "^3.0.2"
+          }
+        },
         "picocolors": {
           "version": "0.2.1",
           "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz",
@@ -2729,289 +3845,6673 @@
             "source-map": "^0.6.1"
           }
         },
+        "postcss-loader": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-4.3.0.tgz",
+          "integrity": "sha512-M/dSoIiNDOo8Rk0mUqoj4kpGq91gcxCfb9PoyZVdZ76/AuhxylHDYZblNE8o+EQ9AMSASeMFEKxZf5aU6wlx1Q==",
+          "dev": true,
+          "requires": {
+            "cosmiconfig": "^7.0.0",
+            "klona": "^2.0.4",
+            "loader-utils": "^2.0.0",
+            "schema-utils": "^3.0.0",
+            "semver": "^7.3.4"
+          }
+        },
+        "sass-loader": {
+          "version": "10.2.1",
+          "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-10.2.1.tgz",
+          "integrity": "sha512-RRvWl+3K2LSMezIsd008ErK4rk6CulIMSwrcc2aZvjymUgKo/vjXGp1rSWmfTUX7bblEOz8tst4wBwWtCGBqKA==",
+          "dev": true,
+          "requires": {
+            "klona": "^2.0.4",
+            "loader-utils": "^2.0.0",
+            "neo-async": "^2.6.2",
+            "schema-utils": "^3.0.0",
+            "semver": "^7.3.2"
+          }
+        },
         "source-map": {
           "version": "0.6.1",
           "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
           "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
           "dev": true
-        }
-      }
-    },
-    "aws-sign2": {
-      "version": "0.7.0",
-      "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
-      "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=",
-      "dev": true
-    },
-    "aws4": {
-      "version": "1.11.0",
-      "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz",
-      "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==",
-      "dev": true
-    },
-    "babel-loader": {
-      "version": "8.2.2",
-      "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.2.tgz",
-      "integrity": "sha512-JvTd0/D889PQBtUXJ2PXaKU/pjZDMtHA9V2ecm+eNRmmBCMR09a+fmpGTNwnJtFmFl5Ei7Vy47LjBb+L0wQ99g==",
-      "dev": true,
-      "requires": {
-        "find-cache-dir": "^3.3.1",
-        "loader-utils": "^1.4.0",
-        "make-dir": "^3.1.0",
-        "schema-utils": "^2.6.5"
-      },
-      "dependencies": {
-        "json5": {
-          "version": "1.0.1",
-          "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
-          "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
-          "dev": true,
-          "requires": {
-            "minimist": "^1.2.0"
-          }
         },
-        "loader-utils": {
-          "version": "1.4.0",
-          "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
-          "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==",
-          "dev": true,
-          "requires": {
-            "big.js": "^5.2.2",
-            "emojis-list": "^3.0.0",
-            "json5": "^1.0.1"
-          }
+        "tapable": {
+          "version": "1.1.3",
+          "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",
+          "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==",
+          "dev": true
         },
-        "schema-utils": {
-          "version": "2.7.1",
-          "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz",
-          "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==",
+        "to-regex-range": {
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
+          "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
           "dev": true,
           "requires": {
-            "@types/json-schema": "^7.0.5",
-            "ajv": "^6.12.4",
-            "ajv-keywords": "^3.5.2"
+            "is-number": "^3.0.0",
+            "repeat-string": "^1.6.1"
           }
         }
       }
     },
-    "babel-plugin-dynamic-import-node": {
-      "version": "2.3.3",
-      "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz",
-      "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==",
-      "dev": true,
-      "requires": {
-        "object.assign": "^4.1.0"
-      }
-    },
-    "babel-plugin-polyfill-corejs2": {
-      "version": "0.2.3",
-      "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.2.3.tgz",
-      "integrity": "sha512-NDZ0auNRzmAfE1oDDPW2JhzIMXUk+FFe2ICejmt5T4ocKgiQx3e0VCRx9NCAidcMtL2RUZaWtXnmjTCkx0tcbA==",
+    "@storybook/api": {
+      "version": "6.5.4",
+      "resolved": "https://registry.npmjs.org/@storybook/api/-/api-6.5.4.tgz",
+      "integrity": "sha512-nZAf7/6bcOMb6yDYsCr2rWZuKtGfHQhyVvlW7uD1uPVytQ0OHS/D215C2odTNvIRxTiG67dQ4zeXcZPHHar+qQ==",
       "dev": true,
       "requires": {
-        "@babel/compat-data": "^7.13.11",
-        "@babel/helper-define-polyfill-provider": "^0.2.4",
-        "semver": "^6.1.1"
+        "@storybook/channels": "6.5.4",
+        "@storybook/client-logger": "6.5.4",
+        "@storybook/core-events": "6.5.4",
+        "@storybook/csf": "0.0.2--canary.4566f4d.1",
+        "@storybook/router": "6.5.4",
+        "@storybook/semver": "^7.3.2",
+        "@storybook/theming": "6.5.4",
+        "core-js": "^3.8.2",
+        "fast-deep-equal": "^3.1.3",
+        "global": "^4.4.0",
+        "lodash": "^4.17.21",
+        "memoizerific": "^1.11.3",
+        "regenerator-runtime": "^0.13.7",
+        "store2": "^2.12.0",
+        "telejson": "^6.0.8",
+        "ts-dedent": "^2.0.0",
+        "util-deprecate": "^1.0.2"
       },
       "dependencies": {
-        "semver": {
-          "version": "6.3.0",
-          "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
-          "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
-          "dev": true
+        "@storybook/semver": {
+          "version": "7.3.2",
+          "resolved": "https://registry.npmjs.org/@storybook/semver/-/semver-7.3.2.tgz",
+          "integrity": "sha512-SWeszlsiPsMI0Ps0jVNtH64cI5c0UF3f7KgjVKJoNP30crQ6wUSddY2hsdeczZXEKVJGEn50Q60flcGsQGIcrg==",
+          "dev": true,
+          "requires": {
+            "core-js": "^3.6.5",
+            "find-up": "^4.1.0"
+          }
         }
       }
     },
-    "babel-plugin-polyfill-corejs3": {
-      "version": "0.2.5",
-      "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.2.5.tgz",
-      "integrity": "sha512-ninF5MQNwAX9Z7c9ED+H2pGt1mXdP4TqzlHKyPIYmJIYz0N+++uwdM7RnJukklhzJ54Q84vA4ZJkgs7lu5vqcw==",
-      "dev": true,
-      "requires": {
-        "@babel/helper-define-polyfill-provider": "^0.2.2",
-        "core-js-compat": "^3.16.2"
-      }
-    },
-    "babel-plugin-polyfill-regenerator": {
-      "version": "0.2.3",
-      "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.2.3.tgz",
-      "integrity": "sha512-JVE78oRZPKFIeUqFGrSORNzQnrDwZR16oiWeGM8ZyjBn2XAT5OjP+wXx5ESuo33nUsFUEJYjtklnsKbxW5L+7g==",
-      "dev": true,
-      "requires": {
-        "@babel/helper-define-polyfill-provider": "^0.2.4"
-      }
-    },
-    "balanced-match": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
-      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
-      "dev": true
-    },
-    "base": {
-      "version": "0.11.2",
-      "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz",
-      "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==",
-      "dev": true,
-      "requires": {
-        "cache-base": "^1.0.1",
-        "class-utils": "^0.3.5",
-        "component-emitter": "^1.2.1",
-        "define-property": "^1.0.0",
-        "isobject": "^3.0.1",
-        "mixin-deep": "^1.2.0",
-        "pascalcase": "^0.1.1"
+    "@storybook/builder-webpack4": {
+      "version": "6.5.4",
+      "resolved": "https://registry.npmjs.org/@storybook/builder-webpack4/-/builder-webpack4-6.5.4.tgz",
+      "integrity": "sha512-9AaSix850yOG7deAG8PDrAVNiDcTBm422Fjo9+OkONVN8tYIObW72U9kjwSrVL2HRMwu6Ikx939z1FBgA80NSw==",
+      "dev": true,
+      "requires": {
+        "@babel/core": "^7.12.10",
+        "@storybook/addons": "6.5.4",
+        "@storybook/api": "6.5.4",
+        "@storybook/channel-postmessage": "6.5.4",
+        "@storybook/channels": "6.5.4",
+        "@storybook/client-api": "6.5.4",
+        "@storybook/client-logger": "6.5.4",
+        "@storybook/components": "6.5.4",
+        "@storybook/core-common": "6.5.4",
+        "@storybook/core-events": "6.5.4",
+        "@storybook/node-logger": "6.5.4",
+        "@storybook/preview-web": "6.5.4",
+        "@storybook/router": "6.5.4",
+        "@storybook/semver": "^7.3.2",
+        "@storybook/store": "6.5.4",
+        "@storybook/theming": "6.5.4",
+        "@storybook/ui": "6.5.4",
+        "@types/node": "^14.0.10 || ^16.0.0",
+        "@types/webpack": "^4.41.26",
+        "autoprefixer": "^9.8.6",
+        "babel-loader": "^8.0.0",
+        "case-sensitive-paths-webpack-plugin": "^2.3.0",
+        "core-js": "^3.8.2",
+        "css-loader": "^3.6.0",
+        "file-loader": "^6.2.0",
+        "find-up": "^5.0.0",
+        "fork-ts-checker-webpack-plugin": "^4.1.6",
+        "glob": "^7.1.6",
+        "glob-promise": "^3.4.0",
+        "global": "^4.4.0",
+        "html-webpack-plugin": "^4.0.0",
+        "pnp-webpack-plugin": "1.6.4",
+        "postcss": "^7.0.36",
+        "postcss-flexbugs-fixes": "^4.2.1",
+        "postcss-loader": "^4.2.0",
+        "raw-loader": "^4.0.2",
+        "stable": "^0.1.8",
+        "style-loader": "^1.3.0",
+        "terser-webpack-plugin": "^4.2.3",
+        "ts-dedent": "^2.0.0",
+        "url-loader": "^4.1.1",
+        "util-deprecate": "^1.0.2",
+        "webpack": "4",
+        "webpack-dev-middleware": "^3.7.3",
+        "webpack-filter-warnings-plugin": "^1.2.1",
+        "webpack-hot-middleware": "^2.25.1",
+        "webpack-virtual-modules": "^0.2.2"
       },
       "dependencies": {
-        "define-property": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
-          "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+        "@storybook/semver": {
+          "version": "7.3.2",
+          "resolved": "https://registry.npmjs.org/@storybook/semver/-/semver-7.3.2.tgz",
+          "integrity": "sha512-SWeszlsiPsMI0Ps0jVNtH64cI5c0UF3f7KgjVKJoNP30crQ6wUSddY2hsdeczZXEKVJGEn50Q60flcGsQGIcrg==",
           "dev": true,
           "requires": {
-            "is-descriptor": "^1.0.0"
+            "core-js": "^3.6.5",
+            "find-up": "^4.1.0"
+          },
+          "dependencies": {
+            "find-up": {
+              "version": "4.1.0",
+              "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+              "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+              "dev": true,
+              "requires": {
+                "locate-path": "^5.0.0",
+                "path-exists": "^4.0.0"
+              }
+            }
           }
         },
-        "is-accessor-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
-          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+        "@types/node": {
+          "version": "16.11.36",
+          "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.36.tgz",
+          "integrity": "sha512-FR5QJe+TaoZ2GsMHkjuwoNabr+UrJNRr2HNOo+r/7vhcuntM6Ee/pRPOnRhhL2XE9OOvX9VLEq+BcXl3VjNoWA==",
+          "dev": true
+        },
+        "@webassemblyjs/ast": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz",
+          "integrity": "sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA==",
           "dev": true,
           "requires": {
-            "kind-of": "^6.0.0"
+            "@webassemblyjs/helper-module-context": "1.9.0",
+            "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
+            "@webassemblyjs/wast-parser": "1.9.0"
           }
         },
-        "is-data-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
-          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+        "@webassemblyjs/helper-api-error": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz",
+          "integrity": "sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw==",
+          "dev": true
+        },
+        "@webassemblyjs/helper-buffer": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz",
+          "integrity": "sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA==",
+          "dev": true
+        },
+        "@webassemblyjs/helper-wasm-bytecode": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz",
+          "integrity": "sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw==",
+          "dev": true
+        },
+        "@webassemblyjs/helper-wasm-section": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz",
+          "integrity": "sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw==",
           "dev": true,
           "requires": {
-            "kind-of": "^6.0.0"
+            "@webassemblyjs/ast": "1.9.0",
+            "@webassemblyjs/helper-buffer": "1.9.0",
+            "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
+            "@webassemblyjs/wasm-gen": "1.9.0"
           }
         },
-        "is-descriptor": {
-          "version": "1.0.2",
-          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
-          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+        "@webassemblyjs/ieee754": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz",
+          "integrity": "sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg==",
           "dev": true,
           "requires": {
-            "is-accessor-descriptor": "^1.0.0",
-            "is-data-descriptor": "^1.0.0",
-            "kind-of": "^6.0.2"
+            "@xtuc/ieee754": "^1.2.0"
           }
-        }
-      }
-    },
-    "base64-js": {
-      "version": "1.5.1",
-      "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
-      "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
-      "dev": true
-    },
-    "base64id": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
-      "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==",
-      "dev": true
-    },
-    "batch": {
-      "version": "0.6.1",
-      "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz",
-      "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=",
-      "dev": true
-    },
-    "bcrypt-pbkdf": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
-      "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
-      "dev": true,
-      "requires": {
-        "tweetnacl": "^0.14.3"
-      }
-    },
-    "big.js": {
-      "version": "5.2.2",
-      "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
-      "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ=="
-    },
-    "binary-extensions": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
-      "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
-      "dev": true
-    },
-    "bl": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
-      "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
-      "dev": true,
-      "requires": {
-        "buffer": "^5.5.0",
-        "inherits": "^2.0.4",
-        "readable-stream": "^3.4.0"
-      },
-      "dependencies": {
-        "readable-stream": {
-          "version": "3.6.0",
-          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
-          "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+        },
+        "@webassemblyjs/leb128": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.9.0.tgz",
+          "integrity": "sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw==",
           "dev": true,
           "requires": {
-            "inherits": "^2.0.3",
-            "string_decoder": "^1.1.1",
-            "util-deprecate": "^1.0.1"
+            "@xtuc/long": "4.2.2"
           }
-        }
-      }
-    },
-    "body-parser": {
-      "version": "1.19.0",
-      "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
-      "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
-      "dev": true,
-      "requires": {
-        "bytes": "3.1.0",
-        "content-type": "~1.0.4",
-        "debug": "2.6.9",
-        "depd": "~1.1.2",
-        "http-errors": "1.7.2",
-        "iconv-lite": "0.4.24",
-        "on-finished": "~2.3.0",
-        "qs": "6.7.0",
-        "raw-body": "2.4.0",
-        "type-is": "~1.6.17"
-      },
-      "dependencies": {
-        "bytes": {
-          "version": "3.1.0",
-          "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
-          "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==",
+        },
+        "@webassemblyjs/utf8": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.9.0.tgz",
+          "integrity": "sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w==",
           "dev": true
         },
-        "debug": {
-          "version": "2.6.9",
-          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
-          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+        "@webassemblyjs/wasm-edit": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz",
+          "integrity": "sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw==",
           "dev": true,
           "requires": {
-            "ms": "2.0.0"
+            "@webassemblyjs/ast": "1.9.0",
+            "@webassemblyjs/helper-buffer": "1.9.0",
+            "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
+            "@webassemblyjs/helper-wasm-section": "1.9.0",
+            "@webassemblyjs/wasm-gen": "1.9.0",
+            "@webassemblyjs/wasm-opt": "1.9.0",
+            "@webassemblyjs/wasm-parser": "1.9.0",
+            "@webassemblyjs/wast-printer": "1.9.0"
           }
         },
-        "ms": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+        "@webassemblyjs/wasm-gen": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz",
+          "integrity": "sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA==",
+          "dev": true,
+          "requires": {
+            "@webassemblyjs/ast": "1.9.0",
+            "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
+            "@webassemblyjs/ieee754": "1.9.0",
+            "@webassemblyjs/leb128": "1.9.0",
+            "@webassemblyjs/utf8": "1.9.0"
+          }
+        },
+        "@webassemblyjs/wasm-opt": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz",
+          "integrity": "sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A==",
+          "dev": true,
+          "requires": {
+            "@webassemblyjs/ast": "1.9.0",
+            "@webassemblyjs/helper-buffer": "1.9.0",
+            "@webassemblyjs/wasm-gen": "1.9.0",
+            "@webassemblyjs/wasm-parser": "1.9.0"
+          }
+        },
+        "@webassemblyjs/wasm-parser": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz",
+          "integrity": "sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA==",
+          "dev": true,
+          "requires": {
+            "@webassemblyjs/ast": "1.9.0",
+            "@webassemblyjs/helper-api-error": "1.9.0",
+            "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
+            "@webassemblyjs/ieee754": "1.9.0",
+            "@webassemblyjs/leb128": "1.9.0",
+            "@webassemblyjs/utf8": "1.9.0"
+          }
+        },
+        "@webassemblyjs/wast-printer": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz",
+          "integrity": "sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA==",
+          "dev": true,
+          "requires": {
+            "@webassemblyjs/ast": "1.9.0",
+            "@webassemblyjs/wast-parser": "1.9.0",
+            "@xtuc/long": "4.2.2"
+          }
+        },
+        "acorn": {
+          "version": "6.4.2",
+          "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz",
+          "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==",
           "dev": true
-        }
-      }
-    },
-    "bonjour": {
-      "version": "3.5.0",
-      "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz",
-      "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=",
-      "dev": true,
-      "requires": {
-        "array-flatten": "^2.1.0",
+        },
+        "braces": {
+          "version": "2.3.2",
+          "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
+          "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
+          "dev": true,
+          "requires": {
+            "arr-flatten": "^1.1.0",
+            "array-unique": "^0.3.2",
+            "extend-shallow": "^2.0.1",
+            "fill-range": "^4.0.0",
+            "isobject": "^3.0.1",
+            "repeat-element": "^1.1.2",
+            "snapdragon": "^0.8.1",
+            "snapdragon-node": "^2.0.1",
+            "split-string": "^3.0.2",
+            "to-regex": "^3.0.1"
+          },
+          "dependencies": {
+            "extend-shallow": {
+              "version": "2.0.1",
+              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+              "dev": true,
+              "requires": {
+                "is-extendable": "^0.1.0"
+              }
+            }
+          }
+        },
+        "chownr": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
+          "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
+          "dev": true
+        },
+        "commander": {
+          "version": "2.20.3",
+          "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+          "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+          "dev": true
+        },
+        "css-loader": {
+          "version": "3.6.0",
+          "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-3.6.0.tgz",
+          "integrity": "sha512-M5lSukoWi1If8dhQAUCvj4H8vUt3vOnwbQBH9DdTm/s4Ym2B/3dPMtYZeJmq7Q3S3Pa+I94DcZ7pc9bP14cWIQ==",
+          "dev": true,
+          "requires": {
+            "camelcase": "^5.3.1",
+            "cssesc": "^3.0.0",
+            "icss-utils": "^4.1.1",
+            "loader-utils": "^1.2.3",
+            "normalize-path": "^3.0.0",
+            "postcss": "^7.0.32",
+            "postcss-modules-extract-imports": "^2.0.0",
+            "postcss-modules-local-by-default": "^3.0.2",
+            "postcss-modules-scope": "^2.2.0",
+            "postcss-modules-values": "^3.0.0",
+            "postcss-value-parser": "^4.1.0",
+            "schema-utils": "^2.7.0",
+            "semver": "^6.3.0"
+          },
+          "dependencies": {
+            "semver": {
+              "version": "6.3.0",
+              "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+              "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+              "dev": true
+            }
+          }
+        },
+        "enhanced-resolve": {
+          "version": "4.5.0",
+          "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz",
+          "integrity": "sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg==",
+          "dev": true,
+          "requires": {
+            "graceful-fs": "^4.1.2",
+            "memory-fs": "^0.5.0",
+            "tapable": "^1.0.0"
+          },
+          "dependencies": {
+            "memory-fs": {
+              "version": "0.5.0",
+              "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz",
+              "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==",
+              "dev": true,
+              "requires": {
+                "errno": "^0.1.3",
+                "readable-stream": "^2.0.1"
+              }
+            }
+          }
+        },
+        "eslint-scope": {
+          "version": "4.0.3",
+          "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz",
+          "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==",
+          "dev": true,
+          "requires": {
+            "esrecurse": "^4.1.0",
+            "estraverse": "^4.1.1"
+          }
+        },
+        "fill-range": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
+          "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
+          "dev": true,
+          "requires": {
+            "extend-shallow": "^2.0.1",
+            "is-number": "^3.0.0",
+            "repeat-string": "^1.6.1",
+            "to-regex-range": "^2.1.0"
+          },
+          "dependencies": {
+            "extend-shallow": {
+              "version": "2.0.1",
+              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+              "dev": true,
+              "requires": {
+                "is-extendable": "^0.1.0"
+              }
+            }
+          }
+        },
+        "find-up": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+          "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+          "dev": true,
+          "requires": {
+            "locate-path": "^6.0.0",
+            "path-exists": "^4.0.0"
+          },
+          "dependencies": {
+            "locate-path": {
+              "version": "6.0.0",
+              "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+              "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+              "dev": true,
+              "requires": {
+                "p-locate": "^5.0.0"
+              }
+            }
+          }
+        },
+        "fork-ts-checker-webpack-plugin": {
+          "version": "4.1.6",
+          "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-4.1.6.tgz",
+          "integrity": "sha512-DUxuQaKoqfNne8iikd14SAkh5uw4+8vNifp6gmA73yYNS6ywLIWSLD/n/mBzHQRpW3J7rbATEakmiA8JvkTyZw==",
+          "dev": true,
+          "requires": {
+            "@babel/code-frame": "^7.5.5",
+            "chalk": "^2.4.1",
+            "micromatch": "^3.1.10",
+            "minimatch": "^3.0.4",
+            "semver": "^5.6.0",
+            "tapable": "^1.0.0",
+            "worker-rpc": "^0.1.0"
+          },
+          "dependencies": {
+            "semver": {
+              "version": "5.7.1",
+              "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+              "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+              "dev": true
+            }
+          }
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
+        "icss-utils": {
+          "version": "4.1.1",
+          "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-4.1.1.tgz",
+          "integrity": "sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA==",
+          "dev": true,
+          "requires": {
+            "postcss": "^7.0.14"
+          }
+        },
+        "is-number": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
+          "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
+          "dev": true,
+          "requires": {
+            "kind-of": "^3.0.2"
+          },
+          "dependencies": {
+            "kind-of": {
+              "version": "3.2.2",
+              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+              "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+              "dev": true,
+              "requires": {
+                "is-buffer": "^1.1.5"
+              }
+            }
+          }
+        },
+        "is-wsl": {
+          "version": "1.1.0",
+          "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz",
+          "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=",
+          "dev": true
+        },
+        "jest-worker": {
+          "version": "26.6.2",
+          "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz",
+          "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==",
+          "dev": true,
+          "requires": {
+            "@types/node": "*",
+            "merge-stream": "^2.0.0",
+            "supports-color": "^7.0.0"
+          }
+        },
+        "json5": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
+          "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
+          "dev": true,
+          "requires": {
+            "minimist": "^1.2.0"
+          }
+        },
+        "loader-runner": {
+          "version": "2.4.0",
+          "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz",
+          "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==",
+          "dev": true
+        },
+        "loader-utils": {
+          "version": "1.4.0",
+          "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
+          "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==",
+          "dev": true,
+          "requires": {
+            "big.js": "^5.2.2",
+            "emojis-list": "^3.0.0",
+            "json5": "^1.0.1"
+          }
+        },
+        "lru-cache": {
+          "version": "5.1.1",
+          "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+          "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+          "dev": true,
+          "requires": {
+            "yallist": "^3.0.2"
+          }
+        },
+        "make-dir": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
+          "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
+          "dev": true,
+          "requires": {
+            "pify": "^4.0.1",
+            "semver": "^5.6.0"
+          },
+          "dependencies": {
+            "semver": {
+              "version": "5.7.1",
+              "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+              "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+              "dev": true
+            }
+          }
+        },
+        "micromatch": {
+          "version": "3.1.10",
+          "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
+          "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
+          "dev": true,
+          "requires": {
+            "arr-diff": "^4.0.0",
+            "array-unique": "^0.3.2",
+            "braces": "^2.3.1",
+            "define-property": "^2.0.2",
+            "extend-shallow": "^3.0.2",
+            "extglob": "^2.0.4",
+            "fragment-cache": "^0.2.1",
+            "kind-of": "^6.0.2",
+            "nanomatch": "^1.2.9",
+            "object.pick": "^1.3.0",
+            "regex-not": "^1.0.0",
+            "snapdragon": "^0.8.1",
+            "to-regex": "^3.0.2"
+          }
+        },
+        "mime": {
+          "version": "2.6.0",
+          "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz",
+          "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==",
+          "dev": true
+        },
+        "mkdirp": {
+          "version": "0.5.6",
+          "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
+          "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
+          "dev": true,
+          "requires": {
+            "minimist": "^1.2.6"
+          }
+        },
+        "p-limit": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+          "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+          "dev": true,
+          "requires": {
+            "yocto-queue": "^0.1.0"
+          }
+        },
+        "p-locate": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+          "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+          "dev": true,
+          "requires": {
+            "p-limit": "^3.0.2"
+          }
+        },
+        "picocolors": {
+          "version": "0.2.1",
+          "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz",
+          "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==",
+          "dev": true
+        },
+        "pkg-dir": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz",
+          "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==",
+          "dev": true,
+          "requires": {
+            "find-up": "^3.0.0"
+          },
+          "dependencies": {
+            "find-up": {
+              "version": "3.0.0",
+              "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
+              "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+              "dev": true,
+              "requires": {
+                "locate-path": "^3.0.0"
+              }
+            },
+            "locate-path": {
+              "version": "3.0.0",
+              "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
+              "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+              "dev": true,
+              "requires": {
+                "p-locate": "^3.0.0",
+                "path-exists": "^3.0.0"
+              }
+            },
+            "p-limit": {
+              "version": "2.3.0",
+              "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+              "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+              "dev": true,
+              "requires": {
+                "p-try": "^2.0.0"
+              }
+            },
+            "p-locate": {
+              "version": "3.0.0",
+              "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
+              "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+              "dev": true,
+              "requires": {
+                "p-limit": "^2.0.0"
+              }
+            },
+            "path-exists": {
+              "version": "3.0.0",
+              "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+              "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
+              "dev": true
+            }
+          }
+        },
+        "postcss": {
+          "version": "7.0.39",
+          "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz",
+          "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==",
+          "dev": true,
+          "requires": {
+            "picocolors": "^0.2.1",
+            "source-map": "^0.6.1"
+          }
+        },
+        "postcss-loader": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-4.3.0.tgz",
+          "integrity": "sha512-M/dSoIiNDOo8Rk0mUqoj4kpGq91gcxCfb9PoyZVdZ76/AuhxylHDYZblNE8o+EQ9AMSASeMFEKxZf5aU6wlx1Q==",
+          "dev": true,
+          "requires": {
+            "cosmiconfig": "^7.0.0",
+            "klona": "^2.0.4",
+            "loader-utils": "^2.0.0",
+            "schema-utils": "^3.0.0",
+            "semver": "^7.3.4"
+          },
+          "dependencies": {
+            "json5": {
+              "version": "2.2.1",
+              "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz",
+              "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==",
+              "dev": true
+            },
+            "loader-utils": {
+              "version": "2.0.2",
+              "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz",
+              "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==",
+              "dev": true,
+              "requires": {
+                "big.js": "^5.2.2",
+                "emojis-list": "^3.0.0",
+                "json5": "^2.1.2"
+              }
+            },
+            "schema-utils": {
+              "version": "3.1.1",
+              "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz",
+              "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==",
+              "dev": true,
+              "requires": {
+                "@types/json-schema": "^7.0.8",
+                "ajv": "^6.12.5",
+                "ajv-keywords": "^3.5.2"
+              }
+            }
+          }
+        },
+        "postcss-modules-extract-imports": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz",
+          "integrity": "sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ==",
+          "dev": true,
+          "requires": {
+            "postcss": "^7.0.5"
+          }
+        },
+        "postcss-modules-local-by-default": {
+          "version": "3.0.3",
+          "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.3.tgz",
+          "integrity": "sha512-e3xDq+LotiGesympRlKNgaJ0PCzoUIdpH0dj47iWAui/kyTgh3CiAr1qP54uodmJhl6p9rN6BoNcdEDVJx9RDw==",
+          "dev": true,
+          "requires": {
+            "icss-utils": "^4.1.1",
+            "postcss": "^7.0.32",
+            "postcss-selector-parser": "^6.0.2",
+            "postcss-value-parser": "^4.1.0"
+          }
+        },
+        "postcss-modules-scope": {
+          "version": "2.2.0",
+          "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-2.2.0.tgz",
+          "integrity": "sha512-YyEgsTMRpNd+HmyC7H/mh3y+MeFWevy7V1evVhJWewmMbjDHIbZbOXICC2y+m1xI1UVfIT1HMW/O04Hxyu9oXQ==",
+          "dev": true,
+          "requires": {
+            "postcss": "^7.0.6",
+            "postcss-selector-parser": "^6.0.0"
+          }
+        },
+        "postcss-modules-values": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-3.0.0.tgz",
+          "integrity": "sha512-1//E5jCBrZ9DmRX+zCtmQtRSV6PV42Ix7Bzj9GbwJceduuf7IqP8MgeTXuRDHOWj2m0VzZD5+roFWDuU8RQjcg==",
+          "dev": true,
+          "requires": {
+            "icss-utils": "^4.0.0",
+            "postcss": "^7.0.6"
+          }
+        },
+        "rimraf": {
+          "version": "2.7.1",
+          "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+          "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+          "dev": true,
+          "requires": {
+            "glob": "^7.1.3"
+          }
+        },
+        "schema-utils": {
+          "version": "2.7.1",
+          "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz",
+          "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==",
+          "dev": true,
+          "requires": {
+            "@types/json-schema": "^7.0.5",
+            "ajv": "^6.12.4",
+            "ajv-keywords": "^3.5.2"
+          }
+        },
+        "serialize-javascript": {
+          "version": "5.0.1",
+          "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz",
+          "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==",
+          "dev": true,
+          "requires": {
+            "randombytes": "^2.1.0"
+          }
+        },
+        "source-map": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+          "dev": true
+        },
+        "ssri": {
+          "version": "6.0.2",
+          "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.2.tgz",
+          "integrity": "sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==",
+          "dev": true,
+          "requires": {
+            "figgy-pudding": "^3.5.1"
+          }
+        },
+        "style-loader": {
+          "version": "1.3.0",
+          "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-1.3.0.tgz",
+          "integrity": "sha512-V7TCORko8rs9rIqkSrlMfkqA63DfoGBBJmK1kKGCcSi+BWb4cqz0SRsnp4l6rU5iwOEd0/2ePv68SV22VXon4Q==",
+          "dev": true,
+          "requires": {
+            "loader-utils": "^2.0.0",
+            "schema-utils": "^2.7.0"
+          },
+          "dependencies": {
+            "json5": {
+              "version": "2.2.1",
+              "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz",
+              "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==",
+              "dev": true
+            },
+            "loader-utils": {
+              "version": "2.0.2",
+              "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz",
+              "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==",
+              "dev": true,
+              "requires": {
+                "big.js": "^5.2.2",
+                "emojis-list": "^3.0.0",
+                "json5": "^2.1.2"
+              }
+            }
+          }
+        },
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
+        },
+        "tapable": {
+          "version": "1.1.3",
+          "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",
+          "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==",
+          "dev": true
+        },
+        "terser-webpack-plugin": {
+          "version": "4.2.3",
+          "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-4.2.3.tgz",
+          "integrity": "sha512-jTgXh40RnvOrLQNgIkwEKnQ8rmHjHK4u+6UBEi+W+FPmvb+uo+chJXntKe7/3lW5mNysgSWD60KyesnhW8D6MQ==",
+          "dev": true,
+          "requires": {
+            "cacache": "^15.0.5",
+            "find-cache-dir": "^3.3.1",
+            "jest-worker": "^26.5.0",
+            "p-limit": "^3.0.2",
+            "schema-utils": "^3.0.0",
+            "serialize-javascript": "^5.0.1",
+            "source-map": "^0.6.1",
+            "terser": "^5.3.4",
+            "webpack-sources": "^1.4.3"
+          },
+          "dependencies": {
+            "schema-utils": {
+              "version": "3.1.1",
+              "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz",
+              "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==",
+              "dev": true,
+              "requires": {
+                "@types/json-schema": "^7.0.8",
+                "ajv": "^6.12.5",
+                "ajv-keywords": "^3.5.2"
+              }
+            }
+          }
+        },
+        "to-regex-range": {
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
+          "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
+          "dev": true,
+          "requires": {
+            "is-number": "^3.0.0",
+            "repeat-string": "^1.6.1"
+          }
+        },
+        "watchpack": {
+          "version": "1.7.5",
+          "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz",
+          "integrity": "sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==",
+          "dev": true,
+          "requires": {
+            "chokidar": "^3.4.1",
+            "graceful-fs": "^4.1.2",
+            "neo-async": "^2.5.0",
+            "watchpack-chokidar2": "^2.0.1"
+          }
+        },
+        "webpack": {
+          "version": "4.46.0",
+          "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.46.0.tgz",
+          "integrity": "sha512-6jJuJjg8znb/xRItk7bkT0+Q7AHCYjjFnvKIWQPkNIOyRqoCGvkOs0ipeQzrqz4l5FtN5ZI/ukEHroeX/o1/5Q==",
+          "dev": true,
+          "requires": {
+            "@webassemblyjs/ast": "1.9.0",
+            "@webassemblyjs/helper-module-context": "1.9.0",
+            "@webassemblyjs/wasm-edit": "1.9.0",
+            "@webassemblyjs/wasm-parser": "1.9.0",
+            "acorn": "^6.4.1",
+            "ajv": "^6.10.2",
+            "ajv-keywords": "^3.4.1",
+            "chrome-trace-event": "^1.0.2",
+            "enhanced-resolve": "^4.5.0",
+            "eslint-scope": "^4.0.3",
+            "json-parse-better-errors": "^1.0.2",
+            "loader-runner": "^2.4.0",
+            "loader-utils": "^1.2.3",
+            "memory-fs": "^0.4.1",
+            "micromatch": "^3.1.10",
+            "mkdirp": "^0.5.3",
+            "neo-async": "^2.6.1",
+            "node-libs-browser": "^2.2.1",
+            "schema-utils": "^1.0.0",
+            "tapable": "^1.1.3",
+            "terser-webpack-plugin": "^1.4.3",
+            "watchpack": "^1.7.4",
+            "webpack-sources": "^1.4.1"
+          },
+          "dependencies": {
+            "cacache": {
+              "version": "12.0.4",
+              "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz",
+              "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==",
+              "dev": true,
+              "requires": {
+                "bluebird": "^3.5.5",
+                "chownr": "^1.1.1",
+                "figgy-pudding": "^3.5.1",
+                "glob": "^7.1.4",
+                "graceful-fs": "^4.1.15",
+                "infer-owner": "^1.0.3",
+                "lru-cache": "^5.1.1",
+                "mississippi": "^3.0.0",
+                "mkdirp": "^0.5.1",
+                "move-concurrently": "^1.0.1",
+                "promise-inflight": "^1.0.1",
+                "rimraf": "^2.6.3",
+                "ssri": "^6.0.1",
+                "unique-filename": "^1.1.1",
+                "y18n": "^4.0.0"
+              }
+            },
+            "find-cache-dir": {
+              "version": "2.1.0",
+              "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz",
+              "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==",
+              "dev": true,
+              "requires": {
+                "commondir": "^1.0.1",
+                "make-dir": "^2.0.0",
+                "pkg-dir": "^3.0.0"
+              }
+            },
+            "schema-utils": {
+              "version": "1.0.0",
+              "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
+              "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
+              "dev": true,
+              "requires": {
+                "ajv": "^6.1.0",
+                "ajv-errors": "^1.0.0",
+                "ajv-keywords": "^3.1.0"
+              }
+            },
+            "serialize-javascript": {
+              "version": "4.0.0",
+              "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz",
+              "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==",
+              "dev": true,
+              "requires": {
+                "randombytes": "^2.1.0"
+              }
+            },
+            "terser": {
+              "version": "4.8.0",
+              "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz",
+              "integrity": "sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==",
+              "dev": true,
+              "requires": {
+                "commander": "^2.20.0",
+                "source-map": "~0.6.1",
+                "source-map-support": "~0.5.12"
+              }
+            },
+            "terser-webpack-plugin": {
+              "version": "1.4.5",
+              "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz",
+              "integrity": "sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw==",
+              "dev": true,
+              "requires": {
+                "cacache": "^12.0.2",
+                "find-cache-dir": "^2.1.0",
+                "is-wsl": "^1.1.0",
+                "schema-utils": "^1.0.0",
+                "serialize-javascript": "^4.0.0",
+                "source-map": "^0.6.1",
+                "terser": "^4.1.2",
+                "webpack-sources": "^1.4.0",
+                "worker-farm": "^1.7.0"
+              }
+            }
+          }
+        },
+        "webpack-dev-middleware": {
+          "version": "3.7.3",
+          "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.7.3.tgz",
+          "integrity": "sha512-djelc/zGiz9nZj/U7PTBi2ViorGJXEWo/3ltkPbDyxCXhhEXkW0ce99falaok4TPj+AsxLiXJR0EBOb0zh9fKQ==",
+          "dev": true,
+          "requires": {
+            "memory-fs": "^0.4.1",
+            "mime": "^2.4.4",
+            "mkdirp": "^0.5.1",
+            "range-parser": "^1.2.1",
+            "webpack-log": "^2.0.0"
+          }
+        },
+        "yallist": {
+          "version": "3.1.1",
+          "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+          "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+          "dev": true
+        }
+      }
+    },
+    "@storybook/builder-webpack5": {
+      "version": "6.5.4",
+      "resolved": "https://registry.npmjs.org/@storybook/builder-webpack5/-/builder-webpack5-6.5.4.tgz",
+      "integrity": "sha512-BoqQkgnyqwySEcfaDreBG9SUnGTzp8jcfdUPqewtqyJK5qQgh79v2VowpSxT00G+eyWcsDkct2U4JnTmjrDpDg==",
+      "dev": true,
+      "requires": {
+        "@babel/core": "^7.12.10",
+        "@storybook/addons": "6.5.4",
+        "@storybook/api": "6.5.4",
+        "@storybook/channel-postmessage": "6.5.4",
+        "@storybook/channels": "6.5.4",
+        "@storybook/client-api": "6.5.4",
+        "@storybook/client-logger": "6.5.4",
+        "@storybook/components": "6.5.4",
+        "@storybook/core-common": "6.5.4",
+        "@storybook/core-events": "6.5.4",
+        "@storybook/node-logger": "6.5.4",
+        "@storybook/preview-web": "6.5.4",
+        "@storybook/router": "6.5.4",
+        "@storybook/semver": "^7.3.2",
+        "@storybook/store": "6.5.4",
+        "@storybook/theming": "6.5.4",
+        "@types/node": "^14.0.10 || ^16.0.0",
+        "babel-loader": "^8.0.0",
+        "babel-plugin-named-exports-order": "^0.0.2",
+        "browser-assert": "^1.2.1",
+        "case-sensitive-paths-webpack-plugin": "^2.3.0",
+        "core-js": "^3.8.2",
+        "css-loader": "^5.0.1",
+        "fork-ts-checker-webpack-plugin": "^6.0.4",
+        "glob": "^7.1.6",
+        "glob-promise": "^3.4.0",
+        "html-webpack-plugin": "^5.0.0",
+        "path-browserify": "^1.0.1",
+        "process": "^0.11.10",
+        "stable": "^0.1.8",
+        "style-loader": "^2.0.0",
+        "terser-webpack-plugin": "^5.0.3",
+        "ts-dedent": "^2.0.0",
+        "util-deprecate": "^1.0.2",
+        "webpack": "^5.9.0",
+        "webpack-dev-middleware": "^4.1.0",
+        "webpack-hot-middleware": "^2.25.1",
+        "webpack-virtual-modules": "^0.4.1"
+      },
+      "dependencies": {
+        "@storybook/semver": {
+          "version": "7.3.2",
+          "resolved": "https://registry.npmjs.org/@storybook/semver/-/semver-7.3.2.tgz",
+          "integrity": "sha512-SWeszlsiPsMI0Ps0jVNtH64cI5c0UF3f7KgjVKJoNP30crQ6wUSddY2hsdeczZXEKVJGEn50Q60flcGsQGIcrg==",
+          "dev": true,
+          "requires": {
+            "core-js": "^3.6.5",
+            "find-up": "^4.1.0"
+          }
+        },
+        "@types/html-minifier-terser": {
+          "version": "6.1.0",
+          "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz",
+          "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==",
+          "dev": true
+        },
+        "@types/node": {
+          "version": "16.11.36",
+          "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.36.tgz",
+          "integrity": "sha512-FR5QJe+TaoZ2GsMHkjuwoNabr+UrJNRr2HNOo+r/7vhcuntM6Ee/pRPOnRhhL2XE9OOvX9VLEq+BcXl3VjNoWA==",
+          "dev": true
+        },
+        "clean-css": {
+          "version": "5.3.0",
+          "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.0.tgz",
+          "integrity": "sha512-YYuuxv4H/iNb1Z/5IbMRoxgrzjWGhOEFfd+groZ5dMCVkpENiMZmwspdrzBo9286JjM1gZJPAyL7ZIdzuvu2AQ==",
+          "dev": true,
+          "requires": {
+            "source-map": "~0.6.0"
+          }
+        },
+        "commander": {
+          "version": "8.3.0",
+          "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
+          "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==",
+          "dev": true
+        },
+        "css-loader": {
+          "version": "5.2.7",
+          "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-5.2.7.tgz",
+          "integrity": "sha512-Q7mOvpBNBG7YrVGMxRxcBJZFL75o+cH2abNASdibkj/fffYD8qWbInZrD0S9ccI6vZclF3DsHE7njGlLtaHbhg==",
+          "dev": true,
+          "requires": {
+            "icss-utils": "^5.1.0",
+            "loader-utils": "^2.0.0",
+            "postcss": "^8.2.15",
+            "postcss-modules-extract-imports": "^3.0.0",
+            "postcss-modules-local-by-default": "^4.0.0",
+            "postcss-modules-scope": "^3.0.0",
+            "postcss-modules-values": "^4.0.0",
+            "postcss-value-parser": "^4.1.0",
+            "schema-utils": "^3.0.0",
+            "semver": "^7.3.5"
+          }
+        },
+        "html-minifier-terser": {
+          "version": "6.1.0",
+          "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz",
+          "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==",
+          "dev": true,
+          "requires": {
+            "camel-case": "^4.1.2",
+            "clean-css": "^5.2.2",
+            "commander": "^8.3.0",
+            "he": "^1.2.0",
+            "param-case": "^3.0.4",
+            "relateurl": "^0.2.7",
+            "terser": "^5.10.0"
+          }
+        },
+        "html-webpack-plugin": {
+          "version": "5.5.0",
+          "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz",
+          "integrity": "sha512-sy88PC2cRTVxvETRgUHFrL4No3UxvcH8G1NepGhqaTT+GXN2kTamqasot0inS5hXeg1cMbFDt27zzo9p35lZVw==",
+          "dev": true,
+          "requires": {
+            "@types/html-minifier-terser": "^6.0.0",
+            "html-minifier-terser": "^6.0.2",
+            "lodash": "^4.17.21",
+            "pretty-error": "^4.0.0",
+            "tapable": "^2.0.0"
+          }
+        },
+        "htmlparser2": {
+          "version": "6.1.0",
+          "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz",
+          "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==",
+          "dev": true,
+          "requires": {
+            "domelementtype": "^2.0.1",
+            "domhandler": "^4.0.0",
+            "domutils": "^2.5.2",
+            "entities": "^2.0.0"
+          }
+        },
+        "pretty-error": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz",
+          "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==",
+          "dev": true,
+          "requires": {
+            "lodash": "^4.17.20",
+            "renderkid": "^3.0.0"
+          }
+        },
+        "renderkid": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz",
+          "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==",
+          "dev": true,
+          "requires": {
+            "css-select": "^4.1.3",
+            "dom-converter": "^0.2.0",
+            "htmlparser2": "^6.1.0",
+            "lodash": "^4.17.21",
+            "strip-ansi": "^6.0.1"
+          }
+        },
+        "source-map": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+          "dev": true
+        },
+        "source-map-support": {
+          "version": "0.5.21",
+          "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
+          "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+          "dev": true,
+          "requires": {
+            "buffer-from": "^1.0.0",
+            "source-map": "^0.6.0"
+          }
+        },
+        "style-loader": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-2.0.0.tgz",
+          "integrity": "sha512-Z0gYUJmzZ6ZdRUqpg1r8GsaFKypE+3xAzuFeMuoHgjc9KZv3wMyCRjQIWEbhoFSq7+7yoHXySDJyyWQaPajeiQ==",
+          "dev": true,
+          "requires": {
+            "loader-utils": "^2.0.0",
+            "schema-utils": "^3.0.0"
+          }
+        },
+        "terser": {
+          "version": "5.13.1",
+          "resolved": "https://registry.npmjs.org/terser/-/terser-5.13.1.tgz",
+          "integrity": "sha512-hn4WKOfwnwbYfe48NgrQjqNOH9jzLqRcIfbYytOXCOv46LBfWr9bDS17MQqOi+BWGD0sJK3Sj5NC/gJjiojaoA==",
+          "dev": true,
+          "requires": {
+            "acorn": "^8.5.0",
+            "commander": "^2.20.0",
+            "source-map": "~0.8.0-beta.0",
+            "source-map-support": "~0.5.20"
+          },
+          "dependencies": {
+            "commander": {
+              "version": "2.20.3",
+              "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+              "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+              "dev": true
+            },
+            "source-map": {
+              "version": "0.8.0-beta.0",
+              "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz",
+              "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==",
+              "dev": true,
+              "requires": {
+                "whatwg-url": "^7.0.0"
+              }
+            }
+          }
+        },
+        "tr46": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz",
+          "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=",
+          "dev": true,
+          "requires": {
+            "punycode": "^2.1.0"
+          }
+        },
+        "webidl-conversions": {
+          "version": "4.0.2",
+          "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz",
+          "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==",
+          "dev": true
+        },
+        "webpack-dev-middleware": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-4.3.0.tgz",
+          "integrity": "sha512-PjwyVY95/bhBh6VUqt6z4THplYcsvQ8YNNBTBM873xLVmw8FLeALn0qurHbs9EmcfhzQis/eoqypSnZeuUz26w==",
+          "dev": true,
+          "requires": {
+            "colorette": "^1.2.2",
+            "mem": "^8.1.1",
+            "memfs": "^3.2.2",
+            "mime-types": "^2.1.30",
+            "range-parser": "^1.2.1",
+            "schema-utils": "^3.0.0"
+          }
+        },
+        "webpack-virtual-modules": {
+          "version": "0.4.3",
+          "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.4.3.tgz",
+          "integrity": "sha512-5NUqC2JquIL2pBAAo/VfBP6KuGkHIZQXW/lNKupLPfhViwh8wNsu0BObtl09yuKZszeEUfbXz8xhrHvSG16Nqw==",
+          "dev": true
+        },
+        "whatwg-url": {
+          "version": "7.1.0",
+          "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz",
+          "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==",
+          "dev": true,
+          "requires": {
+            "lodash.sortby": "^4.7.0",
+            "tr46": "^1.0.1",
+            "webidl-conversions": "^4.0.2"
+          }
+        }
+      }
+    },
+    "@storybook/channel-postmessage": {
+      "version": "6.5.4",
+      "resolved": "https://registry.npmjs.org/@storybook/channel-postmessage/-/channel-postmessage-6.5.4.tgz",
+      "integrity": "sha512-K9UG32KRXB4YKg9/WpyE6aoWBjOI2+XkFbjab9FEpI7zxJSEjKlOIqJk1uE9Xm+oWNvj80MvwD9ecGtJtw9BsA==",
+      "dev": true,
+      "requires": {
+        "@storybook/channels": "6.5.4",
+        "@storybook/client-logger": "6.5.4",
+        "@storybook/core-events": "6.5.4",
+        "core-js": "^3.8.2",
+        "global": "^4.4.0",
+        "qs": "^6.10.0",
+        "telejson": "^6.0.8"
+      }
+    },
+    "@storybook/channel-websocket": {
+      "version": "6.5.4",
+      "resolved": "https://registry.npmjs.org/@storybook/channel-websocket/-/channel-websocket-6.5.4.tgz",
+      "integrity": "sha512-BQtN7ZVoeUUIDtQdsnVCOzKIfpODAIW4IQ8cEuYeHiQDuGWW0+4MuVP5fzfHVugXRoQEF+OBxP8YcVVgeakyQg==",
+      "dev": true,
+      "requires": {
+        "@storybook/channels": "6.5.4",
+        "@storybook/client-logger": "6.5.4",
+        "core-js": "^3.8.2",
+        "global": "^4.4.0",
+        "telejson": "^6.0.8"
+      }
+    },
+    "@storybook/channels": {
+      "version": "6.5.4",
+      "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-6.5.4.tgz",
+      "integrity": "sha512-I78N4AIipaD5mrkh5p3Yiu4iim2G3Ci7N8Y7UeRtQ4TJJzSAEuYMm7ydlpmoEM6G+4+hdU0lG2GjaeWNkIFvtA==",
+      "dev": true,
+      "requires": {
+        "core-js": "^3.8.2",
+        "ts-dedent": "^2.0.0",
+        "util-deprecate": "^1.0.2"
+      }
+    },
+    "@storybook/client-api": {
+      "version": "6.5.4",
+      "resolved": "https://registry.npmjs.org/@storybook/client-api/-/client-api-6.5.4.tgz",
+      "integrity": "sha512-2QMcjXBOxGiqcBWqZu/v1lXSiT8jRCiHL8yiUncYK57rRwB6xltX43CcY9IZRnq2iNtQ+BfrWV6RgtbIa9mHhg==",
+      "dev": true,
+      "requires": {
+        "@storybook/addons": "6.5.4",
+        "@storybook/channel-postmessage": "6.5.4",
+        "@storybook/channels": "6.5.4",
+        "@storybook/client-logger": "6.5.4",
+        "@storybook/core-events": "6.5.4",
+        "@storybook/csf": "0.0.2--canary.4566f4d.1",
+        "@storybook/store": "6.5.4",
+        "@types/qs": "^6.9.5",
+        "@types/webpack-env": "^1.16.0",
+        "core-js": "^3.8.2",
+        "fast-deep-equal": "^3.1.3",
+        "global": "^4.4.0",
+        "lodash": "^4.17.21",
+        "memoizerific": "^1.11.3",
+        "qs": "^6.10.0",
+        "regenerator-runtime": "^0.13.7",
+        "store2": "^2.12.0",
+        "synchronous-promise": "^2.0.15",
+        "ts-dedent": "^2.0.0",
+        "util-deprecate": "^1.0.2"
+      }
+    },
+    "@storybook/client-logger": {
+      "version": "6.5.4",
+      "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-6.5.4.tgz",
+      "integrity": "sha512-8z5cdocpLW2bK6lTGdlBoX8njmNLTd9rrzSEIYRtobAbWjyfB6leCY6F79OVDZCXGsXIOrx/e1fwzioCT/sJSQ==",
+      "dev": true,
+      "requires": {
+        "core-js": "^3.8.2",
+        "global": "^4.4.0"
+      }
+    },
+    "@storybook/components": {
+      "version": "6.5.4",
+      "resolved": "https://registry.npmjs.org/@storybook/components/-/components-6.5.4.tgz",
+      "integrity": "sha512-FCJJm2r/OzW3QvavTCdHZeTubJkbNJeoiGKeZs9kMYSgDZAr4cDUr1xUl30Hw57Uy3TCY9gnYdtGZgCP1tQiLw==",
+      "dev": true,
+      "requires": {
+        "@storybook/client-logger": "6.5.4",
+        "@storybook/csf": "0.0.2--canary.4566f4d.1",
+        "@storybook/theming": "6.5.4",
+        "@types/react-syntax-highlighter": "11.0.5",
+        "core-js": "^3.8.2",
+        "qs": "^6.10.0",
+        "react-syntax-highlighter": "^15.4.5",
+        "regenerator-runtime": "^0.13.7",
+        "util-deprecate": "^1.0.2"
+      }
+    },
+    "@storybook/core": {
+      "version": "6.5.4",
+      "resolved": "https://registry.npmjs.org/@storybook/core/-/core-6.5.4.tgz",
+      "integrity": "sha512-de43+5FgQnLWTJw51h4qpaDVy1aHFte2eE2wi5rC3VHICxqQJJQqtzDrAtZ9NoKhztS5tZHA6pk60fh1TtPk8g==",
+      "dev": true,
+      "requires": {
+        "@storybook/core-client": "6.5.4",
+        "@storybook/core-server": "6.5.4"
+      }
+    },
+    "@storybook/core-client": {
+      "version": "6.5.4",
+      "resolved": "https://registry.npmjs.org/@storybook/core-client/-/core-client-6.5.4.tgz",
+      "integrity": "sha512-0rlQ5reKuCeFwqXdicPSLX/0ISAoEpVQgmBEosGt+1knRUfzS/IdLaBO+ZEwh+ajldFo3k9JfZOfxBOFqPMhsg==",
+      "dev": true,
+      "requires": {
+        "@storybook/addons": "6.5.4",
+        "@storybook/channel-postmessage": "6.5.4",
+        "@storybook/channel-websocket": "6.5.4",
+        "@storybook/client-api": "6.5.4",
+        "@storybook/client-logger": "6.5.4",
+        "@storybook/core-events": "6.5.4",
+        "@storybook/csf": "0.0.2--canary.4566f4d.1",
+        "@storybook/preview-web": "6.5.4",
+        "@storybook/store": "6.5.4",
+        "@storybook/ui": "6.5.4",
+        "airbnb-js-shims": "^2.2.1",
+        "ansi-to-html": "^0.6.11",
+        "core-js": "^3.8.2",
+        "global": "^4.4.0",
+        "lodash": "^4.17.21",
+        "qs": "^6.10.0",
+        "regenerator-runtime": "^0.13.7",
+        "ts-dedent": "^2.0.0",
+        "unfetch": "^4.2.0",
+        "util-deprecate": "^1.0.2"
+      }
+    },
+    "@storybook/core-common": {
+      "version": "6.5.4",
+      "resolved": "https://registry.npmjs.org/@storybook/core-common/-/core-common-6.5.4.tgz",
+      "integrity": "sha512-JJ9PJr8FRUfTVC9KvkTl+K1Fut1hxMOJYuknmh8HdL8mSFzFIUzaizggEf9X7WT+0VVQkzj3Yit6s6dWA5Rh8w==",
+      "dev": true,
+      "requires": {
+        "@babel/core": "^7.12.10",
+        "@babel/plugin-proposal-class-properties": "^7.12.1",
+        "@babel/plugin-proposal-decorators": "^7.12.12",
+        "@babel/plugin-proposal-export-default-from": "^7.12.1",
+        "@babel/plugin-proposal-nullish-coalescing-operator": "^7.12.1",
+        "@babel/plugin-proposal-object-rest-spread": "^7.12.1",
+        "@babel/plugin-proposal-optional-chaining": "^7.12.7",
+        "@babel/plugin-proposal-private-methods": "^7.12.1",
+        "@babel/plugin-proposal-private-property-in-object": "^7.12.1",
+        "@babel/plugin-syntax-dynamic-import": "^7.8.3",
+        "@babel/plugin-transform-arrow-functions": "^7.12.1",
+        "@babel/plugin-transform-block-scoping": "^7.12.12",
+        "@babel/plugin-transform-classes": "^7.12.1",
+        "@babel/plugin-transform-destructuring": "^7.12.1",
+        "@babel/plugin-transform-for-of": "^7.12.1",
+        "@babel/plugin-transform-parameters": "^7.12.1",
+        "@babel/plugin-transform-shorthand-properties": "^7.12.1",
+        "@babel/plugin-transform-spread": "^7.12.1",
+        "@babel/preset-env": "^7.12.11",
+        "@babel/preset-react": "^7.12.10",
+        "@babel/preset-typescript": "^7.12.7",
+        "@babel/register": "^7.12.1",
+        "@storybook/node-logger": "6.5.4",
+        "@storybook/semver": "^7.3.2",
+        "@types/node": "^14.0.10 || ^16.0.0",
+        "@types/pretty-hrtime": "^1.0.0",
+        "babel-loader": "^8.0.0",
+        "babel-plugin-macros": "^3.0.1",
+        "babel-plugin-polyfill-corejs3": "^0.1.0",
+        "chalk": "^4.1.0",
+        "core-js": "^3.8.2",
+        "express": "^4.17.1",
+        "file-system-cache": "^1.0.5",
+        "find-up": "^5.0.0",
+        "fork-ts-checker-webpack-plugin": "^6.0.4",
+        "fs-extra": "^9.0.1",
+        "glob": "^7.1.6",
+        "handlebars": "^4.7.7",
+        "interpret": "^2.2.0",
+        "json5": "^2.1.3",
+        "lazy-universal-dotenv": "^3.0.1",
+        "picomatch": "^2.3.0",
+        "pkg-dir": "^5.0.0",
+        "pretty-hrtime": "^1.0.3",
+        "resolve-from": "^5.0.0",
+        "slash": "^3.0.0",
+        "telejson": "^6.0.8",
+        "ts-dedent": "^2.0.0",
+        "util-deprecate": "^1.0.2",
+        "webpack": "4"
+      },
+      "dependencies": {
+        "@babel/helper-define-polyfill-provider": {
+          "version": "0.1.5",
+          "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.1.5.tgz",
+          "integrity": "sha512-nXuzCSwlJ/WKr8qxzW816gwyT6VZgiJG17zR40fou70yfAcqjoNyTLl/DQ+FExw5Hx5KNqshmN8Ldl/r2N7cTg==",
+          "dev": true,
+          "requires": {
+            "@babel/helper-compilation-targets": "^7.13.0",
+            "@babel/helper-module-imports": "^7.12.13",
+            "@babel/helper-plugin-utils": "^7.13.0",
+            "@babel/traverse": "^7.13.0",
+            "debug": "^4.1.1",
+            "lodash.debounce": "^4.0.8",
+            "resolve": "^1.14.2",
+            "semver": "^6.1.2"
+          },
+          "dependencies": {
+            "semver": {
+              "version": "6.3.0",
+              "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+              "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+              "dev": true
+            }
+          }
+        },
+        "@storybook/semver": {
+          "version": "7.3.2",
+          "resolved": "https://registry.npmjs.org/@storybook/semver/-/semver-7.3.2.tgz",
+          "integrity": "sha512-SWeszlsiPsMI0Ps0jVNtH64cI5c0UF3f7KgjVKJoNP30crQ6wUSddY2hsdeczZXEKVJGEn50Q60flcGsQGIcrg==",
+          "dev": true,
+          "requires": {
+            "core-js": "^3.6.5",
+            "find-up": "^4.1.0"
+          },
+          "dependencies": {
+            "find-up": {
+              "version": "4.1.0",
+              "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+              "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+              "dev": true,
+              "requires": {
+                "locate-path": "^5.0.0",
+                "path-exists": "^4.0.0"
+              }
+            }
+          }
+        },
+        "@types/node": {
+          "version": "16.11.36",
+          "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.36.tgz",
+          "integrity": "sha512-FR5QJe+TaoZ2GsMHkjuwoNabr+UrJNRr2HNOo+r/7vhcuntM6Ee/pRPOnRhhL2XE9OOvX9VLEq+BcXl3VjNoWA==",
+          "dev": true
+        },
+        "@webassemblyjs/ast": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz",
+          "integrity": "sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA==",
+          "dev": true,
+          "requires": {
+            "@webassemblyjs/helper-module-context": "1.9.0",
+            "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
+            "@webassemblyjs/wast-parser": "1.9.0"
+          }
+        },
+        "@webassemblyjs/helper-api-error": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz",
+          "integrity": "sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw==",
+          "dev": true
+        },
+        "@webassemblyjs/helper-buffer": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz",
+          "integrity": "sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA==",
+          "dev": true
+        },
+        "@webassemblyjs/helper-wasm-bytecode": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz",
+          "integrity": "sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw==",
+          "dev": true
+        },
+        "@webassemblyjs/helper-wasm-section": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz",
+          "integrity": "sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw==",
+          "dev": true,
+          "requires": {
+            "@webassemblyjs/ast": "1.9.0",
+            "@webassemblyjs/helper-buffer": "1.9.0",
+            "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
+            "@webassemblyjs/wasm-gen": "1.9.0"
+          }
+        },
+        "@webassemblyjs/ieee754": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz",
+          "integrity": "sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg==",
+          "dev": true,
+          "requires": {
+            "@xtuc/ieee754": "^1.2.0"
+          }
+        },
+        "@webassemblyjs/leb128": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.9.0.tgz",
+          "integrity": "sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw==",
+          "dev": true,
+          "requires": {
+            "@xtuc/long": "4.2.2"
+          }
+        },
+        "@webassemblyjs/utf8": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.9.0.tgz",
+          "integrity": "sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w==",
+          "dev": true
+        },
+        "@webassemblyjs/wasm-edit": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz",
+          "integrity": "sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw==",
+          "dev": true,
+          "requires": {
+            "@webassemblyjs/ast": "1.9.0",
+            "@webassemblyjs/helper-buffer": "1.9.0",
+            "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
+            "@webassemblyjs/helper-wasm-section": "1.9.0",
+            "@webassemblyjs/wasm-gen": "1.9.0",
+            "@webassemblyjs/wasm-opt": "1.9.0",
+            "@webassemblyjs/wasm-parser": "1.9.0",
+            "@webassemblyjs/wast-printer": "1.9.0"
+          }
+        },
+        "@webassemblyjs/wasm-gen": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz",
+          "integrity": "sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA==",
+          "dev": true,
+          "requires": {
+            "@webassemblyjs/ast": "1.9.0",
+            "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
+            "@webassemblyjs/ieee754": "1.9.0",
+            "@webassemblyjs/leb128": "1.9.0",
+            "@webassemblyjs/utf8": "1.9.0"
+          }
+        },
+        "@webassemblyjs/wasm-opt": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz",
+          "integrity": "sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A==",
+          "dev": true,
+          "requires": {
+            "@webassemblyjs/ast": "1.9.0",
+            "@webassemblyjs/helper-buffer": "1.9.0",
+            "@webassemblyjs/wasm-gen": "1.9.0",
+            "@webassemblyjs/wasm-parser": "1.9.0"
+          }
+        },
+        "@webassemblyjs/wasm-parser": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz",
+          "integrity": "sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA==",
+          "dev": true,
+          "requires": {
+            "@webassemblyjs/ast": "1.9.0",
+            "@webassemblyjs/helper-api-error": "1.9.0",
+            "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
+            "@webassemblyjs/ieee754": "1.9.0",
+            "@webassemblyjs/leb128": "1.9.0",
+            "@webassemblyjs/utf8": "1.9.0"
+          }
+        },
+        "@webassemblyjs/wast-printer": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz",
+          "integrity": "sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA==",
+          "dev": true,
+          "requires": {
+            "@webassemblyjs/ast": "1.9.0",
+            "@webassemblyjs/wast-parser": "1.9.0",
+            "@xtuc/long": "4.2.2"
+          }
+        },
+        "acorn": {
+          "version": "6.4.2",
+          "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz",
+          "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==",
+          "dev": true
+        },
+        "ansi-styles": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+          "dev": true,
+          "requires": {
+            "color-convert": "^2.0.1"
+          }
+        },
+        "babel-plugin-polyfill-corejs3": {
+          "version": "0.1.7",
+          "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.1.7.tgz",
+          "integrity": "sha512-u+gbS9bbPhZWEeyy1oR/YaaSpod/KDT07arZHb80aTpl8H5ZBq+uN1nN9/xtX7jQyfLdPfoqI4Rue/MQSWJquw==",
+          "dev": true,
+          "requires": {
+            "@babel/helper-define-polyfill-provider": "^0.1.5",
+            "core-js-compat": "^3.8.1"
+          }
+        },
+        "braces": {
+          "version": "2.3.2",
+          "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
+          "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
+          "dev": true,
+          "requires": {
+            "arr-flatten": "^1.1.0",
+            "array-unique": "^0.3.2",
+            "extend-shallow": "^2.0.1",
+            "fill-range": "^4.0.0",
+            "isobject": "^3.0.1",
+            "repeat-element": "^1.1.2",
+            "snapdragon": "^0.8.1",
+            "snapdragon-node": "^2.0.1",
+            "split-string": "^3.0.2",
+            "to-regex": "^3.0.1"
+          },
+          "dependencies": {
+            "extend-shallow": {
+              "version": "2.0.1",
+              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+              "dev": true,
+              "requires": {
+                "is-extendable": "^0.1.0"
+              }
+            }
+          }
+        },
+        "cacache": {
+          "version": "12.0.4",
+          "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz",
+          "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==",
+          "dev": true,
+          "requires": {
+            "bluebird": "^3.5.5",
+            "chownr": "^1.1.1",
+            "figgy-pudding": "^3.5.1",
+            "glob": "^7.1.4",
+            "graceful-fs": "^4.1.15",
+            "infer-owner": "^1.0.3",
+            "lru-cache": "^5.1.1",
+            "mississippi": "^3.0.0",
+            "mkdirp": "^0.5.1",
+            "move-concurrently": "^1.0.1",
+            "promise-inflight": "^1.0.1",
+            "rimraf": "^2.6.3",
+            "ssri": "^6.0.1",
+            "unique-filename": "^1.1.1",
+            "y18n": "^4.0.0"
+          }
+        },
+        "chalk": {
+          "version": "4.1.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+          "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
+          }
+        },
+        "chownr": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
+          "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
+          "dev": true
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "dev": true,
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
+        },
+        "commander": {
+          "version": "2.20.3",
+          "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+          "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+          "dev": true
+        },
+        "enhanced-resolve": {
+          "version": "4.5.0",
+          "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz",
+          "integrity": "sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg==",
+          "dev": true,
+          "requires": {
+            "graceful-fs": "^4.1.2",
+            "memory-fs": "^0.5.0",
+            "tapable": "^1.0.0"
+          },
+          "dependencies": {
+            "memory-fs": {
+              "version": "0.5.0",
+              "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz",
+              "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==",
+              "dev": true,
+              "requires": {
+                "errno": "^0.1.3",
+                "readable-stream": "^2.0.1"
+              }
+            }
+          }
+        },
+        "eslint-scope": {
+          "version": "4.0.3",
+          "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz",
+          "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==",
+          "dev": true,
+          "requires": {
+            "esrecurse": "^4.1.0",
+            "estraverse": "^4.1.1"
+          }
+        },
+        "fill-range": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
+          "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
+          "dev": true,
+          "requires": {
+            "extend-shallow": "^2.0.1",
+            "is-number": "^3.0.0",
+            "repeat-string": "^1.6.1",
+            "to-regex-range": "^2.1.0"
+          },
+          "dependencies": {
+            "extend-shallow": {
+              "version": "2.0.1",
+              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+              "dev": true,
+              "requires": {
+                "is-extendable": "^0.1.0"
+              }
+            }
+          }
+        },
+        "find-cache-dir": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz",
+          "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==",
+          "dev": true,
+          "requires": {
+            "commondir": "^1.0.1",
+            "make-dir": "^2.0.0",
+            "pkg-dir": "^3.0.0"
+          },
+          "dependencies": {
+            "find-up": {
+              "version": "3.0.0",
+              "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
+              "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+              "dev": true,
+              "requires": {
+                "locate-path": "^3.0.0"
+              }
+            },
+            "locate-path": {
+              "version": "3.0.0",
+              "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
+              "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+              "dev": true,
+              "requires": {
+                "p-locate": "^3.0.0",
+                "path-exists": "^3.0.0"
+              }
+            },
+            "p-limit": {
+              "version": "2.3.0",
+              "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+              "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+              "dev": true,
+              "requires": {
+                "p-try": "^2.0.0"
+              }
+            },
+            "p-locate": {
+              "version": "3.0.0",
+              "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
+              "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+              "dev": true,
+              "requires": {
+                "p-limit": "^2.0.0"
+              }
+            },
+            "path-exists": {
+              "version": "3.0.0",
+              "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+              "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
+              "dev": true
+            },
+            "pkg-dir": {
+              "version": "3.0.0",
+              "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz",
+              "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==",
+              "dev": true,
+              "requires": {
+                "find-up": "^3.0.0"
+              }
+            }
+          }
+        },
+        "find-up": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+          "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+          "dev": true,
+          "requires": {
+            "locate-path": "^6.0.0",
+            "path-exists": "^4.0.0"
+          },
+          "dependencies": {
+            "locate-path": {
+              "version": "6.0.0",
+              "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+              "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+              "dev": true,
+              "requires": {
+                "p-locate": "^5.0.0"
+              }
+            }
+          }
+        },
+        "fs-extra": {
+          "version": "9.1.0",
+          "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
+          "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
+          "dev": true,
+          "requires": {
+            "at-least-node": "^1.0.0",
+            "graceful-fs": "^4.2.0",
+            "jsonfile": "^6.0.1",
+            "universalify": "^2.0.0"
+          }
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
+        "is-number": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
+          "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
+          "dev": true,
+          "requires": {
+            "kind-of": "^3.0.2"
+          },
+          "dependencies": {
+            "kind-of": {
+              "version": "3.2.2",
+              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+              "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+              "dev": true,
+              "requires": {
+                "is-buffer": "^1.1.5"
+              }
+            }
+          }
+        },
+        "is-wsl": {
+          "version": "1.1.0",
+          "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz",
+          "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=",
+          "dev": true
+        },
+        "loader-runner": {
+          "version": "2.4.0",
+          "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz",
+          "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==",
+          "dev": true
+        },
+        "loader-utils": {
+          "version": "1.4.0",
+          "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
+          "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==",
+          "dev": true,
+          "requires": {
+            "big.js": "^5.2.2",
+            "emojis-list": "^3.0.0",
+            "json5": "^1.0.1"
+          },
+          "dependencies": {
+            "json5": {
+              "version": "1.0.1",
+              "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
+              "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
+              "dev": true,
+              "requires": {
+                "minimist": "^1.2.0"
+              }
+            }
+          }
+        },
+        "lru-cache": {
+          "version": "5.1.1",
+          "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+          "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+          "dev": true,
+          "requires": {
+            "yallist": "^3.0.2"
+          }
+        },
+        "make-dir": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
+          "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
+          "dev": true,
+          "requires": {
+            "pify": "^4.0.1",
+            "semver": "^5.6.0"
+          },
+          "dependencies": {
+            "semver": {
+              "version": "5.7.1",
+              "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+              "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+              "dev": true
+            }
+          }
+        },
+        "micromatch": {
+          "version": "3.1.10",
+          "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
+          "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
+          "dev": true,
+          "requires": {
+            "arr-diff": "^4.0.0",
+            "array-unique": "^0.3.2",
+            "braces": "^2.3.1",
+            "define-property": "^2.0.2",
+            "extend-shallow": "^3.0.2",
+            "extglob": "^2.0.4",
+            "fragment-cache": "^0.2.1",
+            "kind-of": "^6.0.2",
+            "nanomatch": "^1.2.9",
+            "object.pick": "^1.3.0",
+            "regex-not": "^1.0.0",
+            "snapdragon": "^0.8.1",
+            "to-regex": "^3.0.2"
+          }
+        },
+        "mkdirp": {
+          "version": "0.5.6",
+          "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
+          "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
+          "dev": true,
+          "requires": {
+            "minimist": "^1.2.6"
+          }
+        },
+        "p-limit": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+          "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+          "dev": true,
+          "requires": {
+            "yocto-queue": "^0.1.0"
+          }
+        },
+        "p-locate": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+          "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+          "dev": true,
+          "requires": {
+            "p-limit": "^3.0.2"
+          }
+        },
+        "pkg-dir": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-5.0.0.tgz",
+          "integrity": "sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA==",
+          "dev": true,
+          "requires": {
+            "find-up": "^5.0.0"
+          }
+        },
+        "resolve-from": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
+          "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
+          "dev": true
+        },
+        "rimraf": {
+          "version": "2.7.1",
+          "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+          "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+          "dev": true,
+          "requires": {
+            "glob": "^7.1.3"
+          }
+        },
+        "schema-utils": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
+          "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
+          "dev": true,
+          "requires": {
+            "ajv": "^6.1.0",
+            "ajv-errors": "^1.0.0",
+            "ajv-keywords": "^3.1.0"
+          }
+        },
+        "serialize-javascript": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz",
+          "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==",
+          "dev": true,
+          "requires": {
+            "randombytes": "^2.1.0"
+          }
+        },
+        "source-map": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+          "dev": true
+        },
+        "ssri": {
+          "version": "6.0.2",
+          "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.2.tgz",
+          "integrity": "sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==",
+          "dev": true,
+          "requires": {
+            "figgy-pudding": "^3.5.1"
+          }
+        },
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
+        },
+        "tapable": {
+          "version": "1.1.3",
+          "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",
+          "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==",
+          "dev": true
+        },
+        "terser": {
+          "version": "4.8.0",
+          "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz",
+          "integrity": "sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==",
+          "dev": true,
+          "requires": {
+            "commander": "^2.20.0",
+            "source-map": "~0.6.1",
+            "source-map-support": "~0.5.12"
+          }
+        },
+        "terser-webpack-plugin": {
+          "version": "1.4.5",
+          "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz",
+          "integrity": "sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw==",
+          "dev": true,
+          "requires": {
+            "cacache": "^12.0.2",
+            "find-cache-dir": "^2.1.0",
+            "is-wsl": "^1.1.0",
+            "schema-utils": "^1.0.0",
+            "serialize-javascript": "^4.0.0",
+            "source-map": "^0.6.1",
+            "terser": "^4.1.2",
+            "webpack-sources": "^1.4.0",
+            "worker-farm": "^1.7.0"
+          }
+        },
+        "to-regex-range": {
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
+          "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
+          "dev": true,
+          "requires": {
+            "is-number": "^3.0.0",
+            "repeat-string": "^1.6.1"
+          }
+        },
+        "watchpack": {
+          "version": "1.7.5",
+          "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz",
+          "integrity": "sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==",
+          "dev": true,
+          "requires": {
+            "chokidar": "^3.4.1",
+            "graceful-fs": "^4.1.2",
+            "neo-async": "^2.5.0",
+            "watchpack-chokidar2": "^2.0.1"
+          }
+        },
+        "webpack": {
+          "version": "4.46.0",
+          "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.46.0.tgz",
+          "integrity": "sha512-6jJuJjg8znb/xRItk7bkT0+Q7AHCYjjFnvKIWQPkNIOyRqoCGvkOs0ipeQzrqz4l5FtN5ZI/ukEHroeX/o1/5Q==",
+          "dev": true,
+          "requires": {
+            "@webassemblyjs/ast": "1.9.0",
+            "@webassemblyjs/helper-module-context": "1.9.0",
+            "@webassemblyjs/wasm-edit": "1.9.0",
+            "@webassemblyjs/wasm-parser": "1.9.0",
+            "acorn": "^6.4.1",
+            "ajv": "^6.10.2",
+            "ajv-keywords": "^3.4.1",
+            "chrome-trace-event": "^1.0.2",
+            "enhanced-resolve": "^4.5.0",
+            "eslint-scope": "^4.0.3",
+            "json-parse-better-errors": "^1.0.2",
+            "loader-runner": "^2.4.0",
+            "loader-utils": "^1.2.3",
+            "memory-fs": "^0.4.1",
+            "micromatch": "^3.1.10",
+            "mkdirp": "^0.5.3",
+            "neo-async": "^2.6.1",
+            "node-libs-browser": "^2.2.1",
+            "schema-utils": "^1.0.0",
+            "tapable": "^1.1.3",
+            "terser-webpack-plugin": "^1.4.3",
+            "watchpack": "^1.7.4",
+            "webpack-sources": "^1.4.1"
+          }
+        },
+        "yallist": {
+          "version": "3.1.1",
+          "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+          "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+          "dev": true
+        }
+      }
+    },
+    "@storybook/core-events": {
+      "version": "6.5.4",
+      "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-6.5.4.tgz",
+      "integrity": "sha512-b03mPlzfX/TkeKqi7cgFzRof80dBb98w7ui6x0GzIXXcG/O0KebzqZ41/vXQICwsbkAIp5JQdkTEq9RUOh/KCg==",
+      "dev": true,
+      "requires": {
+        "core-js": "^3.8.2"
+      }
+    },
+    "@storybook/core-server": {
+      "version": "6.5.4",
+      "resolved": "https://registry.npmjs.org/@storybook/core-server/-/core-server-6.5.4.tgz",
+      "integrity": "sha512-Fmapj0xHHNQ3FUFXiHR/Zex9hBWnxKYMANxmNslAHbD3BEpf8xPYcjtptVvr0hJyHtnygtwqPbR0CUBbCG0KKQ==",
+      "dev": true,
+      "requires": {
+        "@discoveryjs/json-ext": "^0.5.3",
+        "@storybook/builder-webpack4": "6.5.4",
+        "@storybook/core-client": "6.5.4",
+        "@storybook/core-common": "6.5.4",
+        "@storybook/core-events": "6.5.4",
+        "@storybook/csf": "0.0.2--canary.4566f4d.1",
+        "@storybook/csf-tools": "6.5.4",
+        "@storybook/manager-webpack4": "6.5.4",
+        "@storybook/node-logger": "6.5.4",
+        "@storybook/semver": "^7.3.2",
+        "@storybook/store": "6.5.4",
+        "@storybook/telemetry": "6.5.4",
+        "@types/node": "^14.0.10 || ^16.0.0",
+        "@types/node-fetch": "^2.5.7",
+        "@types/pretty-hrtime": "^1.0.0",
+        "@types/webpack": "^4.41.26",
+        "better-opn": "^2.1.1",
+        "boxen": "^5.1.2",
+        "chalk": "^4.1.0",
+        "cli-table3": "^0.6.1",
+        "commander": "^6.2.1",
+        "compression": "^1.7.4",
+        "core-js": "^3.8.2",
+        "cpy": "^8.1.2",
+        "detect-port": "^1.3.0",
+        "express": "^4.17.1",
+        "fs-extra": "^9.0.1",
+        "global": "^4.4.0",
+        "globby": "^11.0.2",
+        "ip": "^1.1.5",
+        "lodash": "^4.17.21",
+        "node-fetch": "^2.6.7",
+        "open": "^8.4.0",
+        "pretty-hrtime": "^1.0.3",
+        "prompts": "^2.4.0",
+        "regenerator-runtime": "^0.13.7",
+        "serve-favicon": "^2.5.0",
+        "slash": "^3.0.0",
+        "telejson": "^6.0.8",
+        "ts-dedent": "^2.0.0",
+        "util-deprecate": "^1.0.2",
+        "watchpack": "^2.2.0",
+        "webpack": "4",
+        "ws": "^8.2.3",
+        "x-default-browser": "^0.4.0"
+      },
+      "dependencies": {
+        "@storybook/semver": {
+          "version": "7.3.2",
+          "resolved": "https://registry.npmjs.org/@storybook/semver/-/semver-7.3.2.tgz",
+          "integrity": "sha512-SWeszlsiPsMI0Ps0jVNtH64cI5c0UF3f7KgjVKJoNP30crQ6wUSddY2hsdeczZXEKVJGEn50Q60flcGsQGIcrg==",
+          "dev": true,
+          "requires": {
+            "core-js": "^3.6.5",
+            "find-up": "^4.1.0"
+          }
+        },
+        "@types/node": {
+          "version": "16.11.36",
+          "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.36.tgz",
+          "integrity": "sha512-FR5QJe+TaoZ2GsMHkjuwoNabr+UrJNRr2HNOo+r/7vhcuntM6Ee/pRPOnRhhL2XE9OOvX9VLEq+BcXl3VjNoWA==",
+          "dev": true
+        },
+        "@webassemblyjs/ast": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz",
+          "integrity": "sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA==",
+          "dev": true,
+          "requires": {
+            "@webassemblyjs/helper-module-context": "1.9.0",
+            "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
+            "@webassemblyjs/wast-parser": "1.9.0"
+          }
+        },
+        "@webassemblyjs/helper-api-error": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz",
+          "integrity": "sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw==",
+          "dev": true
+        },
+        "@webassemblyjs/helper-buffer": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz",
+          "integrity": "sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA==",
+          "dev": true
+        },
+        "@webassemblyjs/helper-wasm-bytecode": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz",
+          "integrity": "sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw==",
+          "dev": true
+        },
+        "@webassemblyjs/helper-wasm-section": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz",
+          "integrity": "sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw==",
+          "dev": true,
+          "requires": {
+            "@webassemblyjs/ast": "1.9.0",
+            "@webassemblyjs/helper-buffer": "1.9.0",
+            "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
+            "@webassemblyjs/wasm-gen": "1.9.0"
+          }
+        },
+        "@webassemblyjs/ieee754": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz",
+          "integrity": "sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg==",
+          "dev": true,
+          "requires": {
+            "@xtuc/ieee754": "^1.2.0"
+          }
+        },
+        "@webassemblyjs/leb128": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.9.0.tgz",
+          "integrity": "sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw==",
+          "dev": true,
+          "requires": {
+            "@xtuc/long": "4.2.2"
+          }
+        },
+        "@webassemblyjs/utf8": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.9.0.tgz",
+          "integrity": "sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w==",
+          "dev": true
+        },
+        "@webassemblyjs/wasm-edit": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz",
+          "integrity": "sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw==",
+          "dev": true,
+          "requires": {
+            "@webassemblyjs/ast": "1.9.0",
+            "@webassemblyjs/helper-buffer": "1.9.0",
+            "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
+            "@webassemblyjs/helper-wasm-section": "1.9.0",
+            "@webassemblyjs/wasm-gen": "1.9.0",
+            "@webassemblyjs/wasm-opt": "1.9.0",
+            "@webassemblyjs/wasm-parser": "1.9.0",
+            "@webassemblyjs/wast-printer": "1.9.0"
+          }
+        },
+        "@webassemblyjs/wasm-gen": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz",
+          "integrity": "sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA==",
+          "dev": true,
+          "requires": {
+            "@webassemblyjs/ast": "1.9.0",
+            "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
+            "@webassemblyjs/ieee754": "1.9.0",
+            "@webassemblyjs/leb128": "1.9.0",
+            "@webassemblyjs/utf8": "1.9.0"
+          }
+        },
+        "@webassemblyjs/wasm-opt": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz",
+          "integrity": "sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A==",
+          "dev": true,
+          "requires": {
+            "@webassemblyjs/ast": "1.9.0",
+            "@webassemblyjs/helper-buffer": "1.9.0",
+            "@webassemblyjs/wasm-gen": "1.9.0",
+            "@webassemblyjs/wasm-parser": "1.9.0"
+          }
+        },
+        "@webassemblyjs/wasm-parser": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz",
+          "integrity": "sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA==",
+          "dev": true,
+          "requires": {
+            "@webassemblyjs/ast": "1.9.0",
+            "@webassemblyjs/helper-api-error": "1.9.0",
+            "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
+            "@webassemblyjs/ieee754": "1.9.0",
+            "@webassemblyjs/leb128": "1.9.0",
+            "@webassemblyjs/utf8": "1.9.0"
+          }
+        },
+        "@webassemblyjs/wast-printer": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz",
+          "integrity": "sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA==",
+          "dev": true,
+          "requires": {
+            "@webassemblyjs/ast": "1.9.0",
+            "@webassemblyjs/wast-parser": "1.9.0",
+            "@xtuc/long": "4.2.2"
+          }
+        },
+        "acorn": {
+          "version": "6.4.2",
+          "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz",
+          "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==",
+          "dev": true
+        },
+        "ansi-styles": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+          "dev": true,
+          "requires": {
+            "color-convert": "^2.0.1"
+          }
+        },
+        "braces": {
+          "version": "2.3.2",
+          "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
+          "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
+          "dev": true,
+          "requires": {
+            "arr-flatten": "^1.1.0",
+            "array-unique": "^0.3.2",
+            "extend-shallow": "^2.0.1",
+            "fill-range": "^4.0.0",
+            "isobject": "^3.0.1",
+            "repeat-element": "^1.1.2",
+            "snapdragon": "^0.8.1",
+            "snapdragon-node": "^2.0.1",
+            "split-string": "^3.0.2",
+            "to-regex": "^3.0.1"
+          },
+          "dependencies": {
+            "extend-shallow": {
+              "version": "2.0.1",
+              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+              "dev": true,
+              "requires": {
+                "is-extendable": "^0.1.0"
+              }
+            }
+          }
+        },
+        "cacache": {
+          "version": "12.0.4",
+          "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz",
+          "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==",
+          "dev": true,
+          "requires": {
+            "bluebird": "^3.5.5",
+            "chownr": "^1.1.1",
+            "figgy-pudding": "^3.5.1",
+            "glob": "^7.1.4",
+            "graceful-fs": "^4.1.15",
+            "infer-owner": "^1.0.3",
+            "lru-cache": "^5.1.1",
+            "mississippi": "^3.0.0",
+            "mkdirp": "^0.5.1",
+            "move-concurrently": "^1.0.1",
+            "promise-inflight": "^1.0.1",
+            "rimraf": "^2.6.3",
+            "ssri": "^6.0.1",
+            "unique-filename": "^1.1.1",
+            "y18n": "^4.0.0"
+          }
+        },
+        "chalk": {
+          "version": "4.1.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+          "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
+          }
+        },
+        "chownr": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
+          "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
+          "dev": true
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "dev": true,
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
+        },
+        "commander": {
+          "version": "6.2.1",
+          "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz",
+          "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==",
+          "dev": true
+        },
+        "enhanced-resolve": {
+          "version": "4.5.0",
+          "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz",
+          "integrity": "sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg==",
+          "dev": true,
+          "requires": {
+            "graceful-fs": "^4.1.2",
+            "memory-fs": "^0.5.0",
+            "tapable": "^1.0.0"
+          },
+          "dependencies": {
+            "memory-fs": {
+              "version": "0.5.0",
+              "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz",
+              "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==",
+              "dev": true,
+              "requires": {
+                "errno": "^0.1.3",
+                "readable-stream": "^2.0.1"
+              }
+            }
+          }
+        },
+        "eslint-scope": {
+          "version": "4.0.3",
+          "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz",
+          "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==",
+          "dev": true,
+          "requires": {
+            "esrecurse": "^4.1.0",
+            "estraverse": "^4.1.1"
+          }
+        },
+        "fill-range": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
+          "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
+          "dev": true,
+          "requires": {
+            "extend-shallow": "^2.0.1",
+            "is-number": "^3.0.0",
+            "repeat-string": "^1.6.1",
+            "to-regex-range": "^2.1.0"
+          },
+          "dependencies": {
+            "extend-shallow": {
+              "version": "2.0.1",
+              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+              "dev": true,
+              "requires": {
+                "is-extendable": "^0.1.0"
+              }
+            }
+          }
+        },
+        "find-cache-dir": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz",
+          "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==",
+          "dev": true,
+          "requires": {
+            "commondir": "^1.0.1",
+            "make-dir": "^2.0.0",
+            "pkg-dir": "^3.0.0"
+          }
+        },
+        "fs-extra": {
+          "version": "9.1.0",
+          "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
+          "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
+          "dev": true,
+          "requires": {
+            "at-least-node": "^1.0.0",
+            "graceful-fs": "^4.2.0",
+            "jsonfile": "^6.0.1",
+            "universalify": "^2.0.0"
+          }
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
+        "is-number": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
+          "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
+          "dev": true,
+          "requires": {
+            "kind-of": "^3.0.2"
+          },
+          "dependencies": {
+            "kind-of": {
+              "version": "3.2.2",
+              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+              "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+              "dev": true,
+              "requires": {
+                "is-buffer": "^1.1.5"
+              }
+            }
+          }
+        },
+        "json5": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
+          "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
+          "dev": true,
+          "requires": {
+            "minimist": "^1.2.0"
+          }
+        },
+        "loader-runner": {
+          "version": "2.4.0",
+          "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz",
+          "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==",
+          "dev": true
+        },
+        "loader-utils": {
+          "version": "1.4.0",
+          "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
+          "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==",
+          "dev": true,
+          "requires": {
+            "big.js": "^5.2.2",
+            "emojis-list": "^3.0.0",
+            "json5": "^1.0.1"
+          }
+        },
+        "locate-path": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
+          "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+          "dev": true,
+          "requires": {
+            "p-locate": "^3.0.0",
+            "path-exists": "^3.0.0"
+          }
+        },
+        "lru-cache": {
+          "version": "5.1.1",
+          "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+          "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+          "dev": true,
+          "requires": {
+            "yallist": "^3.0.2"
+          }
+        },
+        "make-dir": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
+          "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
+          "dev": true,
+          "requires": {
+            "pify": "^4.0.1",
+            "semver": "^5.6.0"
+          },
+          "dependencies": {
+            "semver": {
+              "version": "5.7.1",
+              "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+              "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+              "dev": true
+            }
+          }
+        },
+        "micromatch": {
+          "version": "3.1.10",
+          "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
+          "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
+          "dev": true,
+          "requires": {
+            "arr-diff": "^4.0.0",
+            "array-unique": "^0.3.2",
+            "braces": "^2.3.1",
+            "define-property": "^2.0.2",
+            "extend-shallow": "^3.0.2",
+            "extglob": "^2.0.4",
+            "fragment-cache": "^0.2.1",
+            "kind-of": "^6.0.2",
+            "nanomatch": "^1.2.9",
+            "object.pick": "^1.3.0",
+            "regex-not": "^1.0.0",
+            "snapdragon": "^0.8.1",
+            "to-regex": "^3.0.2"
+          }
+        },
+        "mkdirp": {
+          "version": "0.5.6",
+          "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
+          "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
+          "dev": true,
+          "requires": {
+            "minimist": "^1.2.6"
+          }
+        },
+        "open": {
+          "version": "8.4.0",
+          "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz",
+          "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==",
+          "dev": true,
+          "requires": {
+            "define-lazy-prop": "^2.0.0",
+            "is-docker": "^2.1.1",
+            "is-wsl": "^2.2.0"
+          }
+        },
+        "p-locate": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
+          "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+          "dev": true,
+          "requires": {
+            "p-limit": "^2.0.0"
+          }
+        },
+        "path-exists": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+          "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
+          "dev": true
+        },
+        "pkg-dir": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz",
+          "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==",
+          "dev": true,
+          "requires": {
+            "find-up": "^3.0.0"
+          },
+          "dependencies": {
+            "find-up": {
+              "version": "3.0.0",
+              "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
+              "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+              "dev": true,
+              "requires": {
+                "locate-path": "^3.0.0"
+              }
+            }
+          }
+        },
+        "rimraf": {
+          "version": "2.7.1",
+          "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+          "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+          "dev": true,
+          "requires": {
+            "glob": "^7.1.3"
+          }
+        },
+        "schema-utils": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
+          "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
+          "dev": true,
+          "requires": {
+            "ajv": "^6.1.0",
+            "ajv-errors": "^1.0.0",
+            "ajv-keywords": "^3.1.0"
+          }
+        },
+        "serialize-javascript": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz",
+          "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==",
+          "dev": true,
+          "requires": {
+            "randombytes": "^2.1.0"
+          }
+        },
+        "source-map": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+          "dev": true
+        },
+        "ssri": {
+          "version": "6.0.2",
+          "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.2.tgz",
+          "integrity": "sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==",
+          "dev": true,
+          "requires": {
+            "figgy-pudding": "^3.5.1"
+          }
+        },
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
+        },
+        "tapable": {
+          "version": "1.1.3",
+          "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",
+          "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==",
+          "dev": true
+        },
+        "terser": {
+          "version": "4.8.0",
+          "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz",
+          "integrity": "sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==",
+          "dev": true,
+          "requires": {
+            "commander": "^2.20.0",
+            "source-map": "~0.6.1",
+            "source-map-support": "~0.5.12"
+          },
+          "dependencies": {
+            "commander": {
+              "version": "2.20.3",
+              "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+              "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+              "dev": true
+            }
+          }
+        },
+        "terser-webpack-plugin": {
+          "version": "1.4.5",
+          "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz",
+          "integrity": "sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw==",
+          "dev": true,
+          "requires": {
+            "cacache": "^12.0.2",
+            "find-cache-dir": "^2.1.0",
+            "is-wsl": "^1.1.0",
+            "schema-utils": "^1.0.0",
+            "serialize-javascript": "^4.0.0",
+            "source-map": "^0.6.1",
+            "terser": "^4.1.2",
+            "webpack-sources": "^1.4.0",
+            "worker-farm": "^1.7.0"
+          },
+          "dependencies": {
+            "is-wsl": {
+              "version": "1.1.0",
+              "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz",
+              "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=",
+              "dev": true
+            }
+          }
+        },
+        "to-regex-range": {
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
+          "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
+          "dev": true,
+          "requires": {
+            "is-number": "^3.0.0",
+            "repeat-string": "^1.6.1"
+          }
+        },
+        "webpack": {
+          "version": "4.46.0",
+          "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.46.0.tgz",
+          "integrity": "sha512-6jJuJjg8znb/xRItk7bkT0+Q7AHCYjjFnvKIWQPkNIOyRqoCGvkOs0ipeQzrqz4l5FtN5ZI/ukEHroeX/o1/5Q==",
+          "dev": true,
+          "requires": {
+            "@webassemblyjs/ast": "1.9.0",
+            "@webassemblyjs/helper-module-context": "1.9.0",
+            "@webassemblyjs/wasm-edit": "1.9.0",
+            "@webassemblyjs/wasm-parser": "1.9.0",
+            "acorn": "^6.4.1",
+            "ajv": "^6.10.2",
+            "ajv-keywords": "^3.4.1",
+            "chrome-trace-event": "^1.0.2",
+            "enhanced-resolve": "^4.5.0",
+            "eslint-scope": "^4.0.3",
+            "json-parse-better-errors": "^1.0.2",
+            "loader-runner": "^2.4.0",
+            "loader-utils": "^1.2.3",
+            "memory-fs": "^0.4.1",
+            "micromatch": "^3.1.10",
+            "mkdirp": "^0.5.3",
+            "neo-async": "^2.6.1",
+            "node-libs-browser": "^2.2.1",
+            "schema-utils": "^1.0.0",
+            "tapable": "^1.1.3",
+            "terser-webpack-plugin": "^1.4.3",
+            "watchpack": "^1.7.4",
+            "webpack-sources": "^1.4.1"
+          },
+          "dependencies": {
+            "watchpack": {
+              "version": "1.7.5",
+              "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz",
+              "integrity": "sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==",
+              "dev": true,
+              "requires": {
+                "chokidar": "^3.4.1",
+                "graceful-fs": "^4.1.2",
+                "neo-async": "^2.5.0",
+                "watchpack-chokidar2": "^2.0.1"
+              }
+            }
+          }
+        },
+        "ws": {
+          "version": "8.6.0",
+          "resolved": "https://registry.npmjs.org/ws/-/ws-8.6.0.tgz",
+          "integrity": "sha512-AzmM3aH3gk0aX7/rZLYvjdvZooofDu3fFOzGqcSnQ1tOcTWwhM/o+q++E8mAyVVIyUdajrkzWUGftaVSDLn1bw==",
+          "dev": true
+        },
+        "yallist": {
+          "version": "3.1.1",
+          "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+          "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+          "dev": true
+        }
+      }
+    },
+    "@storybook/csf": {
+      "version": "0.0.2--canary.4566f4d.1",
+      "resolved": "https://registry.npmjs.org/@storybook/csf/-/csf-0.0.2--canary.4566f4d.1.tgz",
+      "integrity": "sha512-9OVvMVh3t9znYZwb0Svf/YQoxX2gVOeQTGe2bses2yj+a3+OJnCrUF3/hGv6Em7KujtOdL2LL+JnG49oMVGFgQ==",
+      "dev": true,
+      "requires": {
+        "lodash": "^4.17.15"
+      }
+    },
+    "@storybook/csf-tools": {
+      "version": "6.5.4",
+      "resolved": "https://registry.npmjs.org/@storybook/csf-tools/-/csf-tools-6.5.4.tgz",
+      "integrity": "sha512-mCAO8Ddig5PzXiI4HxiWhdLmPPpaovzVxqdh7g31k3fhpnRkQrKwftNGTW/ArFDdisMjB1+gm+ZOVACZg0pO4w==",
+      "dev": true,
+      "requires": {
+        "@babel/core": "^7.12.10",
+        "@babel/generator": "^7.12.11",
+        "@babel/parser": "^7.12.11",
+        "@babel/plugin-transform-react-jsx": "^7.12.12",
+        "@babel/preset-env": "^7.12.11",
+        "@babel/traverse": "^7.12.11",
+        "@babel/types": "^7.12.11",
+        "@storybook/csf": "0.0.2--canary.4566f4d.1",
+        "@storybook/mdx1-csf": "^0.0.1-canary.1.eed86d7.0",
+        "core-js": "^3.8.2",
+        "fs-extra": "^9.0.1",
+        "global": "^4.4.0",
+        "regenerator-runtime": "^0.13.7",
+        "ts-dedent": "^2.0.0"
+      },
+      "dependencies": {
+        "fs-extra": {
+          "version": "9.1.0",
+          "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
+          "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
+          "dev": true,
+          "requires": {
+            "at-least-node": "^1.0.0",
+            "graceful-fs": "^4.2.0",
+            "jsonfile": "^6.0.1",
+            "universalify": "^2.0.0"
+          }
+        }
+      }
+    },
+    "@storybook/docs-tools": {
+      "version": "6.5.4",
+      "resolved": "https://registry.npmjs.org/@storybook/docs-tools/-/docs-tools-6.5.4.tgz",
+      "integrity": "sha512-XiCiCKwalbPrhcD9jhMM9IMOdOplUPNnwhW//VmHvWJ2j+JNiQVX42vfecl4hKy8wx63S/AzaGKw2yuavUAiHQ==",
+      "dev": true,
+      "requires": {
+        "@babel/core": "^7.12.10",
+        "@storybook/csf": "0.0.2--canary.4566f4d.1",
+        "@storybook/store": "6.5.4",
+        "core-js": "^3.8.2",
+        "doctrine": "^3.0.0",
+        "lodash": "^4.17.21",
+        "regenerator-runtime": "^0.13.7"
+      }
+    },
+    "@storybook/instrumenter": {
+      "version": "6.5.4",
+      "resolved": "https://registry.npmjs.org/@storybook/instrumenter/-/instrumenter-6.5.4.tgz",
+      "integrity": "sha512-lCpg71qQsZV7uwEECPlbdro0FPKiJG/RykWTtvrj03l+r0OaHhfGos9Bm1ClooJUkMovo+sBxz6/8OxeuCCHSA==",
+      "dev": true,
+      "requires": {
+        "@storybook/addons": "6.5.4",
+        "@storybook/client-logger": "6.5.4",
+        "@storybook/core-events": "6.5.4",
+        "core-js": "^3.8.2",
+        "global": "^4.4.0"
+      }
+    },
+    "@storybook/manager-webpack4": {
+      "version": "6.5.4",
+      "resolved": "https://registry.npmjs.org/@storybook/manager-webpack4/-/manager-webpack4-6.5.4.tgz",
+      "integrity": "sha512-JgFAlnnsOV6+L0ShDApwbDDdzoEAwuy01+TLUVdl60rlpbcBaasBMkZYAy1iwKZ59gCT51GocGMGDXBVeUJq9g==",
+      "dev": true,
+      "requires": {
+        "@babel/core": "^7.12.10",
+        "@babel/plugin-transform-template-literals": "^7.12.1",
+        "@babel/preset-react": "^7.12.10",
+        "@storybook/addons": "6.5.4",
+        "@storybook/core-client": "6.5.4",
+        "@storybook/core-common": "6.5.4",
+        "@storybook/node-logger": "6.5.4",
+        "@storybook/theming": "6.5.4",
+        "@storybook/ui": "6.5.4",
+        "@types/node": "^14.0.10 || ^16.0.0",
+        "@types/webpack": "^4.41.26",
+        "babel-loader": "^8.0.0",
+        "case-sensitive-paths-webpack-plugin": "^2.3.0",
+        "chalk": "^4.1.0",
+        "core-js": "^3.8.2",
+        "css-loader": "^3.6.0",
+        "express": "^4.17.1",
+        "file-loader": "^6.2.0",
+        "find-up": "^5.0.0",
+        "fs-extra": "^9.0.1",
+        "html-webpack-plugin": "^4.0.0",
+        "node-fetch": "^2.6.7",
+        "pnp-webpack-plugin": "1.6.4",
+        "read-pkg-up": "^7.0.1",
+        "regenerator-runtime": "^0.13.7",
+        "resolve-from": "^5.0.0",
+        "style-loader": "^1.3.0",
+        "telejson": "^6.0.8",
+        "terser-webpack-plugin": "^4.2.3",
+        "ts-dedent": "^2.0.0",
+        "url-loader": "^4.1.1",
+        "util-deprecate": "^1.0.2",
+        "webpack": "4",
+        "webpack-dev-middleware": "^3.7.3",
+        "webpack-virtual-modules": "^0.2.2"
+      },
+      "dependencies": {
+        "@types/node": {
+          "version": "16.11.36",
+          "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.36.tgz",
+          "integrity": "sha512-FR5QJe+TaoZ2GsMHkjuwoNabr+UrJNRr2HNOo+r/7vhcuntM6Ee/pRPOnRhhL2XE9OOvX9VLEq+BcXl3VjNoWA==",
+          "dev": true
+        },
+        "@webassemblyjs/ast": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz",
+          "integrity": "sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA==",
+          "dev": true,
+          "requires": {
+            "@webassemblyjs/helper-module-context": "1.9.0",
+            "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
+            "@webassemblyjs/wast-parser": "1.9.0"
+          }
+        },
+        "@webassemblyjs/helper-api-error": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz",
+          "integrity": "sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw==",
+          "dev": true
+        },
+        "@webassemblyjs/helper-buffer": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz",
+          "integrity": "sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA==",
+          "dev": true
+        },
+        "@webassemblyjs/helper-wasm-bytecode": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz",
+          "integrity": "sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw==",
+          "dev": true
+        },
+        "@webassemblyjs/helper-wasm-section": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz",
+          "integrity": "sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw==",
+          "dev": true,
+          "requires": {
+            "@webassemblyjs/ast": "1.9.0",
+            "@webassemblyjs/helper-buffer": "1.9.0",
+            "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
+            "@webassemblyjs/wasm-gen": "1.9.0"
+          }
+        },
+        "@webassemblyjs/ieee754": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz",
+          "integrity": "sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg==",
+          "dev": true,
+          "requires": {
+            "@xtuc/ieee754": "^1.2.0"
+          }
+        },
+        "@webassemblyjs/leb128": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.9.0.tgz",
+          "integrity": "sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw==",
+          "dev": true,
+          "requires": {
+            "@xtuc/long": "4.2.2"
+          }
+        },
+        "@webassemblyjs/utf8": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.9.0.tgz",
+          "integrity": "sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w==",
+          "dev": true
+        },
+        "@webassemblyjs/wasm-edit": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz",
+          "integrity": "sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw==",
+          "dev": true,
+          "requires": {
+            "@webassemblyjs/ast": "1.9.0",
+            "@webassemblyjs/helper-buffer": "1.9.0",
+            "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
+            "@webassemblyjs/helper-wasm-section": "1.9.0",
+            "@webassemblyjs/wasm-gen": "1.9.0",
+            "@webassemblyjs/wasm-opt": "1.9.0",
+            "@webassemblyjs/wasm-parser": "1.9.0",
+            "@webassemblyjs/wast-printer": "1.9.0"
+          }
+        },
+        "@webassemblyjs/wasm-gen": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz",
+          "integrity": "sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA==",
+          "dev": true,
+          "requires": {
+            "@webassemblyjs/ast": "1.9.0",
+            "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
+            "@webassemblyjs/ieee754": "1.9.0",
+            "@webassemblyjs/leb128": "1.9.0",
+            "@webassemblyjs/utf8": "1.9.0"
+          }
+        },
+        "@webassemblyjs/wasm-opt": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz",
+          "integrity": "sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A==",
+          "dev": true,
+          "requires": {
+            "@webassemblyjs/ast": "1.9.0",
+            "@webassemblyjs/helper-buffer": "1.9.0",
+            "@webassemblyjs/wasm-gen": "1.9.0",
+            "@webassemblyjs/wasm-parser": "1.9.0"
+          }
+        },
+        "@webassemblyjs/wasm-parser": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz",
+          "integrity": "sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA==",
+          "dev": true,
+          "requires": {
+            "@webassemblyjs/ast": "1.9.0",
+            "@webassemblyjs/helper-api-error": "1.9.0",
+            "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
+            "@webassemblyjs/ieee754": "1.9.0",
+            "@webassemblyjs/leb128": "1.9.0",
+            "@webassemblyjs/utf8": "1.9.0"
+          }
+        },
+        "@webassemblyjs/wast-printer": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz",
+          "integrity": "sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA==",
+          "dev": true,
+          "requires": {
+            "@webassemblyjs/ast": "1.9.0",
+            "@webassemblyjs/wast-parser": "1.9.0",
+            "@xtuc/long": "4.2.2"
+          }
+        },
+        "acorn": {
+          "version": "6.4.2",
+          "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz",
+          "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==",
+          "dev": true
+        },
+        "ansi-styles": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+          "dev": true,
+          "requires": {
+            "color-convert": "^2.0.1"
+          }
+        },
+        "braces": {
+          "version": "2.3.2",
+          "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
+          "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
+          "dev": true,
+          "requires": {
+            "arr-flatten": "^1.1.0",
+            "array-unique": "^0.3.2",
+            "extend-shallow": "^2.0.1",
+            "fill-range": "^4.0.0",
+            "isobject": "^3.0.1",
+            "repeat-element": "^1.1.2",
+            "snapdragon": "^0.8.1",
+            "snapdragon-node": "^2.0.1",
+            "split-string": "^3.0.2",
+            "to-regex": "^3.0.1"
+          },
+          "dependencies": {
+            "extend-shallow": {
+              "version": "2.0.1",
+              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+              "dev": true,
+              "requires": {
+                "is-extendable": "^0.1.0"
+              }
+            }
+          }
+        },
+        "chalk": {
+          "version": "4.1.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+          "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
+          }
+        },
+        "chownr": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
+          "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
+          "dev": true
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "dev": true,
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
+        },
+        "commander": {
+          "version": "2.20.3",
+          "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+          "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+          "dev": true
+        },
+        "css-loader": {
+          "version": "3.6.0",
+          "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-3.6.0.tgz",
+          "integrity": "sha512-M5lSukoWi1If8dhQAUCvj4H8vUt3vOnwbQBH9DdTm/s4Ym2B/3dPMtYZeJmq7Q3S3Pa+I94DcZ7pc9bP14cWIQ==",
+          "dev": true,
+          "requires": {
+            "camelcase": "^5.3.1",
+            "cssesc": "^3.0.0",
+            "icss-utils": "^4.1.1",
+            "loader-utils": "^1.2.3",
+            "normalize-path": "^3.0.0",
+            "postcss": "^7.0.32",
+            "postcss-modules-extract-imports": "^2.0.0",
+            "postcss-modules-local-by-default": "^3.0.2",
+            "postcss-modules-scope": "^2.2.0",
+            "postcss-modules-values": "^3.0.0",
+            "postcss-value-parser": "^4.1.0",
+            "schema-utils": "^2.7.0",
+            "semver": "^6.3.0"
+          }
+        },
+        "enhanced-resolve": {
+          "version": "4.5.0",
+          "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz",
+          "integrity": "sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg==",
+          "dev": true,
+          "requires": {
+            "graceful-fs": "^4.1.2",
+            "memory-fs": "^0.5.0",
+            "tapable": "^1.0.0"
+          },
+          "dependencies": {
+            "memory-fs": {
+              "version": "0.5.0",
+              "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz",
+              "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==",
+              "dev": true,
+              "requires": {
+                "errno": "^0.1.3",
+                "readable-stream": "^2.0.1"
+              }
+            }
+          }
+        },
+        "eslint-scope": {
+          "version": "4.0.3",
+          "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz",
+          "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==",
+          "dev": true,
+          "requires": {
+            "esrecurse": "^4.1.0",
+            "estraverse": "^4.1.1"
+          }
+        },
+        "fill-range": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
+          "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
+          "dev": true,
+          "requires": {
+            "extend-shallow": "^2.0.1",
+            "is-number": "^3.0.0",
+            "repeat-string": "^1.6.1",
+            "to-regex-range": "^2.1.0"
+          },
+          "dependencies": {
+            "extend-shallow": {
+              "version": "2.0.1",
+              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+              "dev": true,
+              "requires": {
+                "is-extendable": "^0.1.0"
+              }
+            }
+          }
+        },
+        "find-up": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+          "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+          "dev": true,
+          "requires": {
+            "locate-path": "^6.0.0",
+            "path-exists": "^4.0.0"
+          }
+        },
+        "fs-extra": {
+          "version": "9.1.0",
+          "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
+          "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
+          "dev": true,
+          "requires": {
+            "at-least-node": "^1.0.0",
+            "graceful-fs": "^4.2.0",
+            "jsonfile": "^6.0.1",
+            "universalify": "^2.0.0"
+          }
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
+        "icss-utils": {
+          "version": "4.1.1",
+          "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-4.1.1.tgz",
+          "integrity": "sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA==",
+          "dev": true,
+          "requires": {
+            "postcss": "^7.0.14"
+          }
+        },
+        "is-number": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
+          "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
+          "dev": true,
+          "requires": {
+            "kind-of": "^3.0.2"
+          },
+          "dependencies": {
+            "kind-of": {
+              "version": "3.2.2",
+              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+              "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+              "dev": true,
+              "requires": {
+                "is-buffer": "^1.1.5"
+              }
+            }
+          }
+        },
+        "is-wsl": {
+          "version": "1.1.0",
+          "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz",
+          "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=",
+          "dev": true
+        },
+        "jest-worker": {
+          "version": "26.6.2",
+          "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz",
+          "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==",
+          "dev": true,
+          "requires": {
+            "@types/node": "*",
+            "merge-stream": "^2.0.0",
+            "supports-color": "^7.0.0"
+          }
+        },
+        "json5": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
+          "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
+          "dev": true,
+          "requires": {
+            "minimist": "^1.2.0"
+          }
+        },
+        "loader-runner": {
+          "version": "2.4.0",
+          "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz",
+          "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==",
+          "dev": true
+        },
+        "loader-utils": {
+          "version": "1.4.0",
+          "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
+          "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==",
+          "dev": true,
+          "requires": {
+            "big.js": "^5.2.2",
+            "emojis-list": "^3.0.0",
+            "json5": "^1.0.1"
+          }
+        },
+        "locate-path": {
+          "version": "6.0.0",
+          "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+          "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+          "dev": true,
+          "requires": {
+            "p-locate": "^5.0.0"
+          }
+        },
+        "lru-cache": {
+          "version": "5.1.1",
+          "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+          "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+          "dev": true,
+          "requires": {
+            "yallist": "^3.0.2"
+          }
+        },
+        "make-dir": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
+          "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
+          "dev": true,
+          "requires": {
+            "pify": "^4.0.1",
+            "semver": "^5.6.0"
+          },
+          "dependencies": {
+            "semver": {
+              "version": "5.7.1",
+              "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+              "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+              "dev": true
+            }
+          }
+        },
+        "micromatch": {
+          "version": "3.1.10",
+          "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
+          "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
+          "dev": true,
+          "requires": {
+            "arr-diff": "^4.0.0",
+            "array-unique": "^0.3.2",
+            "braces": "^2.3.1",
+            "define-property": "^2.0.2",
+            "extend-shallow": "^3.0.2",
+            "extglob": "^2.0.4",
+            "fragment-cache": "^0.2.1",
+            "kind-of": "^6.0.2",
+            "nanomatch": "^1.2.9",
+            "object.pick": "^1.3.0",
+            "regex-not": "^1.0.0",
+            "snapdragon": "^0.8.1",
+            "to-regex": "^3.0.2"
+          }
+        },
+        "mime": {
+          "version": "2.6.0",
+          "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz",
+          "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==",
+          "dev": true
+        },
+        "mkdirp": {
+          "version": "0.5.6",
+          "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
+          "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
+          "dev": true,
+          "requires": {
+            "minimist": "^1.2.6"
+          }
+        },
+        "p-limit": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+          "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+          "dev": true,
+          "requires": {
+            "yocto-queue": "^0.1.0"
+          }
+        },
+        "p-locate": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+          "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+          "dev": true,
+          "requires": {
+            "p-limit": "^3.0.2"
+          }
+        },
+        "picocolors": {
+          "version": "0.2.1",
+          "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz",
+          "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==",
+          "dev": true
+        },
+        "pkg-dir": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz",
+          "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==",
+          "dev": true,
+          "requires": {
+            "find-up": "^3.0.0"
+          },
+          "dependencies": {
+            "find-up": {
+              "version": "3.0.0",
+              "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
+              "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+              "dev": true,
+              "requires": {
+                "locate-path": "^3.0.0"
+              }
+            },
+            "locate-path": {
+              "version": "3.0.0",
+              "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
+              "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+              "dev": true,
+              "requires": {
+                "p-locate": "^3.0.0",
+                "path-exists": "^3.0.0"
+              }
+            },
+            "p-limit": {
+              "version": "2.3.0",
+              "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+              "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+              "dev": true,
+              "requires": {
+                "p-try": "^2.0.0"
+              }
+            },
+            "p-locate": {
+              "version": "3.0.0",
+              "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
+              "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+              "dev": true,
+              "requires": {
+                "p-limit": "^2.0.0"
+              }
+            },
+            "path-exists": {
+              "version": "3.0.0",
+              "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+              "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
+              "dev": true
+            }
+          }
+        },
+        "postcss": {
+          "version": "7.0.39",
+          "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz",
+          "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==",
+          "dev": true,
+          "requires": {
+            "picocolors": "^0.2.1",
+            "source-map": "^0.6.1"
+          }
+        },
+        "postcss-modules-extract-imports": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz",
+          "integrity": "sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ==",
+          "dev": true,
+          "requires": {
+            "postcss": "^7.0.5"
+          }
+        },
+        "postcss-modules-local-by-default": {
+          "version": "3.0.3",
+          "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.3.tgz",
+          "integrity": "sha512-e3xDq+LotiGesympRlKNgaJ0PCzoUIdpH0dj47iWAui/kyTgh3CiAr1qP54uodmJhl6p9rN6BoNcdEDVJx9RDw==",
+          "dev": true,
+          "requires": {
+            "icss-utils": "^4.1.1",
+            "postcss": "^7.0.32",
+            "postcss-selector-parser": "^6.0.2",
+            "postcss-value-parser": "^4.1.0"
+          }
+        },
+        "postcss-modules-scope": {
+          "version": "2.2.0",
+          "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-2.2.0.tgz",
+          "integrity": "sha512-YyEgsTMRpNd+HmyC7H/mh3y+MeFWevy7V1evVhJWewmMbjDHIbZbOXICC2y+m1xI1UVfIT1HMW/O04Hxyu9oXQ==",
+          "dev": true,
+          "requires": {
+            "postcss": "^7.0.6",
+            "postcss-selector-parser": "^6.0.0"
+          }
+        },
+        "postcss-modules-values": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-3.0.0.tgz",
+          "integrity": "sha512-1//E5jCBrZ9DmRX+zCtmQtRSV6PV42Ix7Bzj9GbwJceduuf7IqP8MgeTXuRDHOWj2m0VzZD5+roFWDuU8RQjcg==",
+          "dev": true,
+          "requires": {
+            "icss-utils": "^4.0.0",
+            "postcss": "^7.0.6"
+          }
+        },
+        "resolve-from": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
+          "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
+          "dev": true
+        },
+        "rimraf": {
+          "version": "2.7.1",
+          "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+          "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+          "dev": true,
+          "requires": {
+            "glob": "^7.1.3"
+          }
+        },
+        "schema-utils": {
+          "version": "2.7.1",
+          "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz",
+          "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==",
+          "dev": true,
+          "requires": {
+            "@types/json-schema": "^7.0.5",
+            "ajv": "^6.12.4",
+            "ajv-keywords": "^3.5.2"
+          }
+        },
+        "semver": {
+          "version": "6.3.0",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+          "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+          "dev": true
+        },
+        "serialize-javascript": {
+          "version": "5.0.1",
+          "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz",
+          "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==",
+          "dev": true,
+          "requires": {
+            "randombytes": "^2.1.0"
+          }
+        },
+        "source-map": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+          "dev": true
+        },
+        "ssri": {
+          "version": "6.0.2",
+          "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.2.tgz",
+          "integrity": "sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==",
+          "dev": true,
+          "requires": {
+            "figgy-pudding": "^3.5.1"
+          }
+        },
+        "style-loader": {
+          "version": "1.3.0",
+          "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-1.3.0.tgz",
+          "integrity": "sha512-V7TCORko8rs9rIqkSrlMfkqA63DfoGBBJmK1kKGCcSi+BWb4cqz0SRsnp4l6rU5iwOEd0/2ePv68SV22VXon4Q==",
+          "dev": true,
+          "requires": {
+            "loader-utils": "^2.0.0",
+            "schema-utils": "^2.7.0"
+          },
+          "dependencies": {
+            "json5": {
+              "version": "2.2.1",
+              "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz",
+              "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==",
+              "dev": true
+            },
+            "loader-utils": {
+              "version": "2.0.2",
+              "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz",
+              "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==",
+              "dev": true,
+              "requires": {
+                "big.js": "^5.2.2",
+                "emojis-list": "^3.0.0",
+                "json5": "^2.1.2"
+              }
+            }
+          }
+        },
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
+        },
+        "tapable": {
+          "version": "1.1.3",
+          "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",
+          "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==",
+          "dev": true
+        },
+        "terser-webpack-plugin": {
+          "version": "4.2.3",
+          "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-4.2.3.tgz",
+          "integrity": "sha512-jTgXh40RnvOrLQNgIkwEKnQ8rmHjHK4u+6UBEi+W+FPmvb+uo+chJXntKe7/3lW5mNysgSWD60KyesnhW8D6MQ==",
+          "dev": true,
+          "requires": {
+            "cacache": "^15.0.5",
+            "find-cache-dir": "^3.3.1",
+            "jest-worker": "^26.5.0",
+            "p-limit": "^3.0.2",
+            "schema-utils": "^3.0.0",
+            "serialize-javascript": "^5.0.1",
+            "source-map": "^0.6.1",
+            "terser": "^5.3.4",
+            "webpack-sources": "^1.4.3"
+          },
+          "dependencies": {
+            "schema-utils": {
+              "version": "3.1.1",
+              "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz",
+              "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==",
+              "dev": true,
+              "requires": {
+                "@types/json-schema": "^7.0.8",
+                "ajv": "^6.12.5",
+                "ajv-keywords": "^3.5.2"
+              }
+            }
+          }
+        },
+        "to-regex-range": {
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
+          "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
+          "dev": true,
+          "requires": {
+            "is-number": "^3.0.0",
+            "repeat-string": "^1.6.1"
+          }
+        },
+        "watchpack": {
+          "version": "1.7.5",
+          "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz",
+          "integrity": "sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==",
+          "dev": true,
+          "requires": {
+            "chokidar": "^3.4.1",
+            "graceful-fs": "^4.1.2",
+            "neo-async": "^2.5.0",
+            "watchpack-chokidar2": "^2.0.1"
+          }
+        },
+        "webpack": {
+          "version": "4.46.0",
+          "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.46.0.tgz",
+          "integrity": "sha512-6jJuJjg8znb/xRItk7bkT0+Q7AHCYjjFnvKIWQPkNIOyRqoCGvkOs0ipeQzrqz4l5FtN5ZI/ukEHroeX/o1/5Q==",
+          "dev": true,
+          "requires": {
+            "@webassemblyjs/ast": "1.9.0",
+            "@webassemblyjs/helper-module-context": "1.9.0",
+            "@webassemblyjs/wasm-edit": "1.9.0",
+            "@webassemblyjs/wasm-parser": "1.9.0",
+            "acorn": "^6.4.1",
+            "ajv": "^6.10.2",
+            "ajv-keywords": "^3.4.1",
+            "chrome-trace-event": "^1.0.2",
+            "enhanced-resolve": "^4.5.0",
+            "eslint-scope": "^4.0.3",
+            "json-parse-better-errors": "^1.0.2",
+            "loader-runner": "^2.4.0",
+            "loader-utils": "^1.2.3",
+            "memory-fs": "^0.4.1",
+            "micromatch": "^3.1.10",
+            "mkdirp": "^0.5.3",
+            "neo-async": "^2.6.1",
+            "node-libs-browser": "^2.2.1",
+            "schema-utils": "^1.0.0",
+            "tapable": "^1.1.3",
+            "terser-webpack-plugin": "^1.4.3",
+            "watchpack": "^1.7.4",
+            "webpack-sources": "^1.4.1"
+          },
+          "dependencies": {
+            "cacache": {
+              "version": "12.0.4",
+              "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz",
+              "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==",
+              "dev": true,
+              "requires": {
+                "bluebird": "^3.5.5",
+                "chownr": "^1.1.1",
+                "figgy-pudding": "^3.5.1",
+                "glob": "^7.1.4",
+                "graceful-fs": "^4.1.15",
+                "infer-owner": "^1.0.3",
+                "lru-cache": "^5.1.1",
+                "mississippi": "^3.0.0",
+                "mkdirp": "^0.5.1",
+                "move-concurrently": "^1.0.1",
+                "promise-inflight": "^1.0.1",
+                "rimraf": "^2.6.3",
+                "ssri": "^6.0.1",
+                "unique-filename": "^1.1.1",
+                "y18n": "^4.0.0"
+              }
+            },
+            "find-cache-dir": {
+              "version": "2.1.0",
+              "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz",
+              "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==",
+              "dev": true,
+              "requires": {
+                "commondir": "^1.0.1",
+                "make-dir": "^2.0.0",
+                "pkg-dir": "^3.0.0"
+              }
+            },
+            "schema-utils": {
+              "version": "1.0.0",
+              "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
+              "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
+              "dev": true,
+              "requires": {
+                "ajv": "^6.1.0",
+                "ajv-errors": "^1.0.0",
+                "ajv-keywords": "^3.1.0"
+              }
+            },
+            "serialize-javascript": {
+              "version": "4.0.0",
+              "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz",
+              "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==",
+              "dev": true,
+              "requires": {
+                "randombytes": "^2.1.0"
+              }
+            },
+            "terser": {
+              "version": "4.8.0",
+              "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz",
+              "integrity": "sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==",
+              "dev": true,
+              "requires": {
+                "commander": "^2.20.0",
+                "source-map": "~0.6.1",
+                "source-map-support": "~0.5.12"
+              }
+            },
+            "terser-webpack-plugin": {
+              "version": "1.4.5",
+              "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz",
+              "integrity": "sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw==",
+              "dev": true,
+              "requires": {
+                "cacache": "^12.0.2",
+                "find-cache-dir": "^2.1.0",
+                "is-wsl": "^1.1.0",
+                "schema-utils": "^1.0.0",
+                "serialize-javascript": "^4.0.0",
+                "source-map": "^0.6.1",
+                "terser": "^4.1.2",
+                "webpack-sources": "^1.4.0",
+                "worker-farm": "^1.7.0"
+              }
+            }
+          }
+        },
+        "webpack-dev-middleware": {
+          "version": "3.7.3",
+          "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.7.3.tgz",
+          "integrity": "sha512-djelc/zGiz9nZj/U7PTBi2ViorGJXEWo/3ltkPbDyxCXhhEXkW0ce99falaok4TPj+AsxLiXJR0EBOb0zh9fKQ==",
+          "dev": true,
+          "requires": {
+            "memory-fs": "^0.4.1",
+            "mime": "^2.4.4",
+            "mkdirp": "^0.5.1",
+            "range-parser": "^1.2.1",
+            "webpack-log": "^2.0.0"
+          }
+        },
+        "yallist": {
+          "version": "3.1.1",
+          "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+          "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+          "dev": true
+        }
+      }
+    },
+    "@storybook/manager-webpack5": {
+      "version": "6.5.4",
+      "resolved": "https://registry.npmjs.org/@storybook/manager-webpack5/-/manager-webpack5-6.5.4.tgz",
+      "integrity": "sha512-UwGnwkHV5CFuHmrsQnjQ0IQj6uXZFopCB7e96HU0YwOBiBA9ABlbJpUrxC/N7vO+OhTNtH6lcwHx0eJbbnnuxw==",
+      "dev": true,
+      "requires": {
+        "@babel/core": "^7.12.10",
+        "@babel/plugin-transform-template-literals": "^7.12.1",
+        "@babel/preset-react": "^7.12.10",
+        "@storybook/addons": "6.5.4",
+        "@storybook/core-client": "6.5.4",
+        "@storybook/core-common": "6.5.4",
+        "@storybook/node-logger": "6.5.4",
+        "@storybook/theming": "6.5.4",
+        "@storybook/ui": "6.5.4",
+        "@types/node": "^14.0.10 || ^16.0.0",
+        "babel-loader": "^8.0.0",
+        "case-sensitive-paths-webpack-plugin": "^2.3.0",
+        "chalk": "^4.1.0",
+        "core-js": "^3.8.2",
+        "css-loader": "^5.0.1",
+        "express": "^4.17.1",
+        "find-up": "^5.0.0",
+        "fs-extra": "^9.0.1",
+        "html-webpack-plugin": "^5.0.0",
+        "node-fetch": "^2.6.7",
+        "process": "^0.11.10",
+        "read-pkg-up": "^7.0.1",
+        "regenerator-runtime": "^0.13.7",
+        "resolve-from": "^5.0.0",
+        "style-loader": "^2.0.0",
+        "telejson": "^6.0.8",
+        "terser-webpack-plugin": "^5.0.3",
+        "ts-dedent": "^2.0.0",
+        "util-deprecate": "^1.0.2",
+        "webpack": "^5.9.0",
+        "webpack-dev-middleware": "^4.1.0",
+        "webpack-virtual-modules": "^0.4.1"
+      },
+      "dependencies": {
+        "@types/html-minifier-terser": {
+          "version": "6.1.0",
+          "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz",
+          "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==",
+          "dev": true
+        },
+        "@types/node": {
+          "version": "16.11.36",
+          "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.36.tgz",
+          "integrity": "sha512-FR5QJe+TaoZ2GsMHkjuwoNabr+UrJNRr2HNOo+r/7vhcuntM6Ee/pRPOnRhhL2XE9OOvX9VLEq+BcXl3VjNoWA==",
+          "dev": true
+        },
+        "ansi-styles": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+          "dev": true,
+          "requires": {
+            "color-convert": "^2.0.1"
+          }
+        },
+        "chalk": {
+          "version": "4.1.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+          "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
+          }
+        },
+        "clean-css": {
+          "version": "5.3.0",
+          "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.0.tgz",
+          "integrity": "sha512-YYuuxv4H/iNb1Z/5IbMRoxgrzjWGhOEFfd+groZ5dMCVkpENiMZmwspdrzBo9286JjM1gZJPAyL7ZIdzuvu2AQ==",
+          "dev": true,
+          "requires": {
+            "source-map": "~0.6.0"
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "dev": true,
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
+        },
+        "commander": {
+          "version": "8.3.0",
+          "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
+          "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==",
+          "dev": true
+        },
+        "css-loader": {
+          "version": "5.2.7",
+          "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-5.2.7.tgz",
+          "integrity": "sha512-Q7mOvpBNBG7YrVGMxRxcBJZFL75o+cH2abNASdibkj/fffYD8qWbInZrD0S9ccI6vZclF3DsHE7njGlLtaHbhg==",
+          "dev": true,
+          "requires": {
+            "icss-utils": "^5.1.0",
+            "loader-utils": "^2.0.0",
+            "postcss": "^8.2.15",
+            "postcss-modules-extract-imports": "^3.0.0",
+            "postcss-modules-local-by-default": "^4.0.0",
+            "postcss-modules-scope": "^3.0.0",
+            "postcss-modules-values": "^4.0.0",
+            "postcss-value-parser": "^4.1.0",
+            "schema-utils": "^3.0.0",
+            "semver": "^7.3.5"
+          }
+        },
+        "find-up": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+          "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+          "dev": true,
+          "requires": {
+            "locate-path": "^6.0.0",
+            "path-exists": "^4.0.0"
+          }
+        },
+        "fs-extra": {
+          "version": "9.1.0",
+          "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
+          "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
+          "dev": true,
+          "requires": {
+            "at-least-node": "^1.0.0",
+            "graceful-fs": "^4.2.0",
+            "jsonfile": "^6.0.1",
+            "universalify": "^2.0.0"
+          }
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
+        "html-minifier-terser": {
+          "version": "6.1.0",
+          "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz",
+          "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==",
+          "dev": true,
+          "requires": {
+            "camel-case": "^4.1.2",
+            "clean-css": "^5.2.2",
+            "commander": "^8.3.0",
+            "he": "^1.2.0",
+            "param-case": "^3.0.4",
+            "relateurl": "^0.2.7",
+            "terser": "^5.10.0"
+          }
+        },
+        "html-webpack-plugin": {
+          "version": "5.5.0",
+          "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz",
+          "integrity": "sha512-sy88PC2cRTVxvETRgUHFrL4No3UxvcH8G1NepGhqaTT+GXN2kTamqasot0inS5hXeg1cMbFDt27zzo9p35lZVw==",
+          "dev": true,
+          "requires": {
+            "@types/html-minifier-terser": "^6.0.0",
+            "html-minifier-terser": "^6.0.2",
+            "lodash": "^4.17.21",
+            "pretty-error": "^4.0.0",
+            "tapable": "^2.0.0"
+          }
+        },
+        "htmlparser2": {
+          "version": "6.1.0",
+          "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz",
+          "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==",
+          "dev": true,
+          "requires": {
+            "domelementtype": "^2.0.1",
+            "domhandler": "^4.0.0",
+            "domutils": "^2.5.2",
+            "entities": "^2.0.0"
+          }
+        },
+        "locate-path": {
+          "version": "6.0.0",
+          "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+          "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+          "dev": true,
+          "requires": {
+            "p-locate": "^5.0.0"
+          }
+        },
+        "p-limit": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+          "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+          "dev": true,
+          "requires": {
+            "yocto-queue": "^0.1.0"
+          }
+        },
+        "p-locate": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+          "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+          "dev": true,
+          "requires": {
+            "p-limit": "^3.0.2"
+          }
+        },
+        "pretty-error": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz",
+          "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==",
+          "dev": true,
+          "requires": {
+            "lodash": "^4.17.20",
+            "renderkid": "^3.0.0"
+          }
+        },
+        "renderkid": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz",
+          "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==",
+          "dev": true,
+          "requires": {
+            "css-select": "^4.1.3",
+            "dom-converter": "^0.2.0",
+            "htmlparser2": "^6.1.0",
+            "lodash": "^4.17.21",
+            "strip-ansi": "^6.0.1"
+          }
+        },
+        "resolve-from": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
+          "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
+          "dev": true
+        },
+        "source-map": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+          "dev": true
+        },
+        "source-map-support": {
+          "version": "0.5.21",
+          "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
+          "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+          "dev": true,
+          "requires": {
+            "buffer-from": "^1.0.0",
+            "source-map": "^0.6.0"
+          }
+        },
+        "style-loader": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-2.0.0.tgz",
+          "integrity": "sha512-Z0gYUJmzZ6ZdRUqpg1r8GsaFKypE+3xAzuFeMuoHgjc9KZv3wMyCRjQIWEbhoFSq7+7yoHXySDJyyWQaPajeiQ==",
+          "dev": true,
+          "requires": {
+            "loader-utils": "^2.0.0",
+            "schema-utils": "^3.0.0"
+          }
+        },
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
+        },
+        "terser": {
+          "version": "5.13.1",
+          "resolved": "https://registry.npmjs.org/terser/-/terser-5.13.1.tgz",
+          "integrity": "sha512-hn4WKOfwnwbYfe48NgrQjqNOH9jzLqRcIfbYytOXCOv46LBfWr9bDS17MQqOi+BWGD0sJK3Sj5NC/gJjiojaoA==",
+          "dev": true,
+          "requires": {
+            "acorn": "^8.5.0",
+            "commander": "^2.20.0",
+            "source-map": "~0.8.0-beta.0",
+            "source-map-support": "~0.5.20"
+          },
+          "dependencies": {
+            "commander": {
+              "version": "2.20.3",
+              "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+              "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+              "dev": true
+            },
+            "source-map": {
+              "version": "0.8.0-beta.0",
+              "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz",
+              "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==",
+              "dev": true,
+              "requires": {
+                "whatwg-url": "^7.0.0"
+              }
+            }
+          }
+        },
+        "tr46": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz",
+          "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=",
+          "dev": true,
+          "requires": {
+            "punycode": "^2.1.0"
+          }
+        },
+        "webidl-conversions": {
+          "version": "4.0.2",
+          "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz",
+          "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==",
+          "dev": true
+        },
+        "webpack-dev-middleware": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-4.3.0.tgz",
+          "integrity": "sha512-PjwyVY95/bhBh6VUqt6z4THplYcsvQ8YNNBTBM873xLVmw8FLeALn0qurHbs9EmcfhzQis/eoqypSnZeuUz26w==",
+          "dev": true,
+          "requires": {
+            "colorette": "^1.2.2",
+            "mem": "^8.1.1",
+            "memfs": "^3.2.2",
+            "mime-types": "^2.1.30",
+            "range-parser": "^1.2.1",
+            "schema-utils": "^3.0.0"
+          }
+        },
+        "webpack-virtual-modules": {
+          "version": "0.4.3",
+          "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.4.3.tgz",
+          "integrity": "sha512-5NUqC2JquIL2pBAAo/VfBP6KuGkHIZQXW/lNKupLPfhViwh8wNsu0BObtl09yuKZszeEUfbXz8xhrHvSG16Nqw==",
+          "dev": true
+        },
+        "whatwg-url": {
+          "version": "7.1.0",
+          "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz",
+          "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==",
+          "dev": true,
+          "requires": {
+            "lodash.sortby": "^4.7.0",
+            "tr46": "^1.0.1",
+            "webidl-conversions": "^4.0.2"
+          }
+        }
+      }
+    },
+    "@storybook/mdx1-csf": {
+      "version": "0.0.1-canary.1.eed86d7.0",
+      "resolved": "https://registry.npmjs.org/@storybook/mdx1-csf/-/mdx1-csf-0.0.1-canary.1.eed86d7.0.tgz",
+      "integrity": "sha512-KOoTWeseRyJYU5qzEWahgNRhRLgHzPrWx4jIiK4oI9MmNx81cD0B0o24Gq04Iyt89X4jTk+VPHc5REJO1Gs99Q==",
+      "dev": true,
+      "requires": {
+        "@babel/generator": "^7.12.11",
+        "@babel/parser": "^7.12.11",
+        "@babel/preset-env": "^7.12.11",
+        "@babel/types": "^7.12.11",
+        "@mdx-js/mdx": "^1.6.22",
+        "@types/lodash": "^4.14.167",
+        "js-string-escape": "^1.0.1",
+        "loader-utils": "^2.0.0",
+        "lodash": "^4.17.21",
+        "prettier": ">=2.2.1 <=2.3.0",
+        "ts-dedent": "^2.0.0"
+      }
+    },
+    "@storybook/node-logger": {
+      "version": "6.5.4",
+      "resolved": "https://registry.npmjs.org/@storybook/node-logger/-/node-logger-6.5.4.tgz",
+      "integrity": "sha512-2rK9oTBhvRm4VOr1ywIWXfWEXVbuV7wGdZu4hpSDlkhnBBFCyYBJH9asEJVoB4Qk77HPMCqkw2IHpstVjkASXQ==",
+      "dev": true,
+      "requires": {
+        "@types/npmlog": "^4.1.2",
+        "chalk": "^4.1.0",
+        "core-js": "^3.8.2",
+        "npmlog": "^5.0.1",
+        "pretty-hrtime": "^1.0.3"
+      },
+      "dependencies": {
+        "ansi-styles": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+          "dev": true,
+          "requires": {
+            "color-convert": "^2.0.1"
+          }
+        },
+        "are-we-there-yet": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz",
+          "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==",
+          "dev": true,
+          "requires": {
+            "delegates": "^1.0.0",
+            "readable-stream": "^3.6.0"
+          }
+        },
+        "chalk": {
+          "version": "4.1.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+          "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "dev": true,
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
+        },
+        "gauge": {
+          "version": "3.0.2",
+          "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz",
+          "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==",
+          "dev": true,
+          "requires": {
+            "aproba": "^1.0.3 || ^2.0.0",
+            "color-support": "^1.1.2",
+            "console-control-strings": "^1.0.0",
+            "has-unicode": "^2.0.1",
+            "object-assign": "^4.1.1",
+            "signal-exit": "^3.0.0",
+            "string-width": "^4.2.3",
+            "strip-ansi": "^6.0.1",
+            "wide-align": "^1.1.2"
+          }
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
+        "npmlog": {
+          "version": "5.0.1",
+          "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz",
+          "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==",
+          "dev": true,
+          "requires": {
+            "are-we-there-yet": "^2.0.0",
+            "console-control-strings": "^1.1.0",
+            "gauge": "^3.0.0",
+            "set-blocking": "^2.0.0"
+          }
+        },
+        "readable-stream": {
+          "version": "3.6.0",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+          "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+          "dev": true,
+          "requires": {
+            "inherits": "^2.0.3",
+            "string_decoder": "^1.1.1",
+            "util-deprecate": "^1.0.1"
+          }
+        },
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
+        }
+      }
+    },
+    "@storybook/postinstall": {
+      "version": "6.5.4",
+      "resolved": "https://registry.npmjs.org/@storybook/postinstall/-/postinstall-6.5.4.tgz",
+      "integrity": "sha512-euiZOwz5KmtL3wcSxzxLTkGkxLCroZTVaNt/NopRBa0F8erJfT9MiQPVw/xFkS9QwEViaR+Zq4KmStxB41zycQ==",
+      "dev": true,
+      "requires": {
+        "core-js": "^3.8.2"
+      }
+    },
+    "@storybook/preview-web": {
+      "version": "6.5.4",
+      "resolved": "https://registry.npmjs.org/@storybook/preview-web/-/preview-web-6.5.4.tgz",
+      "integrity": "sha512-mbyZI0ProlLSB5MVe+YvoCt+XtlyBvP3lzTVdPZWVVtRN6DK5SEcfsK9//rgmqxiYYXfSKfyI53skYZkKAIEcA==",
+      "dev": true,
+      "requires": {
+        "@storybook/addons": "6.5.4",
+        "@storybook/channel-postmessage": "6.5.4",
+        "@storybook/client-logger": "6.5.4",
+        "@storybook/core-events": "6.5.4",
+        "@storybook/csf": "0.0.2--canary.4566f4d.1",
+        "@storybook/store": "6.5.4",
+        "ansi-to-html": "^0.6.11",
+        "core-js": "^3.8.2",
+        "global": "^4.4.0",
+        "lodash": "^4.17.21",
+        "qs": "^6.10.0",
+        "regenerator-runtime": "^0.13.7",
+        "synchronous-promise": "^2.0.15",
+        "ts-dedent": "^2.0.0",
+        "unfetch": "^4.2.0",
+        "util-deprecate": "^1.0.2"
+      }
+    },
+    "@storybook/router": {
+      "version": "6.5.4",
+      "resolved": "https://registry.npmjs.org/@storybook/router/-/router-6.5.4.tgz",
+      "integrity": "sha512-Xw0oYaJSxd/zPUiZw8AoQRCdrePuxsn1XwlDvzfNlMnPnU17LAFzVf8kpHPXvx01F9pLnGI4ZggsjVomHy/E1Q==",
+      "dev": true,
+      "requires": {
+        "@storybook/client-logger": "6.5.4",
+        "core-js": "^3.8.2",
+        "regenerator-runtime": "^0.13.7"
+      }
+    },
+    "@storybook/source-loader": {
+      "version": "6.5.4",
+      "resolved": "https://registry.npmjs.org/@storybook/source-loader/-/source-loader-6.5.4.tgz",
+      "integrity": "sha512-NfNktWLNvsGVyf3IjCke/4HftPcIHD251J7QOuPjq3lD9OTLZgL4blqscUmd2RrGZRUEH+KSfCIV9jTSeqS1wQ==",
+      "dev": true,
+      "requires": {
+        "@storybook/addons": "6.5.4",
+        "@storybook/client-logger": "6.5.4",
+        "@storybook/csf": "0.0.2--canary.4566f4d.1",
+        "core-js": "^3.8.2",
+        "estraverse": "^5.2.0",
+        "global": "^4.4.0",
+        "loader-utils": "^2.0.0",
+        "lodash": "^4.17.21",
+        "prettier": ">=2.2.1 <=2.3.0",
+        "regenerator-runtime": "^0.13.7"
+      },
+      "dependencies": {
+        "estraverse": {
+          "version": "5.3.0",
+          "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+          "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+          "dev": true
+        }
+      }
+    },
+    "@storybook/store": {
+      "version": "6.5.4",
+      "resolved": "https://registry.npmjs.org/@storybook/store/-/store-6.5.4.tgz",
+      "integrity": "sha512-hCHSQorxRVGseVlIfM4JXbJHkA6AAXuAFl+QsUTjG24q3zAc3sIE2nbH5KsHuJN5NorjcrGeLQOAO9amqzUeHA==",
+      "dev": true,
+      "requires": {
+        "@storybook/addons": "6.5.4",
+        "@storybook/client-logger": "6.5.4",
+        "@storybook/core-events": "6.5.4",
+        "@storybook/csf": "0.0.2--canary.4566f4d.1",
+        "core-js": "^3.8.2",
+        "fast-deep-equal": "^3.1.3",
+        "global": "^4.4.0",
+        "lodash": "^4.17.21",
+        "memoizerific": "^1.11.3",
+        "regenerator-runtime": "^0.13.7",
+        "slash": "^3.0.0",
+        "stable": "^0.1.8",
+        "synchronous-promise": "^2.0.15",
+        "ts-dedent": "^2.0.0",
+        "util-deprecate": "^1.0.2"
+      }
+    },
+    "@storybook/telemetry": {
+      "version": "6.5.4",
+      "resolved": "https://registry.npmjs.org/@storybook/telemetry/-/telemetry-6.5.4.tgz",
+      "integrity": "sha512-BKQajMd80mXHp/sE54I1JiusgvfZrnRqBbZW93slGNG3JyWpJLGOBH/KCp4iyu6Htbqssi+nvrxneUCT+wA6eQ==",
+      "dev": true,
+      "requires": {
+        "@storybook/client-logger": "6.5.4",
+        "@storybook/core-common": "6.5.4",
+        "chalk": "^4.1.0",
+        "core-js": "^3.8.2",
+        "detect-package-manager": "^2.0.1",
+        "fetch-retry": "^5.0.2",
+        "fs-extra": "^9.0.1",
+        "global": "^4.4.0",
+        "isomorphic-unfetch": "^3.1.0",
+        "nanoid": "^3.3.1",
+        "read-pkg-up": "^7.0.1",
+        "regenerator-runtime": "^0.13.7"
+      },
+      "dependencies": {
+        "ansi-styles": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+          "dev": true,
+          "requires": {
+            "color-convert": "^2.0.1"
+          }
+        },
+        "chalk": {
+          "version": "4.1.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+          "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "dev": true,
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
+        },
+        "fs-extra": {
+          "version": "9.1.0",
+          "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
+          "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
+          "dev": true,
+          "requires": {
+            "at-least-node": "^1.0.0",
+            "graceful-fs": "^4.2.0",
+            "jsonfile": "^6.0.1",
+            "universalify": "^2.0.0"
+          }
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
+        }
+      }
+    },
+    "@storybook/testing-library": {
+      "version": "0.0.11",
+      "resolved": "https://registry.npmjs.org/@storybook/testing-library/-/testing-library-0.0.11.tgz",
+      "integrity": "sha512-8KbKx3s1e+uF3oWlPdyXRpZa6xtCsCHtXh1nCTisMA6P5YcSDaCg59NXIOVIQCAwKvjRomlqMJH8JL1WyOzeVg==",
+      "dev": true,
+      "requires": {
+        "@storybook/client-logger": "^6.4.0 || >=6.5.0-0",
+        "@storybook/instrumenter": "^6.4.0 || >=6.5.0-0",
+        "@testing-library/dom": "^8.3.0",
+        "@testing-library/user-event": "^13.2.1",
+        "ts-dedent": "^2.2.0"
+      }
+    },
+    "@storybook/theming": {
+      "version": "6.5.4",
+      "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-6.5.4.tgz",
+      "integrity": "sha512-J5ipCqKH8FfMVj8SuVsjxSIYgb9GN8NBsVAVvTakVjqyKnv5znR+w+h0lvyzRNaEnF5LQCy7Uj7DE6+Pbg/wPw==",
+      "dev": true,
+      "requires": {
+        "@storybook/client-logger": "6.5.4",
+        "core-js": "^3.8.2",
+        "regenerator-runtime": "^0.13.7"
+      }
+    },
+    "@storybook/ui": {
+      "version": "6.5.4",
+      "resolved": "https://registry.npmjs.org/@storybook/ui/-/ui-6.5.4.tgz",
+      "integrity": "sha512-+kMgNMcW7xma/H/O4LFzdxjX6GPfEEvOkm3HQDz0dJW7z/wtQVTgZKCA8gAVl+eNcNyRGzYRKOrOU0AzFfU7lw==",
+      "dev": true,
+      "requires": {
+        "@storybook/addons": "6.5.4",
+        "@storybook/api": "6.5.4",
+        "@storybook/channels": "6.5.4",
+        "@storybook/client-logger": "6.5.4",
+        "@storybook/components": "6.5.4",
+        "@storybook/core-events": "6.5.4",
+        "@storybook/router": "6.5.4",
+        "@storybook/semver": "^7.3.2",
+        "@storybook/theming": "6.5.4",
+        "core-js": "^3.8.2",
+        "regenerator-runtime": "^0.13.7",
+        "resolve-from": "^5.0.0"
+      },
+      "dependencies": {
+        "@storybook/semver": {
+          "version": "7.3.2",
+          "resolved": "https://registry.npmjs.org/@storybook/semver/-/semver-7.3.2.tgz",
+          "integrity": "sha512-SWeszlsiPsMI0Ps0jVNtH64cI5c0UF3f7KgjVKJoNP30crQ6wUSddY2hsdeczZXEKVJGEn50Q60flcGsQGIcrg==",
+          "dev": true,
+          "requires": {
+            "core-js": "^3.6.5",
+            "find-up": "^4.1.0"
+          }
+        },
+        "resolve-from": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
+          "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
+          "dev": true
+        }
+      }
+    },
+    "@testing-library/dom": {
+      "version": "8.13.0",
+      "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.13.0.tgz",
+      "integrity": "sha512-9VHgfIatKNXQNaZTtLnalIy0jNZzY35a4S3oi08YAt9Hv1VsfZ/DfA45lM8D/UhtHBGJ4/lGwp0PZkVndRkoOQ==",
+      "dev": true,
+      "requires": {
+        "@babel/code-frame": "^7.10.4",
+        "@babel/runtime": "^7.12.5",
+        "@types/aria-query": "^4.2.0",
+        "aria-query": "^5.0.0",
+        "chalk": "^4.1.0",
+        "dom-accessibility-api": "^0.5.9",
+        "lz-string": "^1.4.4",
+        "pretty-format": "^27.0.2"
+      },
+      "dependencies": {
+        "ansi-styles": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+          "dev": true,
+          "requires": {
+            "color-convert": "^2.0.1"
+          }
+        },
+        "chalk": {
+          "version": "4.1.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+          "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "dev": true,
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
+        }
+      }
+    },
+    "@testing-library/user-event": {
+      "version": "13.5.0",
+      "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-13.5.0.tgz",
+      "integrity": "sha512-5Kwtbo3Y/NowpkbRuSepbyMFkZmHgD+vPzYB/RJ4oxt5Gj/avFFBYjhw27cqSVPVw/3a67NK1PbiIr9k4Gwmdg==",
+      "dev": true,
+      "requires": {
+        "@babel/runtime": "^7.12.5"
+      }
+    },
+    "@tootallnate/once": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
+      "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==",
+      "dev": true
+    },
+    "@trysound/sax": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz",
+      "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==",
+      "dev": true
+    },
+    "@ts-morph/common": {
+      "version": "0.12.3",
+      "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.12.3.tgz",
+      "integrity": "sha512-4tUmeLyXJnJWvTFOKtcNJ1yh0a3SsTLi2MUoyj8iUNznFRN1ZquaNe7Oukqrnki2FzZkm0J9adCNLDZxUzvj+w==",
+      "dev": true,
+      "requires": {
+        "fast-glob": "^3.2.7",
+        "minimatch": "^3.0.4",
+        "mkdirp": "^1.0.4",
+        "path-browserify": "^1.0.1"
+      }
+    },
+    "@types/aria-query": {
+      "version": "4.2.2",
+      "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.2.tgz",
+      "integrity": "sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig==",
+      "dev": true
+    },
+    "@types/component-emitter": {
+      "version": "1.2.11",
+      "resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.11.tgz",
+      "integrity": "sha512-SRXjM+tfsSlA9VuG8hGO2nft2p8zjXCK1VcC6N4NXbBbYbSia9kzCChYQajIjzIqOOOuh5Ock6MmV2oux4jDZQ==",
+      "dev": true
+    },
+    "@types/cookie": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz",
+      "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==",
+      "dev": true
+    },
+    "@types/cors": {
+      "version": "2.8.12",
+      "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz",
+      "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==",
+      "dev": true
+    },
+    "@types/eslint": {
+      "version": "8.4.2",
+      "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.2.tgz",
+      "integrity": "sha512-Z1nseZON+GEnFjJc04sv4NSALGjhFwy6K0HXt7qsn5ArfAKtb63dXNJHf+1YW6IpOIYRBGUbu3GwJdj8DGnCjA==",
+      "dev": true,
+      "requires": {
+        "@types/estree": "*",
+        "@types/json-schema": "*"
+      }
+    },
+    "@types/eslint-scope": {
+      "version": "3.7.3",
+      "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.3.tgz",
+      "integrity": "sha512-PB3ldyrcnAicT35TWPs5IcwKD8S333HMaa2VVv4+wdvebJkjWuW/xESoB8IwRcog8HYVYamb1g/R31Qv5Bx03g==",
+      "dev": true,
+      "requires": {
+        "@types/eslint": "*",
+        "@types/estree": "*"
+      }
+    },
+    "@types/estree": {
+      "version": "0.0.50",
+      "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.50.tgz",
+      "integrity": "sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==",
+      "dev": true
+    },
+    "@types/glob": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz",
+      "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==",
+      "dev": true,
+      "requires": {
+        "@types/minimatch": "*",
+        "@types/node": "*"
+      }
+    },
+    "@types/graceful-fs": {
+      "version": "4.1.5",
+      "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz",
+      "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==",
+      "dev": true,
+      "requires": {
+        "@types/node": "*"
+      }
+    },
+    "@types/hast": {
+      "version": "2.3.4",
+      "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.4.tgz",
+      "integrity": "sha512-wLEm0QvaoawEDoTRwzTXp4b4jpwiJDvR5KMnFnVodm3scufTlBOWRD6N1OBf9TZMhjlNsSfcO5V+7AF4+Vy+9g==",
+      "dev": true,
+      "requires": {
+        "@types/unist": "*"
+      }
+    },
+    "@types/html-minifier-terser": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-5.1.2.tgz",
+      "integrity": "sha512-h4lTMgMJctJybDp8CQrxTUiiYmedihHWkjnF/8Pxseu2S6Nlfcy8kwboQ8yejh456rP2yWoEVm1sS/FVsfM48w==",
+      "dev": true
+    },
+    "@types/is-function": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/@types/is-function/-/is-function-1.0.1.tgz",
+      "integrity": "sha512-A79HEEiwXTFtfY+Bcbo58M2GRYzCr9itHWzbzHVFNEYCcoU/MMGwYYf721gBrnhpj1s6RGVVha/IgNFnR0Iw/Q==",
+      "dev": true
+    },
+    "@types/istanbul-lib-coverage": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz",
+      "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==",
+      "dev": true
+    },
+    "@types/istanbul-lib-report": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz",
+      "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==",
+      "dev": true,
+      "requires": {
+        "@types/istanbul-lib-coverage": "*"
+      }
+    },
+    "@types/istanbul-reports": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz",
+      "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==",
+      "dev": true,
+      "requires": {
+        "@types/istanbul-lib-report": "*"
+      }
+    },
+    "@types/jasmine": {
+      "version": "3.8.2",
+      "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-3.8.2.tgz",
+      "integrity": "sha512-u5h7dqzy2XpXTzhOzSNQUQpKGFvROF8ElNX9P/TJvsHnTg/JvsAseVsGWQAQQldqanYaM+5kwxW909BBFAUYsg==",
+      "dev": true
+    },
+    "@types/json-schema": {
+      "version": "7.0.11",
+      "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
+      "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ=="
+    },
+    "@types/json5": {
+      "version": "0.0.29",
+      "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
+      "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
+      "dev": true
+    },
+    "@types/lodash": {
+      "version": "4.14.182",
+      "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.182.tgz",
+      "integrity": "sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q==",
+      "dev": true
+    },
+    "@types/mdast": {
+      "version": "3.0.10",
+      "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.10.tgz",
+      "integrity": "sha512-W864tg/Osz1+9f4lrGTZpCSO5/z4608eUp19tbozkq2HJK6i3z1kT0H9tlADXuYIb1YYOBByU4Jsqkk75q48qA==",
+      "dev": true,
+      "requires": {
+        "@types/unist": "*"
+      }
+    },
+    "@types/minimatch": {
+      "version": "3.0.5",
+      "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz",
+      "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==",
+      "dev": true
+    },
+    "@types/node": {
+      "version": "12.20.52",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.52.tgz",
+      "integrity": "sha512-cfkwWw72849SNYp3Zx0IcIs25vABmFh73xicxhCkTcvtZQeIez15PpwQN8fY3RD7gv1Wrxlc9MEtfMORZDEsGw==",
+      "dev": true
+    },
+    "@types/node-fetch": {
+      "version": "2.6.1",
+      "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.1.tgz",
+      "integrity": "sha512-oMqjURCaxoSIsHSr1E47QHzbmzNR5rK8McHuNb11BOM9cHcIK3Avy0s/b2JlXHoQGTYS3NsvWzV1M0iK7l0wbA==",
+      "dev": true,
+      "requires": {
+        "@types/node": "*",
+        "form-data": "^3.0.0"
+      }
+    },
+    "@types/normalize-package-data": {
+      "version": "2.4.1",
+      "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz",
+      "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==",
+      "dev": true
+    },
+    "@types/npmlog": {
+      "version": "4.1.4",
+      "resolved": "https://registry.npmjs.org/@types/npmlog/-/npmlog-4.1.4.tgz",
+      "integrity": "sha512-WKG4gTr8przEZBiJ5r3s8ZIAoMXNbOgQ+j/d5O4X3x6kZJRLNvyUJuUK/KoG3+8BaOHPhp2m7WC6JKKeovDSzQ==",
+      "dev": true
+    },
+    "@types/parse-json": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
+      "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==",
+      "dev": true
+    },
+    "@types/parse5": {
+      "version": "5.0.3",
+      "resolved": "https://registry.npmjs.org/@types/parse5/-/parse5-5.0.3.tgz",
+      "integrity": "sha512-kUNnecmtkunAoQ3CnjmMkzNU/gtxG8guhi+Fk2U/kOpIKjIMKnXGp4IJCgQJrXSgMsWYimYG4TGjz/UzbGEBTw==",
+      "dev": true
+    },
+    "@types/pretty-hrtime": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/@types/pretty-hrtime/-/pretty-hrtime-1.0.1.tgz",
+      "integrity": "sha512-VjID5MJb1eGKthz2qUerWT8+R4b9N+CHvGCzg9fn4kWZgaF9AhdYikQio3R7wV8YY1NsQKPaCwKz1Yff+aHNUQ==",
+      "dev": true
+    },
+    "@types/prop-types": {
+      "version": "15.7.5",
+      "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz",
+      "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==",
+      "dev": true
+    },
+    "@types/qs": {
+      "version": "6.9.7",
+      "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz",
+      "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==",
+      "dev": true
+    },
+    "@types/react": {
+      "version": "18.0.9",
+      "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.9.tgz",
+      "integrity": "sha512-9bjbg1hJHUm4De19L1cHiW0Jvx3geel6Qczhjd0qY5VKVE2X5+x77YxAepuCwVh4vrgZJdgEJw48zrhRIeF4Nw==",
+      "dev": true,
+      "requires": {
+        "@types/prop-types": "*",
+        "@types/scheduler": "*",
+        "csstype": "^3.0.2"
+      }
+    },
+    "@types/react-dom": {
+      "version": "16.9.16",
+      "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.16.tgz",
+      "integrity": "sha512-Oqc0RY4fggGA3ltEgyPLc3IV9T73IGoWjkONbsyJ3ZBn+UPPCYpU2ec0i3cEbJuEdZtkqcCF2l1zf2pBdgUGSg==",
+      "dev": true,
+      "requires": {
+        "@types/react": "^16"
+      },
+      "dependencies": {
+        "@types/react": {
+          "version": "16.14.26",
+          "resolved": "https://registry.npmjs.org/@types/react/-/react-16.14.26.tgz",
+          "integrity": "sha512-c/5CYyciOO4XdFcNhZW1O2woVx86k4T+DO2RorHZL7EhitkNQgSD/SgpdZJAUJa/qjVgOmTM44gHkAdZSXeQuQ==",
+          "dev": true,
+          "requires": {
+            "@types/prop-types": "*",
+            "@types/scheduler": "*",
+            "csstype": "^3.0.2"
+          }
+        }
+      }
+    },
+    "@types/react-syntax-highlighter": {
+      "version": "11.0.5",
+      "resolved": "https://registry.npmjs.org/@types/react-syntax-highlighter/-/react-syntax-highlighter-11.0.5.tgz",
+      "integrity": "sha512-VIOi9i2Oj5XsmWWoB72p3KlZoEbdRAcechJa8Ztebw7bDl2YmR+odxIqhtJGp1q2EozHs02US+gzxJ9nuf56qg==",
+      "dev": true,
+      "requires": {
+        "@types/react": "*"
+      }
+    },
+    "@types/scheduler": {
+      "version": "0.16.2",
+      "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
+      "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==",
+      "dev": true
+    },
+    "@types/source-list-map": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz",
+      "integrity": "sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA==",
+      "dev": true
+    },
+    "@types/tapable": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.8.tgz",
+      "integrity": "sha512-ipixuVrh2OdNmauvtT51o3d8z12p6LtFW9in7U79der/kwejjdNchQC5UMn5u/KxNoM7VHHOs/l8KS8uHxhODQ==",
+      "dev": true
+    },
+    "@types/uglify-js": {
+      "version": "3.13.2",
+      "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.13.2.tgz",
+      "integrity": "sha512-/xFrPIo+4zOeNGtVMbf9rUm0N+i4pDf1ynExomqtokIJmVzR3962lJ1UE+MmexMkA0cmN9oTzg5Xcbwge0Ij2Q==",
+      "dev": true,
+      "requires": {
+        "source-map": "^0.6.1"
+      },
+      "dependencies": {
+        "source-map": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+          "dev": true
+        }
+      }
+    },
+    "@types/unist": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz",
+      "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==",
+      "dev": true
+    },
+    "@types/webpack": {
+      "version": "4.41.32",
+      "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.32.tgz",
+      "integrity": "sha512-cb+0ioil/7oz5//7tZUSwbrSAN/NWHrQylz5cW8G0dWTcF/g+/dSdMlKVZspBYuMAN1+WnwHrkxiRrLcwd0Heg==",
+      "dev": true,
+      "requires": {
+        "@types/node": "*",
+        "@types/tapable": "^1",
+        "@types/uglify-js": "*",
+        "@types/webpack-sources": "*",
+        "anymatch": "^3.0.0",
+        "source-map": "^0.6.0"
+      },
+      "dependencies": {
+        "source-map": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+          "dev": true
+        }
+      }
+    },
+    "@types/webpack-env": {
+      "version": "1.17.0",
+      "resolved": "https://registry.npmjs.org/@types/webpack-env/-/webpack-env-1.17.0.tgz",
+      "integrity": "sha512-eHSaNYEyxRA5IAG0Ym/yCyf86niZUIF/TpWKofQI/CVfh5HsMEUyfE2kwFxha4ow0s5g0LfISQxpDKjbRDrizw==",
+      "dev": true
+    },
+    "@types/webpack-sources": {
+      "version": "0.1.9",
+      "resolved": "https://registry.npmjs.org/@types/webpack-sources/-/webpack-sources-0.1.9.tgz",
+      "integrity": "sha512-bvzMnzqoK16PQIC8AYHNdW45eREJQMd6WG/msQWX5V2+vZmODCOPb4TJcbgRljTZZTwTM4wUMcsI8FftNA7new==",
+      "dev": true,
+      "requires": {
+        "@types/node": "*",
+        "@types/source-list-map": "*",
+        "source-map": "^0.6.1"
+      },
+      "dependencies": {
+        "source-map": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+          "dev": true
+        }
+      }
+    },
+    "@types/yargs": {
+      "version": "15.0.14",
+      "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.14.tgz",
+      "integrity": "sha512-yEJzHoxf6SyQGhBhIYGXQDSCkJjB6HohDShto7m8vaKg9Yp0Yn8+71J9eakh2bnPg6BfsH9PRMhiRTZnd4eXGQ==",
+      "dev": true,
+      "requires": {
+        "@types/yargs-parser": "*"
+      }
+    },
+    "@types/yargs-parser": {
+      "version": "21.0.0",
+      "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz",
+      "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==",
+      "dev": true
+    },
+    "@typescript-eslint/eslint-plugin": {
+      "version": "4.33.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.33.0.tgz",
+      "integrity": "sha512-aINiAxGVdOl1eJyVjaWn/YcVAq4Gi/Yo35qHGCnqbWVz61g39D0h23veY/MA0rFFGfxK7TySg2uwDeNv+JgVpg==",
+      "dev": true,
+      "requires": {
+        "@typescript-eslint/experimental-utils": "4.33.0",
+        "@typescript-eslint/scope-manager": "4.33.0",
+        "debug": "^4.3.1",
+        "functional-red-black-tree": "^1.0.1",
+        "ignore": "^5.1.8",
+        "regexpp": "^3.1.0",
+        "semver": "^7.3.5",
+        "tsutils": "^3.21.0"
+      }
+    },
+    "@typescript-eslint/experimental-utils": {
+      "version": "4.33.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.33.0.tgz",
+      "integrity": "sha512-zeQjOoES5JFjTnAhI5QY7ZviczMzDptls15GFsI6jyUOq0kOf9+WonkhtlIhh0RgHRnqj5gdNxW5j1EvAyYg6Q==",
+      "dev": true,
+      "requires": {
+        "@types/json-schema": "^7.0.7",
+        "@typescript-eslint/scope-manager": "4.33.0",
+        "@typescript-eslint/types": "4.33.0",
+        "@typescript-eslint/typescript-estree": "4.33.0",
+        "eslint-scope": "^5.1.1",
+        "eslint-utils": "^3.0.0"
+      }
+    },
+    "@typescript-eslint/parser": {
+      "version": "4.33.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.33.0.tgz",
+      "integrity": "sha512-ZohdsbXadjGBSK0/r+d87X0SBmKzOq4/S5nzK6SBgJspFo9/CUDJ7hjayuze+JK7CZQLDMroqytp7pOcFKTxZA==",
+      "dev": true,
+      "requires": {
+        "@typescript-eslint/scope-manager": "4.33.0",
+        "@typescript-eslint/types": "4.33.0",
+        "@typescript-eslint/typescript-estree": "4.33.0",
+        "debug": "^4.3.1"
+      }
+    },
+    "@typescript-eslint/scope-manager": {
+      "version": "4.33.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.33.0.tgz",
+      "integrity": "sha512-5IfJHpgTsTZuONKbODctL4kKuQje/bzBRkwHE8UOZ4f89Zeddg+EGZs8PD8NcN4LdM3ygHWYB3ukPAYjvl/qbQ==",
+      "dev": true,
+      "requires": {
+        "@typescript-eslint/types": "4.33.0",
+        "@typescript-eslint/visitor-keys": "4.33.0"
+      }
+    },
+    "@typescript-eslint/types": {
+      "version": "4.33.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.33.0.tgz",
+      "integrity": "sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ==",
+      "dev": true
+    },
+    "@typescript-eslint/typescript-estree": {
+      "version": "4.33.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz",
+      "integrity": "sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA==",
+      "dev": true,
+      "requires": {
+        "@typescript-eslint/types": "4.33.0",
+        "@typescript-eslint/visitor-keys": "4.33.0",
+        "debug": "^4.3.1",
+        "globby": "^11.0.3",
+        "is-glob": "^4.0.1",
+        "semver": "^7.3.5",
+        "tsutils": "^3.21.0"
+      }
+    },
+    "@typescript-eslint/utils": {
+      "version": "5.26.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.26.0.tgz",
+      "integrity": "sha512-PJFwcTq2Pt4AMOKfe3zQOdez6InIDOjUJJD3v3LyEtxHGVVRK3Vo7Dd923t/4M9hSH2q2CLvcTdxlLPjcIk3eg==",
+      "dev": true,
+      "requires": {
+        "@types/json-schema": "^7.0.9",
+        "@typescript-eslint/scope-manager": "5.26.0",
+        "@typescript-eslint/types": "5.26.0",
+        "@typescript-eslint/typescript-estree": "5.26.0",
+        "eslint-scope": "^5.1.1",
+        "eslint-utils": "^3.0.0"
+      },
+      "dependencies": {
+        "@typescript-eslint/scope-manager": {
+          "version": "5.26.0",
+          "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.26.0.tgz",
+          "integrity": "sha512-gVzTJUESuTwiju/7NiTb4c5oqod8xt5GhMbExKsCTp6adU3mya6AGJ4Pl9xC7x2DX9UYFsjImC0mA62BCY22Iw==",
+          "dev": true,
+          "requires": {
+            "@typescript-eslint/types": "5.26.0",
+            "@typescript-eslint/visitor-keys": "5.26.0"
+          }
+        },
+        "@typescript-eslint/types": {
+          "version": "5.26.0",
+          "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.26.0.tgz",
+          "integrity": "sha512-8794JZFE1RN4XaExLWLI2oSXsVImNkl79PzTOOWt9h0UHROwJedNOD2IJyfL0NbddFllcktGIO2aOu10avQQyA==",
+          "dev": true
+        },
+        "@typescript-eslint/typescript-estree": {
+          "version": "5.26.0",
+          "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.26.0.tgz",
+          "integrity": "sha512-EyGpw6eQDsfD6jIqmXP3rU5oHScZ51tL/cZgFbFBvWuCwrIptl+oueUZzSmLtxFuSOQ9vDcJIs+279gnJkfd1w==",
+          "dev": true,
+          "requires": {
+            "@typescript-eslint/types": "5.26.0",
+            "@typescript-eslint/visitor-keys": "5.26.0",
+            "debug": "^4.3.4",
+            "globby": "^11.1.0",
+            "is-glob": "^4.0.3",
+            "semver": "^7.3.7",
+            "tsutils": "^3.21.0"
+          }
+        },
+        "@typescript-eslint/visitor-keys": {
+          "version": "5.26.0",
+          "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.26.0.tgz",
+          "integrity": "sha512-wei+ffqHanYDOQgg/fS6Hcar6wAWv0CUPQ3TZzOWd2BLfgP539rb49bwua8WRAs7R6kOSLn82rfEu2ro6Llt8Q==",
+          "dev": true,
+          "requires": {
+            "@typescript-eslint/types": "5.26.0",
+            "eslint-visitor-keys": "^3.3.0"
+          }
+        },
+        "eslint-visitor-keys": {
+          "version": "3.3.0",
+          "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz",
+          "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==",
+          "dev": true
+        },
+        "semver": {
+          "version": "7.3.7",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
+          "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
+          "dev": true,
+          "requires": {
+            "lru-cache": "^6.0.0"
+          }
+        }
+      }
+    },
+    "@typescript-eslint/visitor-keys": {
+      "version": "4.33.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.33.0.tgz",
+      "integrity": "sha512-uqi/2aSz9g2ftcHWf8uLPJA70rUv6yuMW5Bohw+bwcuzaxQIHaKFZCKGoGXIrc9vkTJ3+0txM73K0Hq3d5wgIg==",
+      "dev": true,
+      "requires": {
+        "@typescript-eslint/types": "4.33.0",
+        "eslint-visitor-keys": "^2.0.0"
+      }
+    },
+    "@webassemblyjs/ast": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz",
+      "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==",
+      "dev": true,
+      "requires": {
+        "@webassemblyjs/helper-numbers": "1.11.1",
+        "@webassemblyjs/helper-wasm-bytecode": "1.11.1"
+      }
+    },
+    "@webassemblyjs/floating-point-hex-parser": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz",
+      "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==",
+      "dev": true
+    },
+    "@webassemblyjs/helper-api-error": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz",
+      "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==",
+      "dev": true
+    },
+    "@webassemblyjs/helper-buffer": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz",
+      "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==",
+      "dev": true
+    },
+    "@webassemblyjs/helper-code-frame": {
+      "version": "1.9.0",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz",
+      "integrity": "sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA==",
+      "dev": true,
+      "requires": {
+        "@webassemblyjs/wast-printer": "1.9.0"
+      },
+      "dependencies": {
+        "@webassemblyjs/ast": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz",
+          "integrity": "sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA==",
+          "dev": true,
+          "requires": {
+            "@webassemblyjs/helper-module-context": "1.9.0",
+            "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
+            "@webassemblyjs/wast-parser": "1.9.0"
+          }
+        },
+        "@webassemblyjs/helper-wasm-bytecode": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz",
+          "integrity": "sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw==",
+          "dev": true
+        },
+        "@webassemblyjs/wast-printer": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz",
+          "integrity": "sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA==",
+          "dev": true,
+          "requires": {
+            "@webassemblyjs/ast": "1.9.0",
+            "@webassemblyjs/wast-parser": "1.9.0",
+            "@xtuc/long": "4.2.2"
+          }
+        }
+      }
+    },
+    "@webassemblyjs/helper-fsm": {
+      "version": "1.9.0",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz",
+      "integrity": "sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw==",
+      "dev": true
+    },
+    "@webassemblyjs/helper-module-context": {
+      "version": "1.9.0",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz",
+      "integrity": "sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g==",
+      "dev": true,
+      "requires": {
+        "@webassemblyjs/ast": "1.9.0"
+      },
+      "dependencies": {
+        "@webassemblyjs/ast": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz",
+          "integrity": "sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA==",
+          "dev": true,
+          "requires": {
+            "@webassemblyjs/helper-module-context": "1.9.0",
+            "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
+            "@webassemblyjs/wast-parser": "1.9.0"
+          }
+        },
+        "@webassemblyjs/helper-wasm-bytecode": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz",
+          "integrity": "sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw==",
+          "dev": true
+        }
+      }
+    },
+    "@webassemblyjs/helper-numbers": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz",
+      "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==",
+      "dev": true,
+      "requires": {
+        "@webassemblyjs/floating-point-hex-parser": "1.11.1",
+        "@webassemblyjs/helper-api-error": "1.11.1",
+        "@xtuc/long": "4.2.2"
+      }
+    },
+    "@webassemblyjs/helper-wasm-bytecode": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz",
+      "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==",
+      "dev": true
+    },
+    "@webassemblyjs/helper-wasm-section": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz",
+      "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==",
+      "dev": true,
+      "requires": {
+        "@webassemblyjs/ast": "1.11.1",
+        "@webassemblyjs/helper-buffer": "1.11.1",
+        "@webassemblyjs/helper-wasm-bytecode": "1.11.1",
+        "@webassemblyjs/wasm-gen": "1.11.1"
+      }
+    },
+    "@webassemblyjs/ieee754": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz",
+      "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==",
+      "dev": true,
+      "requires": {
+        "@xtuc/ieee754": "^1.2.0"
+      }
+    },
+    "@webassemblyjs/leb128": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz",
+      "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==",
+      "dev": true,
+      "requires": {
+        "@xtuc/long": "4.2.2"
+      }
+    },
+    "@webassemblyjs/utf8": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz",
+      "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==",
+      "dev": true
+    },
+    "@webassemblyjs/wasm-edit": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz",
+      "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==",
+      "dev": true,
+      "requires": {
+        "@webassemblyjs/ast": "1.11.1",
+        "@webassemblyjs/helper-buffer": "1.11.1",
+        "@webassemblyjs/helper-wasm-bytecode": "1.11.1",
+        "@webassemblyjs/helper-wasm-section": "1.11.1",
+        "@webassemblyjs/wasm-gen": "1.11.1",
+        "@webassemblyjs/wasm-opt": "1.11.1",
+        "@webassemblyjs/wasm-parser": "1.11.1",
+        "@webassemblyjs/wast-printer": "1.11.1"
+      }
+    },
+    "@webassemblyjs/wasm-gen": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz",
+      "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==",
+      "dev": true,
+      "requires": {
+        "@webassemblyjs/ast": "1.11.1",
+        "@webassemblyjs/helper-wasm-bytecode": "1.11.1",
+        "@webassemblyjs/ieee754": "1.11.1",
+        "@webassemblyjs/leb128": "1.11.1",
+        "@webassemblyjs/utf8": "1.11.1"
+      }
+    },
+    "@webassemblyjs/wasm-opt": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz",
+      "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==",
+      "dev": true,
+      "requires": {
+        "@webassemblyjs/ast": "1.11.1",
+        "@webassemblyjs/helper-buffer": "1.11.1",
+        "@webassemblyjs/wasm-gen": "1.11.1",
+        "@webassemblyjs/wasm-parser": "1.11.1"
+      }
+    },
+    "@webassemblyjs/wasm-parser": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz",
+      "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==",
+      "dev": true,
+      "requires": {
+        "@webassemblyjs/ast": "1.11.1",
+        "@webassemblyjs/helper-api-error": "1.11.1",
+        "@webassemblyjs/helper-wasm-bytecode": "1.11.1",
+        "@webassemblyjs/ieee754": "1.11.1",
+        "@webassemblyjs/leb128": "1.11.1",
+        "@webassemblyjs/utf8": "1.11.1"
+      }
+    },
+    "@webassemblyjs/wast-parser": {
+      "version": "1.9.0",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz",
+      "integrity": "sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw==",
+      "dev": true,
+      "requires": {
+        "@webassemblyjs/ast": "1.9.0",
+        "@webassemblyjs/floating-point-hex-parser": "1.9.0",
+        "@webassemblyjs/helper-api-error": "1.9.0",
+        "@webassemblyjs/helper-code-frame": "1.9.0",
+        "@webassemblyjs/helper-fsm": "1.9.0",
+        "@xtuc/long": "4.2.2"
+      },
+      "dependencies": {
+        "@webassemblyjs/ast": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz",
+          "integrity": "sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA==",
+          "dev": true,
+          "requires": {
+            "@webassemblyjs/helper-module-context": "1.9.0",
+            "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
+            "@webassemblyjs/wast-parser": "1.9.0"
+          }
+        },
+        "@webassemblyjs/floating-point-hex-parser": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz",
+          "integrity": "sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA==",
+          "dev": true
+        },
+        "@webassemblyjs/helper-api-error": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz",
+          "integrity": "sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw==",
+          "dev": true
+        },
+        "@webassemblyjs/helper-wasm-bytecode": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz",
+          "integrity": "sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw==",
+          "dev": true
+        }
+      }
+    },
+    "@webassemblyjs/wast-printer": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz",
+      "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==",
+      "dev": true,
+      "requires": {
+        "@webassemblyjs/ast": "1.11.1",
+        "@xtuc/long": "4.2.2"
+      }
+    },
+    "@xtuc/ieee754": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
+      "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==",
+      "dev": true
+    },
+    "@xtuc/long": {
+      "version": "4.2.2",
+      "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz",
+      "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
+      "dev": true
+    },
+    "@yarnpkg/lockfile": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz",
+      "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==",
+      "dev": true
+    },
+    "abab": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz",
+      "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==",
+      "dev": true
+    },
+    "abbrev": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
+      "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
+      "dev": true
+    },
+    "accepts": {
+      "version": "1.3.8",
+      "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
+      "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
+      "dev": true,
+      "requires": {
+        "mime-types": "~2.1.34",
+        "negotiator": "0.6.3"
+      }
+    },
+    "acorn": {
+      "version": "8.7.1",
+      "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz",
+      "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A=="
+    },
+    "acorn-import-assertions": {
+      "version": "1.8.0",
+      "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz",
+      "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==",
+      "dev": true
+    },
+    "acorn-jsx": {
+      "version": "5.3.2",
+      "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+      "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+      "dev": true
+    },
+    "acorn-node": {
+      "version": "1.8.2",
+      "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz",
+      "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==",
+      "dev": true,
+      "requires": {
+        "acorn": "^7.0.0",
+        "acorn-walk": "^7.0.0",
+        "xtend": "^4.0.2"
+      },
+      "dependencies": {
+        "acorn": {
+          "version": "7.4.1",
+          "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
+          "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
+          "dev": true
+        }
+      }
+    },
+    "acorn-walk": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz",
+      "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==",
+      "dev": true
+    },
+    "address": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/address/-/address-1.2.0.tgz",
+      "integrity": "sha512-tNEZYz5G/zYunxFm7sfhAxkXEuLj3K6BKwv6ZURlsF6yiUQ65z0Q2wZW9L5cPUl9ocofGvXOdFYbFHp0+6MOig==",
+      "dev": true
+    },
+    "adjust-sourcemap-loader": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz",
+      "integrity": "sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==",
+      "dev": true,
+      "requires": {
+        "loader-utils": "^2.0.0",
+        "regex-parser": "^2.2.11"
+      }
+    },
+    "agent-base": {
+      "version": "6.0.2",
+      "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
+      "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+      "dev": true,
+      "requires": {
+        "debug": "4"
+      }
+    },
+    "agentkeepalive": {
+      "version": "4.2.1",
+      "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.2.1.tgz",
+      "integrity": "sha512-Zn4cw2NEqd+9fiSVWMscnjyQ1a8Yfoc5oBajLeo5w+YBHgDUcEBY2hS4YpTz6iN5f/2zQiktcuM6tS8x1p9dpA==",
+      "dev": true,
+      "requires": {
+        "debug": "^4.1.0",
+        "depd": "^1.1.2",
+        "humanize-ms": "^1.2.1"
+      },
+      "dependencies": {
+        "depd": {
+          "version": "1.1.2",
+          "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
+          "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=",
+          "dev": true
+        }
+      }
+    },
+    "aggregate-error": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
+      "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==",
+      "dev": true,
+      "requires": {
+        "clean-stack": "^2.0.0",
+        "indent-string": "^4.0.0"
+      }
+    },
+    "airbnb-js-shims": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/airbnb-js-shims/-/airbnb-js-shims-2.2.1.tgz",
+      "integrity": "sha512-wJNXPH66U2xjgo1Zwyjf9EydvJ2Si94+vSdk6EERcBfB2VZkeltpqIats0cqIZMLCXP3zcyaUKGYQeIBT6XjsQ==",
+      "dev": true,
+      "requires": {
+        "array-includes": "^3.0.3",
+        "array.prototype.flat": "^1.2.1",
+        "array.prototype.flatmap": "^1.2.1",
+        "es5-shim": "^4.5.13",
+        "es6-shim": "^0.35.5",
+        "function.prototype.name": "^1.1.0",
+        "globalthis": "^1.0.0",
+        "object.entries": "^1.1.0",
+        "object.fromentries": "^2.0.0 || ^1.0.0",
+        "object.getownpropertydescriptors": "^2.0.3",
+        "object.values": "^1.1.0",
+        "promise.allsettled": "^1.0.0",
+        "promise.prototype.finally": "^3.1.0",
+        "string.prototype.matchall": "^4.0.0 || ^3.0.1",
+        "string.prototype.padend": "^3.0.0",
+        "string.prototype.padstart": "^3.0.0",
+        "symbol.prototype.description": "^1.0.0"
+      }
+    },
+    "ajv": {
+      "version": "6.12.6",
+      "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+      "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+      "requires": {
+        "fast-deep-equal": "^3.1.1",
+        "fast-json-stable-stringify": "^2.0.0",
+        "json-schema-traverse": "^0.4.1",
+        "uri-js": "^4.2.2"
+      }
+    },
+    "ajv-errors": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz",
+      "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==",
+      "dev": true
+    },
+    "ajv-formats": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.0.tgz",
+      "integrity": "sha512-USH2jBb+C/hIpwD2iRjp0pe0k+MvzG0mlSn/FIdCgQhUb9ALPRjt2KIQdfZDS9r0ZIeUAg7gOu9KL0PFqGqr5Q==",
+      "dev": true,
+      "requires": {
+        "ajv": "^8.0.0"
+      },
+      "dependencies": {
+        "ajv": {
+          "version": "8.11.0",
+          "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz",
+          "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==",
+          "dev": true,
+          "requires": {
+            "fast-deep-equal": "^3.1.1",
+            "json-schema-traverse": "^1.0.0",
+            "require-from-string": "^2.0.2",
+            "uri-js": "^4.2.2"
+          }
+        },
+        "json-schema-traverse": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+          "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+          "dev": true
+        }
+      }
+    },
+    "ajv-keywords": {
+      "version": "3.5.2",
+      "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
+      "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ=="
+    },
+    "amdefine": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz",
+      "integrity": "sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg==",
+      "dev": true,
+      "optional": true
+    },
+    "ansi-align": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz",
+      "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==",
+      "dev": true,
+      "requires": {
+        "string-width": "^4.1.0"
+      }
+    },
+    "ansi-colors": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
+      "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==",
+      "dev": true
+    },
+    "ansi-escapes": {
+      "version": "4.3.2",
+      "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
+      "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==",
+      "dev": true,
+      "requires": {
+        "type-fest": "^0.21.3"
+      }
+    },
+    "ansi-gray": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz",
+      "integrity": "sha512-HrgGIZUl8h2EHuZaU9hTR/cU5nhKxpVE1V6kdGsQ8e4zirElJ5fvtfc8N7Q1oq1aatO275i8pUFUCpNWCAnVWw==",
+      "dev": true,
+      "requires": {
+        "ansi-wrap": "0.1.0"
+      }
+    },
+    "ansi-html-community": {
+      "version": "0.0.8",
+      "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz",
+      "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==",
+      "dev": true
+    },
+    "ansi-regex": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+      "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+      "dev": true
+    },
+    "ansi-styles": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+      "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+      "dev": true,
+      "requires": {
+        "color-convert": "^1.9.0"
+      }
+    },
+    "ansi-to-html": {
+      "version": "0.6.15",
+      "resolved": "https://registry.npmjs.org/ansi-to-html/-/ansi-to-html-0.6.15.tgz",
+      "integrity": "sha512-28ijx2aHJGdzbs+O5SNQF65r6rrKYnkuwTYm8lZlChuoJ9P1vVzIpWO20sQTqTPDXYp6NFwk326vApTtLVFXpQ==",
+      "dev": true,
+      "requires": {
+        "entities": "^2.0.0"
+      }
+    },
+    "ansi-wrap": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz",
+      "integrity": "sha512-ZyznvL8k/FZeQHr2T6LzcJ/+vBApDnMNZvfVFy3At0knswWd6rJ3/0Hhmpu8oqa6C92npmozs890sX9Dl6q+Qw==",
+      "dev": true
+    },
+    "anymatch": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
+      "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
+      "dev": true,
+      "requires": {
+        "normalize-path": "^3.0.0",
+        "picomatch": "^2.0.4"
+      }
+    },
+    "apache-crypt": {
+      "version": "1.2.5",
+      "resolved": "https://registry.npmjs.org/apache-crypt/-/apache-crypt-1.2.5.tgz",
+      "integrity": "sha512-ICnYQH+DFVmw+S4Q0QY2XRXD8Ne8ewh8HgbuFH4K7022zCxgHM0Hz1xkRnUlEfAXNbwp1Cnhbedu60USIfDxvg==",
+      "dev": true,
+      "requires": {
+        "unix-crypt-td-js": "^1.1.4"
+      }
+    },
+    "apache-md5": {
+      "version": "1.1.7",
+      "resolved": "https://registry.npmjs.org/apache-md5/-/apache-md5-1.1.7.tgz",
+      "integrity": "sha512-JtHjzZmJxtzfTSjsCyHgPR155HBe5WGyUyHTaEkfy46qhwCFKx1Epm6nAxgUG3WfUZP1dWhGqj9Z2NOBeZ+uBw==",
+      "dev": true
+    },
+    "app-root-dir": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/app-root-dir/-/app-root-dir-1.0.2.tgz",
+      "integrity": "sha512-jlpIfsOoNoafl92Sz//64uQHGSyMrD2vYG5d8o2a4qGvyNCvXur7bzIsWtAC/6flI2RYAp3kv8rsfBtaLm7w0g==",
+      "dev": true
+    },
+    "aproba": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz",
+      "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==",
+      "dev": true
+    },
+    "are-we-there-yet": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.0.tgz",
+      "integrity": "sha512-0GWpv50YSOcLXaN6/FAKY3vfRbllXWV2xvfA/oKJF8pzFhWXPV+yjhJXDBbjscDYowv7Yw1A3uigpzn5iEGTyw==",
+      "dev": true,
+      "requires": {
+        "delegates": "^1.0.0",
+        "readable-stream": "^3.6.0"
+      },
+      "dependencies": {
+        "readable-stream": {
+          "version": "3.6.0",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+          "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+          "dev": true,
+          "requires": {
+            "inherits": "^2.0.3",
+            "string_decoder": "^1.1.1",
+            "util-deprecate": "^1.0.1"
+          }
+        }
+      }
+    },
+    "argparse": {
+      "version": "1.0.10",
+      "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+      "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+      "dev": true,
+      "requires": {
+        "sprintf-js": "~1.0.2"
+      }
+    },
+    "aria-query": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.0.0.tgz",
+      "integrity": "sha512-V+SM7AbUwJ+EBnB8+DXs0hPZHO0W6pqBcc0dW90OwtVG02PswOu/teuARoLQjdDOH+t9pJgGnW5/Qmouf3gPJg==",
+      "dev": true
+    },
+    "arr-diff": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
+      "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==",
+      "dev": true
+    },
+    "arr-flatten": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz",
+      "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==",
+      "dev": true
+    },
+    "arr-union": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
+      "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==",
+      "dev": true
+    },
+    "array-find-index": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz",
+      "integrity": "sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==",
+      "dev": true,
+      "optional": true
+    },
+    "array-flatten": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz",
+      "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==",
+      "dev": true
+    },
+    "array-from": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/array-from/-/array-from-2.1.1.tgz",
+      "integrity": "sha512-GQTc6Uupx1FCavi5mPzBvVT7nEOeWMmUA9P95wpfpW1XwMSKs+KaymD5C2Up7KAUKg/mYwbsUYzdZWcoajlNZg==",
+      "dev": true
+    },
+    "array-includes": {
+      "version": "3.1.5",
+      "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.5.tgz",
+      "integrity": "sha512-iSDYZMMyTPkiFasVqfuAQnWAYcvO/SeBSCGKePoEthjp4LEMTe4uLc7b025o4jAZpHhihh8xPo99TNWUWWkGDQ==",
+      "dev": true,
+      "requires": {
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.4",
+        "es-abstract": "^1.19.5",
+        "get-intrinsic": "^1.1.1",
+        "is-string": "^1.0.7"
+      }
+    },
+    "array-union": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
+      "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
+      "dev": true
+    },
+    "array-uniq": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz",
+      "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==",
+      "dev": true
+    },
+    "array-unique": {
+      "version": "0.3.2",
+      "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
+      "integrity": "sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==",
+      "dev": true
+    },
+    "array.prototype.flat": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.0.tgz",
+      "integrity": "sha512-12IUEkHsAhA4DY5s0FPgNXIdc8VRSqD9Zp78a5au9abH/SOBrsp082JOWFNTjkMozh8mqcdiKuaLGhPeYztxSw==",
+      "dev": true,
+      "requires": {
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.3",
+        "es-abstract": "^1.19.2",
+        "es-shim-unscopables": "^1.0.0"
+      }
+    },
+    "array.prototype.flatmap": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.0.tgz",
+      "integrity": "sha512-PZC9/8TKAIxcWKdyeb77EzULHPrIX/tIZebLJUQOMR1OwYosT8yggdfWScfTBCDj5utONvOuPQQumYsU2ULbkg==",
+      "dev": true,
+      "requires": {
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.3",
+        "es-abstract": "^1.19.2",
+        "es-shim-unscopables": "^1.0.0"
+      }
+    },
+    "array.prototype.map": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/array.prototype.map/-/array.prototype.map-1.0.4.tgz",
+      "integrity": "sha512-Qds9QnX7A0qISY7JT5WuJO0NJPE9CMlC6JzHQfhpqAAQQzufVRoeH7EzUY5GcPTx72voG8LV/5eo+b8Qi8hmhA==",
+      "dev": true,
+      "requires": {
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.3",
+        "es-abstract": "^1.19.0",
+        "es-array-method-boxes-properly": "^1.0.0",
+        "is-string": "^1.0.7"
+      }
+    },
+    "array.prototype.reduce": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/array.prototype.reduce/-/array.prototype.reduce-1.0.4.tgz",
+      "integrity": "sha512-WnM+AjG/DvLRLo4DDl+r+SvCzYtD2Jd9oeBYMcEaI7t3fFrHY9M53/wdLcTvmZNQ70IU6Htj0emFkZ5TS+lrdw==",
+      "dev": true,
+      "requires": {
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.3",
+        "es-abstract": "^1.19.2",
+        "es-array-method-boxes-properly": "^1.0.0",
+        "is-string": "^1.0.7"
+      }
+    },
+    "arrify": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz",
+      "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==",
+      "dev": true
+    },
+    "asn1.js": {
+      "version": "5.4.1",
+      "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz",
+      "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==",
+      "dev": true,
+      "requires": {
+        "bn.js": "^4.0.0",
+        "inherits": "^2.0.1",
+        "minimalistic-assert": "^1.0.0",
+        "safer-buffer": "^2.1.0"
+      },
+      "dependencies": {
+        "bn.js": {
+          "version": "4.12.0",
+          "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
+          "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==",
+          "dev": true
+        }
+      }
+    },
+    "assert": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz",
+      "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==",
+      "dev": true,
+      "requires": {
+        "object-assign": "^4.1.1",
+        "util": "0.10.3"
+      },
+      "dependencies": {
+        "inherits": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz",
+          "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=",
+          "dev": true
+        },
+        "util": {
+          "version": "0.10.3",
+          "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz",
+          "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=",
+          "dev": true,
+          "requires": {
+            "inherits": "2.0.1"
+          }
+        }
+      }
+    },
+    "assign-symbols": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
+      "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==",
+      "dev": true
+    },
+    "ast-transform": {
+      "version": "0.0.0",
+      "resolved": "https://registry.npmjs.org/ast-transform/-/ast-transform-0.0.0.tgz",
+      "integrity": "sha512-e/JfLiSoakfmL4wmTGPjv0HpTICVmxwXgYOB8x+mzozHL8v+dSfCbrJ8J8hJ0YBP0XcYu1aLZ6b/3TnxNK3P2A==",
+      "dev": true,
+      "requires": {
+        "escodegen": "~1.2.0",
+        "esprima": "~1.0.4",
+        "through": "~2.3.4"
+      },
+      "dependencies": {
+        "escodegen": {
+          "version": "1.2.0",
+          "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.2.0.tgz",
+          "integrity": "sha1-Cd55Z3kcyVi3+Jot220jRRrzJ+E=",
+          "dev": true,
+          "requires": {
+            "esprima": "~1.0.4",
+            "estraverse": "~1.5.0",
+            "esutils": "~1.0.0",
+            "source-map": "~0.1.30"
+          }
+        },
+        "esprima": {
+          "version": "1.0.4",
+          "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz",
+          "integrity": "sha1-n1V+CPw7TSbs6d00+Pv0drYlha0=",
+          "dev": true
+        },
+        "estraverse": {
+          "version": "1.5.1",
+          "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.5.1.tgz",
+          "integrity": "sha1-hno+jlip+EYYr7bC3bzZFrfLr3E=",
+          "dev": true
+        },
+        "esutils": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/esutils/-/esutils-1.0.0.tgz",
+          "integrity": "sha1-gVHTWOIMisx/t0XnRywAJf5JZXA=",
+          "dev": true
+        },
+        "source-map": {
+          "version": "0.1.43",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz",
+          "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "amdefine": ">=0.0.4"
+          }
+        }
+      }
+    },
+    "ast-types": {
+      "version": "0.7.8",
+      "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.7.8.tgz",
+      "integrity": "sha512-RIOpVnVlltB6PcBJ5BMLx+H+6JJ/zjDGU0t7f0L6c2M1dqcK92VQopLBlPQ9R80AVXelfqYgjcPLtHtDbNFg0Q==",
+      "dev": true
+    },
+    "astral-regex": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz",
+      "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==",
+      "dev": true
+    },
+    "async": {
+      "version": "2.6.4",
+      "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz",
+      "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==",
+      "dev": true,
+      "requires": {
+        "lodash": "^4.17.14"
+      }
+    },
+    "async-each": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz",
+      "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==",
+      "dev": true
+    },
+    "async-limiter": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
+      "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==",
+      "dev": true
+    },
+    "asynckit": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+      "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+      "dev": true
+    },
+    "at-least-node": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz",
+      "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==",
+      "dev": true
+    },
+    "atob": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
+      "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
+      "dev": true
+    },
+    "autoprefixer": {
+      "version": "9.8.8",
+      "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.8.tgz",
+      "integrity": "sha512-eM9d/swFopRt5gdJ7jrpCwgvEMIayITpojhkkSMRsFHYuH5bkSQ4p/9qTEHtmNudUZh22Tehu7I6CxAW0IXTKA==",
+      "dev": true,
+      "requires": {
+        "browserslist": "^4.12.0",
+        "caniuse-lite": "^1.0.30001109",
+        "normalize-range": "^0.1.2",
+        "num2fraction": "^1.2.2",
+        "picocolors": "^0.2.1",
+        "postcss": "^7.0.32",
+        "postcss-value-parser": "^4.1.0"
+      },
+      "dependencies": {
+        "picocolors": {
+          "version": "0.2.1",
+          "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz",
+          "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==",
+          "dev": true
+        },
+        "postcss": {
+          "version": "7.0.39",
+          "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz",
+          "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==",
+          "dev": true,
+          "requires": {
+            "picocolors": "^0.2.1",
+            "source-map": "^0.6.1"
+          }
+        },
+        "source-map": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+          "dev": true
+        }
+      }
+    },
+    "babel-loader": {
+      "version": "8.2.5",
+      "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.5.tgz",
+      "integrity": "sha512-OSiFfH89LrEMiWd4pLNqGz4CwJDtbs2ZVc+iGu2HrkRfPxId9F2anQj38IxWpmRfsUY0aBZYi1EFcd3mhtRMLQ==",
+      "dev": true,
+      "requires": {
+        "find-cache-dir": "^3.3.1",
+        "loader-utils": "^2.0.0",
+        "make-dir": "^3.1.0",
+        "schema-utils": "^2.6.5"
+      },
+      "dependencies": {
+        "schema-utils": {
+          "version": "2.7.1",
+          "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz",
+          "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==",
+          "dev": true,
+          "requires": {
+            "@types/json-schema": "^7.0.5",
+            "ajv": "^6.12.4",
+            "ajv-keywords": "^3.5.2"
+          }
+        }
+      }
+    },
+    "babel-plugin-apply-mdx-type-prop": {
+      "version": "1.6.22",
+      "resolved": "https://registry.npmjs.org/babel-plugin-apply-mdx-type-prop/-/babel-plugin-apply-mdx-type-prop-1.6.22.tgz",
+      "integrity": "sha512-VefL+8o+F/DfK24lPZMtJctrCVOfgbqLAGZSkxwhazQv4VxPg3Za/i40fu22KR2m8eEda+IfSOlPLUSIiLcnCQ==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "7.10.4",
+        "@mdx-js/util": "1.6.22"
+      },
+      "dependencies": {
+        "@babel/helper-plugin-utils": {
+          "version": "7.10.4",
+          "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz",
+          "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==",
+          "dev": true
+        }
+      }
+    },
+    "babel-plugin-dynamic-import-node": {
+      "version": "2.3.3",
+      "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz",
+      "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==",
+      "dev": true,
+      "requires": {
+        "object.assign": "^4.1.0"
+      }
+    },
+    "babel-plugin-extract-import-names": {
+      "version": "1.6.22",
+      "resolved": "https://registry.npmjs.org/babel-plugin-extract-import-names/-/babel-plugin-extract-import-names-1.6.22.tgz",
+      "integrity": "sha512-yJ9BsJaISua7d8zNT7oRG1ZLBJCIdZ4PZqmH8qa9N5AK01ifk3fnkc98AXhtzE7UkfCsEumvoQWgoYLhOnJ7jQ==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "7.10.4"
+      },
+      "dependencies": {
+        "@babel/helper-plugin-utils": {
+          "version": "7.10.4",
+          "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz",
+          "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==",
+          "dev": true
+        }
+      }
+    },
+    "babel-plugin-istanbul": {
+      "version": "6.1.1",
+      "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz",
+      "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.0.0",
+        "@istanbuljs/load-nyc-config": "^1.0.0",
+        "@istanbuljs/schema": "^0.1.2",
+        "istanbul-lib-instrument": "^5.0.4",
+        "test-exclude": "^6.0.0"
+      },
+      "dependencies": {
+        "istanbul-lib-instrument": {
+          "version": "5.2.0",
+          "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.0.tgz",
+          "integrity": "sha512-6Lthe1hqXHBNsqvgDzGO6l03XNeu3CrG4RqQ1KM9+l5+jNGpEJfIELx1NS3SEHmJQA8np/u+E4EPRKRiu6m19A==",
+          "dev": true,
+          "requires": {
+            "@babel/core": "^7.12.3",
+            "@babel/parser": "^7.14.7",
+            "@istanbuljs/schema": "^0.1.2",
+            "istanbul-lib-coverage": "^3.2.0",
+            "semver": "^6.3.0"
+          }
+        },
+        "semver": {
+          "version": "6.3.0",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+          "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+          "dev": true
+        }
+      }
+    },
+    "babel-plugin-macros": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz",
+      "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==",
+      "dev": true,
+      "requires": {
+        "@babel/runtime": "^7.12.5",
+        "cosmiconfig": "^7.0.0",
+        "resolve": "^1.19.0"
+      }
+    },
+    "babel-plugin-named-exports-order": {
+      "version": "0.0.2",
+      "resolved": "https://registry.npmjs.org/babel-plugin-named-exports-order/-/babel-plugin-named-exports-order-0.0.2.tgz",
+      "integrity": "sha512-OgOYHOLoRK+/mvXU9imKHlG6GkPLYrUCvFXG/CM93R/aNNO8pOOF4aS+S8CCHMDQoNSeiOYEZb/G6RwL95Jktw==",
+      "dev": true
+    },
+    "babel-plugin-polyfill-corejs2": {
+      "version": "0.2.3",
+      "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.2.3.tgz",
+      "integrity": "sha512-NDZ0auNRzmAfE1oDDPW2JhzIMXUk+FFe2ICejmt5T4ocKgiQx3e0VCRx9NCAidcMtL2RUZaWtXnmjTCkx0tcbA==",
+      "dev": true,
+      "requires": {
+        "@babel/compat-data": "^7.13.11",
+        "@babel/helper-define-polyfill-provider": "^0.2.4",
+        "semver": "^6.1.1"
+      },
+      "dependencies": {
+        "semver": {
+          "version": "6.3.0",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+          "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+          "dev": true
+        }
+      }
+    },
+    "babel-plugin-polyfill-corejs3": {
+      "version": "0.2.5",
+      "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.2.5.tgz",
+      "integrity": "sha512-ninF5MQNwAX9Z7c9ED+H2pGt1mXdP4TqzlHKyPIYmJIYz0N+++uwdM7RnJukklhzJ54Q84vA4ZJkgs7lu5vqcw==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-define-polyfill-provider": "^0.2.2",
+        "core-js-compat": "^3.16.2"
+      }
+    },
+    "babel-plugin-polyfill-regenerator": {
+      "version": "0.2.3",
+      "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.2.3.tgz",
+      "integrity": "sha512-JVE78oRZPKFIeUqFGrSORNzQnrDwZR16oiWeGM8ZyjBn2XAT5OjP+wXx5ESuo33nUsFUEJYjtklnsKbxW5L+7g==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-define-polyfill-provider": "^0.2.4"
+      }
+    },
+    "bail": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.5.tgz",
+      "integrity": "sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ==",
+      "dev": true
+    },
+    "balanced-match": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+      "dev": true
+    },
+    "base": {
+      "version": "0.11.2",
+      "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz",
+      "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==",
+      "dev": true,
+      "requires": {
+        "cache-base": "^1.0.1",
+        "class-utils": "^0.3.5",
+        "component-emitter": "^1.2.1",
+        "define-property": "^1.0.0",
+        "isobject": "^3.0.1",
+        "mixin-deep": "^1.2.0",
+        "pascalcase": "^0.1.1"
+      },
+      "dependencies": {
+        "define-property": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+          "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+          "dev": true,
+          "requires": {
+            "is-descriptor": "^1.0.0"
+          }
+        },
+        "is-accessor-descriptor": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+          "dev": true,
+          "requires": {
+            "kind-of": "^6.0.0"
+          }
+        },
+        "is-data-descriptor": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+          "dev": true,
+          "requires": {
+            "kind-of": "^6.0.0"
+          }
+        },
+        "is-descriptor": {
+          "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+          "dev": true,
+          "requires": {
+            "is-accessor-descriptor": "^1.0.0",
+            "is-data-descriptor": "^1.0.0",
+            "kind-of": "^6.0.2"
+          }
+        }
+      }
+    },
+    "base64-js": {
+      "version": "1.5.1",
+      "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+      "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+      "dev": true
+    },
+    "base64id": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
+      "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==",
+      "dev": true
+    },
+    "basic-auth": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
+      "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==",
+      "dev": true,
+      "requires": {
+        "safe-buffer": "5.1.2"
+      }
+    },
+    "batch": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz",
+      "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==",
+      "dev": true
+    },
+    "bcryptjs": {
+      "version": "2.4.3",
+      "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz",
+      "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==",
+      "dev": true
+    },
+    "better-opn": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/better-opn/-/better-opn-2.1.1.tgz",
+      "integrity": "sha512-kIPXZS5qwyKiX/HcRvDYfmBQUa8XP17I0mYZZ0y4UhpYOSvtsLHDYqmomS+Mj20aDvD3knEiQ0ecQy2nhio3yA==",
+      "dev": true,
+      "requires": {
+        "open": "^7.0.3"
+      },
+      "dependencies": {
+        "open": {
+          "version": "7.4.2",
+          "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz",
+          "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==",
+          "dev": true,
+          "requires": {
+            "is-docker": "^2.0.0",
+            "is-wsl": "^2.1.1"
+          }
+        }
+      }
+    },
+    "big-integer": {
+      "version": "1.6.51",
+      "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz",
+      "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==",
+      "dev": true,
+      "optional": true
+    },
+    "big.js": {
+      "version": "5.2.2",
+      "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
+      "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ=="
+    },
+    "binary-extensions": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
+      "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
+      "dev": true
+    },
+    "bl": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
+      "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
+      "dev": true,
+      "requires": {
+        "buffer": "^5.5.0",
+        "inherits": "^2.0.4",
+        "readable-stream": "^3.4.0"
+      },
+      "dependencies": {
+        "readable-stream": {
+          "version": "3.6.0",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+          "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+          "dev": true,
+          "requires": {
+            "inherits": "^2.0.3",
+            "string_decoder": "^1.1.1",
+            "util-deprecate": "^1.0.1"
+          }
+        }
+      }
+    },
+    "bluebird": {
+      "version": "3.7.2",
+      "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
+      "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==",
+      "dev": true
+    },
+    "bn.js": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz",
+      "integrity": "sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==",
+      "dev": true
+    },
+    "body-parser": {
+      "version": "1.20.0",
+      "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz",
+      "integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==",
+      "dev": true,
+      "requires": {
+        "bytes": "3.1.2",
+        "content-type": "~1.0.4",
+        "debug": "2.6.9",
+        "depd": "2.0.0",
+        "destroy": "1.2.0",
+        "http-errors": "2.0.0",
+        "iconv-lite": "0.4.24",
+        "on-finished": "2.4.1",
+        "qs": "6.10.3",
+        "raw-body": "2.5.1",
+        "type-is": "~1.6.18",
+        "unpipe": "1.0.0"
+      },
+      "dependencies": {
+        "bytes": {
+          "version": "3.1.2",
+          "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
+          "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
+          "dev": true
+        },
+        "debug": {
+          "version": "2.6.9",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+          "dev": true,
+          "requires": {
+            "ms": "2.0.0"
+          }
+        },
+        "ms": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+          "dev": true
+        }
+      }
+    },
+    "bonjour": {
+      "version": "3.5.0",
+      "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz",
+      "integrity": "sha512-RaVTblr+OnEli0r/ud8InrU7D+G0y6aJhlxaLa6Pwty4+xoxboF1BsUI45tujvRpbj9dQVoglChqonGAsjEBYg==",
+      "dev": true,
+      "requires": {
+        "array-flatten": "^2.1.0",
         "deep-equal": "^1.0.1",
         "dns-equal": "^1.0.0",
         "dns-txt": "^2.0.2",
@@ -3019,210 +10519,3345 @@
         "multicast-dns-service-types": "^1.1.0"
       }
     },
-    "boolbase": {
+    "boolbase": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
+      "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
+      "dev": true
+    },
+    "boxen": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz",
+      "integrity": "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==",
+      "dev": true,
+      "requires": {
+        "ansi-align": "^3.0.0",
+        "camelcase": "^6.2.0",
+        "chalk": "^4.1.0",
+        "cli-boxes": "^2.2.1",
+        "string-width": "^4.2.2",
+        "type-fest": "^0.20.2",
+        "widest-line": "^3.1.0",
+        "wrap-ansi": "^7.0.0"
+      },
+      "dependencies": {
+        "ansi-styles": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+          "dev": true,
+          "requires": {
+            "color-convert": "^2.0.1"
+          }
+        },
+        "camelcase": {
+          "version": "6.3.0",
+          "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
+          "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
+          "dev": true
+        },
+        "chalk": {
+          "version": "4.1.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+          "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "dev": true,
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
+        },
+        "type-fest": {
+          "version": "0.20.2",
+          "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+          "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+          "dev": true
+        },
+        "wrap-ansi": {
+          "version": "7.0.0",
+          "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+          "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^4.0.0",
+            "string-width": "^4.1.0",
+            "strip-ansi": "^6.0.0"
+          }
+        }
+      }
+    },
+    "bplist-parser": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.1.1.tgz",
+      "integrity": "sha512-2AEM0FXy8ZxVLBuqX0hqt1gDwcnz2zygEkQ6zaD5Wko/sB9paUNwlpawrFtKeHUAQUOzjVy9AO4oeonqIHKA9Q==",
+      "dev": true,
+      "optional": true,
+      "requires": {
+        "big-integer": "^1.6.7"
+      }
+    },
+    "brace-expansion": {
+      "version": "1.1.11",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+      "dev": true,
+      "requires": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "braces": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+      "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+      "dev": true,
+      "requires": {
+        "fill-range": "^7.0.1"
+      }
+    },
+    "brfs": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/brfs/-/brfs-2.0.2.tgz",
+      "integrity": "sha512-IrFjVtwu4eTJZyu8w/V2gxU7iLTtcHih67sgEdzrhjLBMHp2uYefUBfdM4k2UvcuWMgV7PQDZHSLeNWnLFKWVQ==",
+      "dev": true,
+      "requires": {
+        "quote-stream": "^1.0.1",
+        "resolve": "^1.1.5",
+        "static-module": "^3.0.2",
+        "through2": "^2.0.0"
+      }
+    },
+    "brorand": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
+      "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==",
+      "dev": true
+    },
+    "brotli": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.2.tgz",
+      "integrity": "sha512-K0HNa0RRpUpcF8yS4yNSd6vmkrvA+wRd+symIcwhfqGLAi7YgGlKfO4oDYVgiahiLGNviO9uY7Zlb1MCPeTmSA==",
+      "dev": true,
+      "requires": {
+        "base64-js": "^1.1.2"
+      }
+    },
+    "browser-assert": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/browser-assert/-/browser-assert-1.2.1.tgz",
+      "integrity": "sha512-nfulgvOR6S4gt9UKCeGJOuSGBPGiFT6oQ/2UBnvTY/5aQ1PnksW72fhZkM30DzoRRv2WpwZf1vHHEr3mtuXIWQ==",
+      "dev": true
+    },
+    "browser-resolve": {
+      "version": "1.11.3",
+      "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz",
+      "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==",
+      "dev": true,
+      "requires": {
+        "resolve": "1.1.7"
+      },
+      "dependencies": {
+        "resolve": {
+          "version": "1.1.7",
+          "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz",
+          "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=",
+          "dev": true
+        }
+      }
+    },
+    "browserify-aes": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
+      "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==",
+      "dev": true,
+      "requires": {
+        "buffer-xor": "^1.0.3",
+        "cipher-base": "^1.0.0",
+        "create-hash": "^1.1.0",
+        "evp_bytestokey": "^1.0.3",
+        "inherits": "^2.0.1",
+        "safe-buffer": "^5.0.1"
+      }
+    },
+    "browserify-cipher": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz",
+      "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==",
+      "dev": true,
+      "requires": {
+        "browserify-aes": "^1.0.4",
+        "browserify-des": "^1.0.0",
+        "evp_bytestokey": "^1.0.0"
+      }
+    },
+    "browserify-des": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz",
+      "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==",
+      "dev": true,
+      "requires": {
+        "cipher-base": "^1.0.1",
+        "des.js": "^1.0.0",
+        "inherits": "^2.0.1",
+        "safe-buffer": "^5.1.2"
+      }
+    },
+    "browserify-optional": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/browserify-optional/-/browserify-optional-1.0.1.tgz",
+      "integrity": "sha512-VrhjbZ+Ba5mDiSYEuPelekQMfTbhcA2DhLk2VQWqdcCROWeFqlTcXZ7yfRkXCIl8E+g4gINJYJiRB7WEtfomAQ==",
+      "dev": true,
+      "requires": {
+        "ast-transform": "0.0.0",
+        "ast-types": "^0.7.0",
+        "browser-resolve": "^1.8.1"
+      }
+    },
+    "browserify-rsa": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz",
+      "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==",
+      "dev": true,
+      "requires": {
+        "bn.js": "^5.0.0",
+        "randombytes": "^2.0.1"
+      }
+    },
+    "browserify-sign": {
+      "version": "4.2.1",
+      "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz",
+      "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==",
+      "dev": true,
+      "requires": {
+        "bn.js": "^5.1.1",
+        "browserify-rsa": "^4.0.1",
+        "create-hash": "^1.2.0",
+        "create-hmac": "^1.1.7",
+        "elliptic": "^6.5.3",
+        "inherits": "^2.0.4",
+        "parse-asn1": "^5.1.5",
+        "readable-stream": "^3.6.0",
+        "safe-buffer": "^5.2.0"
+      },
+      "dependencies": {
+        "readable-stream": {
+          "version": "3.6.0",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+          "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+          "dev": true,
+          "requires": {
+            "inherits": "^2.0.3",
+            "string_decoder": "^1.1.1",
+            "util-deprecate": "^1.0.1"
+          }
+        },
+        "safe-buffer": {
+          "version": "5.2.1",
+          "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+          "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+          "dev": true
+        }
+      }
+    },
+    "browserify-zlib": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz",
+      "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==",
+      "dev": true,
+      "requires": {
+        "pako": "~1.0.5"
+      }
+    },
+    "browserslist": {
+      "version": "4.20.3",
+      "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.3.tgz",
+      "integrity": "sha512-NBhymBQl1zM0Y5dQT/O+xiLP9/rzOIQdKM/eMJBAq7yBgaB6krIYLGejrwVYnSHZdqjscB1SPuAjHwxjvN6Wdg==",
+      "dev": true,
+      "requires": {
+        "caniuse-lite": "^1.0.30001332",
+        "electron-to-chromium": "^1.4.118",
+        "escalade": "^3.1.1",
+        "node-releases": "^2.0.3",
+        "picocolors": "^1.0.0"
+      }
+    },
+    "bser": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz",
+      "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==",
+      "dev": true,
+      "requires": {
+        "node-int64": "^0.4.0"
+      }
+    },
+    "buffer": {
+      "version": "5.7.1",
+      "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
+      "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
+      "dev": true,
+      "requires": {
+        "base64-js": "^1.3.1",
+        "ieee754": "^1.1.13"
+      }
+    },
+    "buffer-equal": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-0.0.1.tgz",
+      "integrity": "sha512-RgSV6InVQ9ODPdLWJ5UAqBqJBOg370Nz6ZQtRzpt6nUjc8v0St97uJ4PYC6NztqIScrAXafKM3mZPMygSe1ggA==",
+      "dev": true
+    },
+    "buffer-from": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
+      "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
+      "dev": true
+    },
+    "buffer-indexof": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz",
+      "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==",
+      "dev": true
+    },
+    "buffer-xor": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz",
+      "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==",
+      "dev": true
+    },
+    "builtin-status-codes": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz",
+      "integrity": "sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==",
+      "dev": true
+    },
+    "builtins": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz",
+      "integrity": "sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ==",
+      "dev": true
+    },
+    "bytes": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
+      "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==",
+      "dev": true
+    },
+    "cacache": {
+      "version": "15.2.0",
+      "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.2.0.tgz",
+      "integrity": "sha512-uKoJSHmnrqXgthDFx/IU6ED/5xd+NNGe+Bb+kLZy7Ku4P+BaiWEUflAKPZ7eAzsYGcsAGASJZsybXp+quEcHTw==",
+      "dev": true,
+      "requires": {
+        "@npmcli/move-file": "^1.0.1",
+        "chownr": "^2.0.0",
+        "fs-minipass": "^2.0.0",
+        "glob": "^7.1.4",
+        "infer-owner": "^1.0.4",
+        "lru-cache": "^6.0.0",
+        "minipass": "^3.1.1",
+        "minipass-collect": "^1.0.2",
+        "minipass-flush": "^1.0.5",
+        "minipass-pipeline": "^1.2.2",
+        "mkdirp": "^1.0.3",
+        "p-map": "^4.0.0",
+        "promise-inflight": "^1.0.1",
+        "rimraf": "^3.0.2",
+        "ssri": "^8.0.1",
+        "tar": "^6.0.2",
+        "unique-filename": "^1.1.1"
+      }
+    },
+    "cache-base": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
+      "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==",
+      "dev": true,
+      "requires": {
+        "collection-visit": "^1.0.0",
+        "component-emitter": "^1.2.1",
+        "get-value": "^2.0.6",
+        "has-value": "^1.0.0",
+        "isobject": "^3.0.1",
+        "set-value": "^2.0.0",
+        "to-object-path": "^0.3.0",
+        "union-value": "^1.0.0",
+        "unset-value": "^1.0.0"
+      }
+    },
+    "call-bind": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
+      "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
+      "dev": true,
+      "requires": {
+        "function-bind": "^1.1.1",
+        "get-intrinsic": "^1.0.2"
+      }
+    },
+    "call-me-maybe": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz",
+      "integrity": "sha512-wCyFsDQkKPwwF8BDwOiWNx/9K45L/hvggQiDbve+viMNMQnWhrlYIuBk09offfwCRtCO9P6XwUttufzU11WCVw==",
+      "dev": true
+    },
+    "callsite": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz",
+      "integrity": "sha512-0vdNRFXn5q+dtOqjfFtmtlI9N2eVZ7LMyEV2iKC5mEEFvSg/69Ml6b/WU2qF8W1nLRa0wiSrDT3Y5jOHZCwKPQ==",
+      "dev": true
+    },
+    "callsites": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+      "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+      "dev": true
+    },
+    "camel-case": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz",
+      "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==",
+      "dev": true,
+      "requires": {
+        "pascal-case": "^3.1.2",
+        "tslib": "^2.0.3"
+      }
+    },
+    "camelcase": {
+      "version": "5.3.1",
+      "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+      "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+      "dev": true
+    },
+    "camelcase-css": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
+      "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==",
+      "dev": true
+    },
+    "camelcase-keys": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz",
+      "integrity": "sha512-bA/Z/DERHKqoEOrp+qeGKw1QlvEQkGZSc0XaY6VnTxZr+Kv1G5zFwttpjv8qxZ/sBPT4nthwZaAcsAZTJlSKXQ==",
+      "dev": true,
+      "optional": true,
+      "requires": {
+        "camelcase": "^2.0.0",
+        "map-obj": "^1.0.0"
+      },
+      "dependencies": {
+        "camelcase": {
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz",
+          "integrity": "sha512-DLIsRzJVBQu72meAKPkWQOLcujdXT32hwdfnkI1frSiSRMK1MofjKHf+MEx0SB6fjEFXL8fBDv1dKymBlOp4Qw==",
+          "dev": true,
+          "optional": true
+        }
+      }
+    },
+    "caniuse-api": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz",
+      "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==",
+      "dev": true,
+      "requires": {
+        "browserslist": "^4.0.0",
+        "caniuse-lite": "^1.0.0",
+        "lodash.memoize": "^4.1.2",
+        "lodash.uniq": "^4.5.0"
+      }
+    },
+    "caniuse-lite": {
+      "version": "1.0.30001342",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001342.tgz",
+      "integrity": "sha512-bn6sOCu7L7jcbBbyNhLg0qzXdJ/PMbybZTH/BA6Roet9wxYRm6Tr9D0s0uhLkOZ6MSG+QU6txUgdpr3MXIVqjA==",
+      "dev": true
+    },
+    "canonical-path": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/canonical-path/-/canonical-path-1.0.0.tgz",
+      "integrity": "sha512-feylzsbDxi1gPZ1IjystzIQZagYYLvfKrSuygUCgf7z6x790VEzze5QEkdSV1U58RA7Hi0+v6fv4K54atOzATg==",
+      "dev": true
+    },
+    "capture-exit": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz",
+      "integrity": "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==",
+      "dev": true,
+      "requires": {
+        "rsvp": "^4.8.4"
+      }
+    },
+    "case-sensitive-paths-webpack-plugin": {
+      "version": "2.4.0",
+      "resolved": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz",
+      "integrity": "sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw==",
+      "dev": true
+    },
+    "ccount": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/ccount/-/ccount-1.1.0.tgz",
+      "integrity": "sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg==",
+      "dev": true
+    },
+    "chalk": {
+      "version": "2.4.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+      "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+      "dev": true,
+      "requires": {
+        "ansi-styles": "^3.2.1",
+        "escape-string-regexp": "^1.0.5",
+        "supports-color": "^5.3.0"
+      }
+    },
+    "character-entities": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz",
+      "integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==",
+      "dev": true
+    },
+    "character-entities-legacy": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz",
+      "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==",
+      "dev": true
+    },
+    "character-reference-invalid": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz",
+      "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==",
+      "dev": true
+    },
+    "charcodes": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/charcodes/-/charcodes-0.2.0.tgz",
+      "integrity": "sha512-Y4kiDb+AM4Ecy58YkuZrrSRJBDQdQ2L+NyS1vHHFtNtUjgutcZfx3yp1dAONI/oPaPmyGfCLx5CxL+zauIMyKQ==",
+      "dev": true
+    },
+    "chardet": {
+      "version": "0.7.0",
+      "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
+      "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==",
+      "dev": true
+    },
+    "cheerio": {
+      "version": "1.0.0-rc.11",
+      "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.11.tgz",
+      "integrity": "sha512-bQwNaDIBKID5ts/DsdhxrjqFXYfLw4ste+wMKqWA8DyKcS4qwsPP4Bk8ZNaTJjvpiX/qW3BT4sU7d6Bh5i+dag==",
+      "dev": true,
+      "requires": {
+        "cheerio-select": "^2.1.0",
+        "dom-serializer": "^2.0.0",
+        "domhandler": "^5.0.3",
+        "domutils": "^3.0.1",
+        "htmlparser2": "^8.0.1",
+        "parse5": "^7.0.0",
+        "parse5-htmlparser2-tree-adapter": "^7.0.0",
+        "tslib": "^2.4.0"
+      },
+      "dependencies": {
+        "dom-serializer": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
+          "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
+          "dev": true,
+          "requires": {
+            "domelementtype": "^2.3.0",
+            "domhandler": "^5.0.2",
+            "entities": "^4.2.0"
+          }
+        },
+        "domhandler": {
+          "version": "5.0.3",
+          "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
+          "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
+          "dev": true,
+          "requires": {
+            "domelementtype": "^2.3.0"
+          }
+        },
+        "domutils": {
+          "version": "3.0.1",
+          "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz",
+          "integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==",
+          "dev": true,
+          "requires": {
+            "dom-serializer": "^2.0.0",
+            "domelementtype": "^2.3.0",
+            "domhandler": "^5.0.1"
+          }
+        },
+        "entities": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/entities/-/entities-4.3.0.tgz",
+          "integrity": "sha512-/iP1rZrSEJ0DTlPiX+jbzlA3eVkY/e8L8SozroF395fIqE3TYF/Nz7YOMAawta+vLmyJ/hkGNNPcSbMADCCXbg==",
+          "dev": true
+        },
+        "parse5": {
+          "version": "7.0.0",
+          "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.0.0.tgz",
+          "integrity": "sha512-y/t8IXSPWTuRZqXc0ajH/UwDj4mnqLEbSttNbThcFhGrZuOyoyvNBO85PBp2jQa55wY9d07PBNjsK8ZP3K5U6g==",
+          "dev": true,
+          "requires": {
+            "entities": "^4.3.0"
+          }
+        },
+        "parse5-htmlparser2-tree-adapter": {
+          "version": "7.0.0",
+          "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz",
+          "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==",
+          "dev": true,
+          "requires": {
+            "domhandler": "^5.0.2",
+            "parse5": "^7.0.0"
+          }
+        }
+      }
+    },
+    "cheerio-select": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz",
+      "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==",
+      "dev": true,
+      "requires": {
+        "boolbase": "^1.0.0",
+        "css-select": "^5.1.0",
+        "css-what": "^6.1.0",
+        "domelementtype": "^2.3.0",
+        "domhandler": "^5.0.3",
+        "domutils": "^3.0.1"
+      },
+      "dependencies": {
+        "css-select": {
+          "version": "5.1.0",
+          "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz",
+          "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==",
+          "dev": true,
+          "requires": {
+            "boolbase": "^1.0.0",
+            "css-what": "^6.1.0",
+            "domhandler": "^5.0.2",
+            "domutils": "^3.0.1",
+            "nth-check": "^2.0.1"
+          }
+        },
+        "dom-serializer": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
+          "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
+          "dev": true,
+          "requires": {
+            "domelementtype": "^2.3.0",
+            "domhandler": "^5.0.2",
+            "entities": "^4.2.0"
+          }
+        },
+        "domhandler": {
+          "version": "5.0.3",
+          "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
+          "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
+          "dev": true,
+          "requires": {
+            "domelementtype": "^2.3.0"
+          }
+        },
+        "domutils": {
+          "version": "3.0.1",
+          "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz",
+          "integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==",
+          "dev": true,
+          "requires": {
+            "dom-serializer": "^2.0.0",
+            "domelementtype": "^2.3.0",
+            "domhandler": "^5.0.1"
+          }
+        },
+        "entities": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/entities/-/entities-4.3.0.tgz",
+          "integrity": "sha512-/iP1rZrSEJ0DTlPiX+jbzlA3eVkY/e8L8SozroF395fIqE3TYF/Nz7YOMAawta+vLmyJ/hkGNNPcSbMADCCXbg==",
+          "dev": true
+        }
+      }
+    },
+    "chokidar": {
+      "version": "3.5.3",
+      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
+      "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
+      "dev": true,
+      "requires": {
+        "anymatch": "~3.1.2",
+        "braces": "~3.0.2",
+        "fsevents": "~2.3.2",
+        "glob-parent": "~5.1.2",
+        "is-binary-path": "~2.1.0",
+        "is-glob": "~4.0.1",
+        "normalize-path": "~3.0.0",
+        "readdirp": "~3.6.0"
+      },
+      "dependencies": {
+        "glob-parent": {
+          "version": "5.1.2",
+          "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+          "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+          "dev": true,
+          "requires": {
+            "is-glob": "^4.0.1"
+          }
+        }
+      }
+    },
+    "chownr": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
+      "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
+      "dev": true
+    },
+    "chrome-trace-event": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz",
+      "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==",
+      "dev": true
+    },
+    "ci-info": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz",
+      "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==",
+      "dev": true
+    },
+    "cipher-base": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
+      "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==",
+      "dev": true,
+      "requires": {
+        "inherits": "^2.0.1",
+        "safe-buffer": "^5.0.1"
+      }
+    },
+    "circular-dependency-plugin": {
+      "version": "5.2.2",
+      "resolved": "https://registry.npmjs.org/circular-dependency-plugin/-/circular-dependency-plugin-5.2.2.tgz",
+      "integrity": "sha512-g38K9Cm5WRwlaH6g03B9OEz/0qRizI+2I7n+Gz+L5DxXJAPAiWQvwlYNm1V1jkdpUv95bOe/ASm2vfi/G560jQ==",
+      "dev": true
+    },
+    "class-utils": {
+      "version": "0.3.6",
+      "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
+      "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==",
+      "dev": true,
+      "requires": {
+        "arr-union": "^3.1.0",
+        "define-property": "^0.2.5",
+        "isobject": "^3.0.0",
+        "static-extend": "^0.1.1"
+      },
+      "dependencies": {
+        "define-property": {
+          "version": "0.2.5",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+          "dev": true,
+          "requires": {
+            "is-descriptor": "^0.1.0"
+          }
+        }
+      }
+    },
+    "clean-css": {
+      "version": "4.2.4",
+      "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.4.tgz",
+      "integrity": "sha512-EJUDT7nDVFDvaQgAo2G/PJvxmp1o/c6iXLbswsBbUFXi1Nr+AjA2cKmfbKDMjMvzEe75g3P6JkaDDAKk96A85A==",
+      "dev": true,
+      "requires": {
+        "source-map": "~0.6.0"
+      },
+      "dependencies": {
+        "source-map": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+          "dev": true
+        }
+      }
+    },
+    "clean-stack": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
+      "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==",
+      "dev": true
+    },
+    "cli-boxes": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz",
+      "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==",
+      "dev": true
+    },
+    "cli-cursor": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
+      "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
+      "dev": true,
+      "requires": {
+        "restore-cursor": "^3.1.0"
+      }
+    },
+    "cli-spinners": {
+      "version": "2.6.1",
+      "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz",
+      "integrity": "sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==",
+      "dev": true
+    },
+    "cli-table3": {
+      "version": "0.6.2",
+      "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.2.tgz",
+      "integrity": "sha512-QyavHCaIC80cMivimWu4aWHilIpiDpfm3hGmqAmXVL1UsnbLuBSMd21hTX6VY4ZSDSM73ESLeF8TOYId3rBTbw==",
+      "dev": true,
+      "requires": {
+        "@colors/colors": "1.5.0",
+        "string-width": "^4.2.0"
+      }
+    },
+    "cli-width": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz",
+      "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==",
+      "dev": true
+    },
+    "cliui": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
+      "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
+      "dev": true,
+      "requires": {
+        "string-width": "^3.1.0",
+        "strip-ansi": "^5.2.0",
+        "wrap-ansi": "^5.1.0"
+      },
+      "dependencies": {
+        "ansi-regex": {
+          "version": "4.1.1",
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
+          "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==",
+          "dev": true
+        },
+        "emoji-regex": {
+          "version": "7.0.3",
+          "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
+          "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
+          "dev": true
+        },
+        "is-fullwidth-code-point": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+          "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+          "dev": true
+        },
+        "string-width": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+          "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+          "dev": true,
+          "requires": {
+            "emoji-regex": "^7.0.1",
+            "is-fullwidth-code-point": "^2.0.0",
+            "strip-ansi": "^5.1.0"
+          }
+        },
+        "strip-ansi": {
+          "version": "5.2.0",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+          "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+          "dev": true,
+          "requires": {
+            "ansi-regex": "^4.1.0"
+          }
+        }
+      }
+    },
+    "clone": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz",
+      "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=",
+      "dev": true
+    },
+    "clone-deep": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz",
+      "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==",
+      "dev": true,
+      "requires": {
+        "is-plain-object": "^2.0.4",
+        "kind-of": "^6.0.2",
+        "shallow-clone": "^3.0.0"
+      }
+    },
+    "clsx": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.1.0.tgz",
+      "integrity": "sha512-3avwM37fSK5oP6M5rQ9CNe99lwxhXDOeSWVPAOYF6OazUTgZCMb0yWlJpmdD74REy1gkEaFiub2ULv4fq9GUhA==",
+      "dev": true
+    },
+    "code-block-writer": {
+      "version": "11.0.0",
+      "resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-11.0.0.tgz",
+      "integrity": "sha512-GEqWvEWWsOvER+g9keO4ohFoD3ymwyCnqY3hoTr7GZipYFwEhMHJw+TtV0rfgRhNImM6QWZGO2XYjlJVyYT62w==",
+      "dev": true,
+      "requires": {
+        "tslib": "2.3.1"
+      },
+      "dependencies": {
+        "tslib": {
+          "version": "2.3.1",
+          "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
+          "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==",
+          "dev": true
+        }
+      }
+    },
+    "collapse-white-space": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-1.0.6.tgz",
+      "integrity": "sha512-jEovNnrhMuqyCcjfEJA56v0Xq8SkIoPKDyaHahwo3POf4qcSXqMYuwNcOTzp74vTsR9Tn08z4MxWqAhcekogkQ==",
+      "dev": true
+    },
+    "collection-visit": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
+      "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=",
+      "dev": true,
+      "requires": {
+        "map-visit": "^1.0.0",
+        "object-visit": "^1.0.0"
+      }
+    },
+    "color-convert": {
+      "version": "1.9.3",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+      "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+      "dev": true,
+      "requires": {
+        "color-name": "1.1.3"
+      }
+    },
+    "color-name": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+      "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+      "dev": true
+    },
+    "color-support": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz",
+      "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==",
+      "dev": true
+    },
+    "colord": {
+      "version": "2.9.2",
+      "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.2.tgz",
+      "integrity": "sha512-Uqbg+J445nc1TKn4FoDPS6ZZqAvEDnwrH42yo8B40JSOgSLxMZ/gt3h4nmCtPLQeXhjJJkqBx7SCY35WnIixaQ==",
+      "dev": true
+    },
+    "colorette": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz",
+      "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==",
+      "dev": true
+    },
+    "colors": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz",
+      "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==",
+      "dev": true
+    },
+    "combined-stream": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+      "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+      "dev": true,
+      "requires": {
+        "delayed-stream": "~1.0.0"
+      }
+    },
+    "comma-separated-tokens": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz",
+      "integrity": "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==",
+      "dev": true
+    },
+    "commander": {
+      "version": "9.2.0",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-9.2.0.tgz",
+      "integrity": "sha512-e2i4wANQiSXgnrBlIatyHtP1odfUp0BbV5Y5nEGbxtIrStkEOAAzCUirvLBNXHLr7kwLvJl6V+4V3XV9x7Wd9w=="
+    },
+    "commondir": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
+      "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=",
+      "dev": true
+    },
+    "component-emitter": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
+      "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==",
+      "dev": true
+    },
+    "compressible": {
+      "version": "2.0.18",
+      "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz",
+      "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==",
+      "dev": true,
+      "requires": {
+        "mime-db": ">= 1.43.0 < 2"
+      }
+    },
+    "compression": {
+      "version": "1.7.4",
+      "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz",
+      "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==",
+      "dev": true,
+      "requires": {
+        "accepts": "~1.3.5",
+        "bytes": "3.0.0",
+        "compressible": "~2.0.16",
+        "debug": "2.6.9",
+        "on-headers": "~1.0.2",
+        "safe-buffer": "5.1.2",
+        "vary": "~1.1.2"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "2.6.9",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+          "dev": true,
+          "requires": {
+            "ms": "2.0.0"
+          }
+        },
+        "ms": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+          "dev": true
+        }
+      }
+    },
+    "concat-map": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+      "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+      "dev": true
+    },
+    "concat-stream": {
+      "version": "1.6.2",
+      "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
+      "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
+      "dev": true,
+      "requires": {
+        "buffer-from": "^1.0.0",
+        "inherits": "^2.0.3",
+        "readable-stream": "^2.2.2",
+        "typedarray": "^0.0.6"
+      }
+    },
+    "connect": {
+      "version": "3.7.0",
+      "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz",
+      "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==",
+      "dev": true,
+      "requires": {
+        "debug": "2.6.9",
+        "finalhandler": "1.1.2",
+        "parseurl": "~1.3.3",
+        "utils-merge": "1.0.1"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "2.6.9",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+          "dev": true,
+          "requires": {
+            "ms": "2.0.0"
+          }
+        },
+        "finalhandler": {
+          "version": "1.1.2",
+          "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
+          "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
+          "dev": true,
+          "requires": {
+            "debug": "2.6.9",
+            "encodeurl": "~1.0.2",
+            "escape-html": "~1.0.3",
+            "on-finished": "~2.3.0",
+            "parseurl": "~1.3.3",
+            "statuses": "~1.5.0",
+            "unpipe": "~1.0.0"
+          }
+        },
+        "ms": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+          "dev": true
+        },
+        "on-finished": {
+          "version": "2.3.0",
+          "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
+          "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
+          "dev": true,
+          "requires": {
+            "ee-first": "1.1.1"
+          }
+        },
+        "statuses": {
+          "version": "1.5.0",
+          "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
+          "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=",
+          "dev": true
+        }
+      }
+    },
+    "connect-history-api-fallback": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz",
+      "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==",
+      "dev": true
+    },
+    "console-browserify": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz",
+      "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==",
+      "dev": true
+    },
+    "console-control-strings": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
+      "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
+      "dev": true
+    },
+    "constants-browserify": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz",
+      "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=",
+      "dev": true
+    },
+    "content-disposition": {
+      "version": "0.5.4",
+      "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
+      "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
+      "dev": true,
+      "requires": {
+        "safe-buffer": "5.2.1"
+      },
+      "dependencies": {
+        "safe-buffer": {
+          "version": "5.2.1",
+          "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+          "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+          "dev": true
+        }
+      }
+    },
+    "content-type": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
+      "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==",
+      "dev": true
+    },
+    "convert-source-map": {
+      "version": "1.8.0",
+      "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz",
+      "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==",
+      "dev": true,
+      "requires": {
+        "safe-buffer": "~5.1.1"
+      }
+    },
+    "cookie": {
+      "version": "0.5.0",
+      "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
+      "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==",
+      "dev": true
+    },
+    "cookie-signature": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
+      "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=",
+      "dev": true
+    },
+    "copy-anything": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz",
+      "integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==",
+      "dev": true,
+      "requires": {
+        "is-what": "^3.14.1"
+      }
+    },
+    "copy-concurrently": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz",
+      "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==",
+      "dev": true,
+      "requires": {
+        "aproba": "^1.1.1",
+        "fs-write-stream-atomic": "^1.0.8",
+        "iferr": "^0.1.5",
+        "mkdirp": "^0.5.1",
+        "rimraf": "^2.5.4",
+        "run-queue": "^1.0.0"
+      },
+      "dependencies": {
+        "aproba": {
+          "version": "1.2.0",
+          "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
+          "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
+          "dev": true
+        },
+        "mkdirp": {
+          "version": "0.5.6",
+          "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
+          "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
+          "dev": true,
+          "requires": {
+            "minimist": "^1.2.6"
+          }
+        },
+        "rimraf": {
+          "version": "2.7.1",
+          "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+          "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+          "dev": true,
+          "requires": {
+            "glob": "^7.1.3"
+          }
+        }
+      }
+    },
+    "copy-descriptor": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
+      "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=",
+      "dev": true
+    },
+    "copy-webpack-plugin": {
+      "version": "9.0.1",
+      "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-9.0.1.tgz",
+      "integrity": "sha512-14gHKKdYIxF84jCEgPgYXCPpldbwpxxLbCmA7LReY7gvbaT555DgeBWBgBZM116tv/fO6RRJrsivBqRyRlukhw==",
+      "dev": true,
+      "requires": {
+        "fast-glob": "^3.2.5",
+        "glob-parent": "^6.0.0",
+        "globby": "^11.0.3",
+        "normalize-path": "^3.0.0",
+        "p-limit": "^3.1.0",
+        "schema-utils": "^3.0.0",
+        "serialize-javascript": "^6.0.0"
+      },
+      "dependencies": {
+        "p-limit": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+          "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+          "dev": true,
+          "requires": {
+            "yocto-queue": "^0.1.0"
+          }
+        }
+      }
+    },
+    "core-js": {
+      "version": "3.16.0",
+      "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.16.0.tgz",
+      "integrity": "sha512-5+5VxRFmSf97nM8Jr2wzOwLqRo6zphH2aX+7KsAUONObyzakDNq2G/bgbhinxB4PoV9L3aXQYhiDKyIKWd2c8g==",
+      "dev": true
+    },
+    "core-js-compat": {
+      "version": "3.22.6",
+      "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.22.6.tgz",
+      "integrity": "sha512-dQ/SxlHcuiywaPIoSUCU6Fx+Mk/H5TXENqd/ZJcK85ta0ZcQkbzHwblxPeL0hF5o+NsT2uK3q9ZOG5TboiVuWw==",
+      "dev": true,
+      "requires": {
+        "browserslist": "^4.20.3",
+        "semver": "7.0.0"
+      },
+      "dependencies": {
+        "semver": {
+          "version": "7.0.0",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz",
+          "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==",
+          "dev": true
+        }
+      }
+    },
+    "core-util-is": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
+      "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
+    },
+    "cors": {
+      "version": "2.8.5",
+      "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
+      "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
+      "dev": true,
+      "requires": {
+        "object-assign": "^4",
+        "vary": "^1"
+      }
+    },
+    "cosmiconfig": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz",
+      "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==",
+      "dev": true,
+      "requires": {
+        "@types/parse-json": "^4.0.0",
+        "import-fresh": "^3.2.1",
+        "parse-json": "^5.0.0",
+        "path-type": "^4.0.0",
+        "yaml": "^1.10.0"
+      }
+    },
+    "cp-file": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/cp-file/-/cp-file-7.0.0.tgz",
+      "integrity": "sha512-0Cbj7gyvFVApzpK/uhCtQ/9kE9UnYpxMzaq5nQQC/Dh4iaj5fxp7iEFIullrYwzj8nf0qnsI1Qsx34hAeAebvw==",
+      "dev": true,
+      "requires": {
+        "graceful-fs": "^4.1.2",
+        "make-dir": "^3.0.0",
+        "nested-error-stacks": "^2.0.0",
+        "p-event": "^4.1.0"
+      }
+    },
+    "cpy": {
+      "version": "8.1.2",
+      "resolved": "https://registry.npmjs.org/cpy/-/cpy-8.1.2.tgz",
+      "integrity": "sha512-dmC4mUesv0OYH2kNFEidtf/skUwv4zePmGeepjyyJ0qTo5+8KhA1o99oIAwVVLzQMAeDJml74d6wPPKb6EZUTg==",
+      "dev": true,
+      "requires": {
+        "arrify": "^2.0.1",
+        "cp-file": "^7.0.0",
+        "globby": "^9.2.0",
+        "has-glob": "^1.0.0",
+        "junk": "^3.1.0",
+        "nested-error-stacks": "^2.1.0",
+        "p-all": "^2.1.0",
+        "p-filter": "^2.1.0",
+        "p-map": "^3.0.0"
+      },
+      "dependencies": {
+        "@nodelib/fs.stat": {
+          "version": "1.1.3",
+          "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz",
+          "integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==",
+          "dev": true
+        },
+        "array-union": {
+          "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
+          "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==",
+          "dev": true,
+          "requires": {
+            "array-uniq": "^1.0.1"
+          }
+        },
+        "braces": {
+          "version": "2.3.2",
+          "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
+          "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
+          "dev": true,
+          "requires": {
+            "arr-flatten": "^1.1.0",
+            "array-unique": "^0.3.2",
+            "extend-shallow": "^2.0.1",
+            "fill-range": "^4.0.0",
+            "isobject": "^3.0.1",
+            "repeat-element": "^1.1.2",
+            "snapdragon": "^0.8.1",
+            "snapdragon-node": "^2.0.1",
+            "split-string": "^3.0.2",
+            "to-regex": "^3.0.1"
+          },
+          "dependencies": {
+            "extend-shallow": {
+              "version": "2.0.1",
+              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+              "dev": true,
+              "requires": {
+                "is-extendable": "^0.1.0"
+              }
+            }
+          }
+        },
+        "dir-glob": {
+          "version": "2.2.2",
+          "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.2.2.tgz",
+          "integrity": "sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==",
+          "dev": true,
+          "requires": {
+            "path-type": "^3.0.0"
+          }
+        },
+        "fast-glob": {
+          "version": "2.2.7",
+          "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.7.tgz",
+          "integrity": "sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw==",
+          "dev": true,
+          "requires": {
+            "@mrmlnc/readdir-enhanced": "^2.2.1",
+            "@nodelib/fs.stat": "^1.1.2",
+            "glob-parent": "^3.1.0",
+            "is-glob": "^4.0.0",
+            "merge2": "^1.2.3",
+            "micromatch": "^3.1.10"
+          }
+        },
+        "fill-range": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
+          "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
+          "dev": true,
+          "requires": {
+            "extend-shallow": "^2.0.1",
+            "is-number": "^3.0.0",
+            "repeat-string": "^1.6.1",
+            "to-regex-range": "^2.1.0"
+          },
+          "dependencies": {
+            "extend-shallow": {
+              "version": "2.0.1",
+              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+              "dev": true,
+              "requires": {
+                "is-extendable": "^0.1.0"
+              }
+            }
+          }
+        },
+        "glob-parent": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
+          "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=",
+          "dev": true,
+          "requires": {
+            "is-glob": "^3.1.0",
+            "path-dirname": "^1.0.0"
+          },
+          "dependencies": {
+            "is-glob": {
+              "version": "3.1.0",
+              "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
+              "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
+              "dev": true,
+              "requires": {
+                "is-extglob": "^2.1.0"
+              }
+            }
+          }
+        },
+        "globby": {
+          "version": "9.2.0",
+          "resolved": "https://registry.npmjs.org/globby/-/globby-9.2.0.tgz",
+          "integrity": "sha512-ollPHROa5mcxDEkwg6bPt3QbEf4pDQSNtd6JPL1YvOvAo/7/0VAm9TccUeoTmarjPw4pfUthSCqcyfNB1I3ZSg==",
+          "dev": true,
+          "requires": {
+            "@types/glob": "^7.1.1",
+            "array-union": "^1.0.2",
+            "dir-glob": "^2.2.2",
+            "fast-glob": "^2.2.6",
+            "glob": "^7.1.3",
+            "ignore": "^4.0.3",
+            "pify": "^4.0.1",
+            "slash": "^2.0.0"
+          }
+        },
+        "ignore": {
+          "version": "4.0.6",
+          "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
+          "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
+          "dev": true
+        },
+        "is-number": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
+          "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
+          "dev": true,
+          "requires": {
+            "kind-of": "^3.0.2"
+          },
+          "dependencies": {
+            "kind-of": {
+              "version": "3.2.2",
+              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+              "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+              "dev": true,
+              "requires": {
+                "is-buffer": "^1.1.5"
+              }
+            }
+          }
+        },
+        "micromatch": {
+          "version": "3.1.10",
+          "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
+          "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
+          "dev": true,
+          "requires": {
+            "arr-diff": "^4.0.0",
+            "array-unique": "^0.3.2",
+            "braces": "^2.3.1",
+            "define-property": "^2.0.2",
+            "extend-shallow": "^3.0.2",
+            "extglob": "^2.0.4",
+            "fragment-cache": "^0.2.1",
+            "kind-of": "^6.0.2",
+            "nanomatch": "^1.2.9",
+            "object.pick": "^1.3.0",
+            "regex-not": "^1.0.0",
+            "snapdragon": "^0.8.1",
+            "to-regex": "^3.0.2"
+          }
+        },
+        "p-map": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz",
+          "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==",
+          "dev": true,
+          "requires": {
+            "aggregate-error": "^3.0.0"
+          }
+        },
+        "path-type": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz",
+          "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==",
+          "dev": true,
+          "requires": {
+            "pify": "^3.0.0"
+          },
+          "dependencies": {
+            "pify": {
+              "version": "3.0.0",
+              "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+              "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
+              "dev": true
+            }
+          }
+        },
+        "slash": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz",
+          "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==",
+          "dev": true
+        },
+        "to-regex-range": {
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
+          "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
+          "dev": true,
+          "requires": {
+            "is-number": "^3.0.0",
+            "repeat-string": "^1.6.1"
+          }
+        }
+      }
+    },
+    "create-ecdh": {
+      "version": "4.0.4",
+      "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz",
+      "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==",
+      "dev": true,
+      "requires": {
+        "bn.js": "^4.1.0",
+        "elliptic": "^6.5.3"
+      },
+      "dependencies": {
+        "bn.js": {
+          "version": "4.12.0",
+          "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
+          "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==",
+          "dev": true
+        }
+      }
+    },
+    "create-hash": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
+      "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==",
+      "dev": true,
+      "requires": {
+        "cipher-base": "^1.0.1",
+        "inherits": "^2.0.1",
+        "md5.js": "^1.3.4",
+        "ripemd160": "^2.0.1",
+        "sha.js": "^2.4.0"
+      }
+    },
+    "create-hmac": {
+      "version": "1.1.7",
+      "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
+      "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==",
+      "dev": true,
+      "requires": {
+        "cipher-base": "^1.0.3",
+        "create-hash": "^1.1.0",
+        "inherits": "^2.0.1",
+        "ripemd160": "^2.0.0",
+        "safe-buffer": "^5.0.1",
+        "sha.js": "^2.4.8"
+      }
+    },
+    "critters": {
+      "version": "0.0.12",
+      "resolved": "https://registry.npmjs.org/critters/-/critters-0.0.12.tgz",
+      "integrity": "sha512-ujxKtKc/mWpjrOKeaACTaQ1aP0O31M0ZPWhfl85jZF1smPU4Ivb9va5Ox2poif4zVJQQo0LCFlzGtEZAsCAPcw==",
+      "dev": true,
+      "requires": {
+        "chalk": "^4.1.0",
+        "css-select": "^4.1.3",
+        "parse5": "^6.0.1",
+        "parse5-htmlparser2-tree-adapter": "^6.0.1",
+        "postcss": "^8.3.7",
+        "pretty-bytes": "^5.3.0"
+      },
+      "dependencies": {
+        "ansi-styles": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+          "dev": true,
+          "requires": {
+            "color-convert": "^2.0.1"
+          }
+        },
+        "chalk": {
+          "version": "4.1.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+          "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "dev": true,
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
+        "parse5": {
+          "version": "6.0.1",
+          "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
+          "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==",
+          "dev": true
+        },
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
+        }
+      }
+    },
+    "cross-spawn": {
+      "version": "6.0.5",
+      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
+      "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
+      "dev": true,
+      "requires": {
+        "nice-try": "^1.0.4",
+        "path-key": "^2.0.1",
+        "semver": "^5.5.0",
+        "shebang-command": "^1.2.0",
+        "which": "^1.2.9"
+      },
+      "dependencies": {
+        "semver": {
+          "version": "5.7.1",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+          "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+          "dev": true
+        }
+      }
+    },
+    "crypto-browserify": {
+      "version": "3.12.0",
+      "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz",
+      "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==",
+      "dev": true,
+      "requires": {
+        "browserify-cipher": "^1.0.0",
+        "browserify-sign": "^4.0.0",
+        "create-ecdh": "^4.0.0",
+        "create-hash": "^1.1.0",
+        "create-hmac": "^1.1.0",
+        "diffie-hellman": "^5.0.0",
+        "inherits": "^2.0.1",
+        "pbkdf2": "^3.0.3",
+        "public-encrypt": "^4.0.0",
+        "randombytes": "^2.0.0",
+        "randomfill": "^1.0.3"
+      }
+    },
+    "crypto-js": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz",
+      "integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==",
+      "dev": true
+    },
+    "css": {
+      "version": "2.2.4",
+      "resolved": "https://registry.npmjs.org/css/-/css-2.2.4.tgz",
+      "integrity": "sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==",
+      "dev": true,
+      "requires": {
+        "inherits": "^2.0.3",
+        "source-map": "^0.6.1",
+        "source-map-resolve": "^0.5.2",
+        "urix": "^0.1.0"
+      },
+      "dependencies": {
+        "source-map": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+          "dev": true
+        }
+      }
+    },
+    "css-blank-pseudo": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-0.1.4.tgz",
+      "integrity": "sha512-LHz35Hr83dnFeipc7oqFDmsjHdljj3TQtxGGiNWSOsTLIAubSm4TEz8qCaKFpk7idaQ1GfWscF4E6mgpBysA1w==",
+      "dev": true,
+      "requires": {
+        "postcss": "^7.0.5"
+      },
+      "dependencies": {
+        "picocolors": {
+          "version": "0.2.1",
+          "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz",
+          "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==",
+          "dev": true
+        },
+        "postcss": {
+          "version": "7.0.39",
+          "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz",
+          "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==",
+          "dev": true,
+          "requires": {
+            "picocolors": "^0.2.1",
+            "source-map": "^0.6.1"
+          }
+        },
+        "source-map": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+          "dev": true
+        }
+      }
+    },
+    "css-declaration-sorter": {
+      "version": "6.2.2",
+      "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.2.2.tgz",
+      "integrity": "sha512-Ufadglr88ZLsrvS11gjeu/40Lw74D9Am/Jpr3LlYm5Q4ZP5KdlUhG+6u2EjyXeZcxmZ2h1ebCKngDjolpeLHpg==",
+      "dev": true
+    },
+    "css-has-pseudo": {
+      "version": "0.10.0",
+      "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-0.10.0.tgz",
+      "integrity": "sha512-Z8hnfsZu4o/kt+AuFzeGpLVhFOGO9mluyHBaA2bA8aCGTwah5sT3WV/fTHH8UNZUytOIImuGPrl/prlb4oX4qQ==",
+      "dev": true,
+      "requires": {
+        "postcss": "^7.0.6",
+        "postcss-selector-parser": "^5.0.0-rc.4"
+      },
+      "dependencies": {
+        "cssesc": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz",
+          "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==",
+          "dev": true
+        },
+        "picocolors": {
+          "version": "0.2.1",
+          "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz",
+          "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==",
+          "dev": true
+        },
+        "postcss": {
+          "version": "7.0.39",
+          "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz",
+          "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==",
+          "dev": true,
+          "requires": {
+            "picocolors": "^0.2.1",
+            "source-map": "^0.6.1"
+          }
+        },
+        "postcss-selector-parser": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz",
+          "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==",
+          "dev": true,
+          "requires": {
+            "cssesc": "^2.0.0",
+            "indexes-of": "^1.0.1",
+            "uniq": "^1.0.1"
+          }
+        },
+        "source-map": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+          "dev": true
+        }
+      }
+    },
+    "css-loader": {
+      "version": "6.2.0",
+      "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.2.0.tgz",
+      "integrity": "sha512-/rvHfYRjIpymZblf49w8jYcRo2y9gj6rV8UroHGmBxKrIyGLokpycyKzp9OkitvqT29ZSpzJ0Ic7SpnJX3sC8g==",
+      "dev": true,
+      "requires": {
+        "icss-utils": "^5.1.0",
+        "postcss": "^8.2.15",
+        "postcss-modules-extract-imports": "^3.0.0",
+        "postcss-modules-local-by-default": "^4.0.0",
+        "postcss-modules-scope": "^3.0.0",
+        "postcss-modules-values": "^4.0.0",
+        "postcss-value-parser": "^4.1.0",
+        "semver": "^7.3.5"
+      }
+    },
+    "css-minimizer-webpack-plugin": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-3.0.2.tgz",
+      "integrity": "sha512-B3I5e17RwvKPJwsxjjWcdgpU/zqylzK1bPVghcmpFHRL48DXiBgrtqz1BJsn68+t/zzaLp9kYAaEDvQ7GyanFQ==",
+      "dev": true,
+      "requires": {
+        "cssnano": "^5.0.6",
+        "jest-worker": "^27.0.2",
+        "p-limit": "^3.0.2",
+        "postcss": "^8.3.5",
+        "schema-utils": "^3.0.0",
+        "serialize-javascript": "^6.0.0",
+        "source-map": "^0.6.1"
+      },
+      "dependencies": {
+        "p-limit": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+          "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+          "dev": true,
+          "requires": {
+            "yocto-queue": "^0.1.0"
+          }
+        },
+        "source-map": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+          "dev": true
+        }
+      }
+    },
+    "css-parse": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/css-parse/-/css-parse-2.0.0.tgz",
+      "integrity": "sha1-pGjuZnwW2BzPBcWMONKpfHgNv9Q=",
+      "dev": true,
+      "requires": {
+        "css": "^2.0.0"
+      }
+    },
+    "css-prefers-color-scheme": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-3.1.1.tgz",
+      "integrity": "sha512-MTu6+tMs9S3EUqzmqLXEcgNRbNkkD/TGFvowpeoWJn5Vfq7FMgsmRQs9X5NXAURiOBmOxm/lLjsDNXDE6k9bhg==",
+      "dev": true,
+      "requires": {
+        "postcss": "^7.0.5"
+      },
+      "dependencies": {
+        "picocolors": {
+          "version": "0.2.1",
+          "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz",
+          "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==",
+          "dev": true
+        },
+        "postcss": {
+          "version": "7.0.39",
+          "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz",
+          "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==",
+          "dev": true,
+          "requires": {
+            "picocolors": "^0.2.1",
+            "source-map": "^0.6.1"
+          }
+        },
+        "source-map": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+          "dev": true
+        }
+      }
+    },
+    "css-select": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz",
+      "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==",
+      "dev": true,
+      "requires": {
+        "boolbase": "^1.0.0",
+        "css-what": "^6.0.1",
+        "domhandler": "^4.3.1",
+        "domutils": "^2.8.0",
+        "nth-check": "^2.0.1"
+      }
+    },
+    "css-tree": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz",
+      "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==",
+      "dev": true,
+      "requires": {
+        "mdn-data": "2.0.14",
+        "source-map": "^0.6.1"
+      },
+      "dependencies": {
+        "source-map": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+          "dev": true
+        }
+      }
+    },
+    "css-what": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz",
+      "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==",
+      "dev": true
+    },
+    "cssdb": {
+      "version": "4.4.0",
+      "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-4.4.0.tgz",
+      "integrity": "sha512-LsTAR1JPEM9TpGhl/0p3nQecC2LJ0kD8X5YARu1hk/9I1gril5vDtMZyNxcEpxxDj34YNck/ucjuoUd66K03oQ==",
+      "dev": true
+    },
+    "cssesc": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
+      "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
+      "dev": true
+    },
+    "cssnano": {
+      "version": "5.1.9",
+      "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.1.9.tgz",
+      "integrity": "sha512-hctQHIIeDrfMjq0bQhoVmRVaSeNNOGxkvkKVOcKpJzLr09wlRrZWH4GaYudp0aszpW8wJeaO5/yBmID9n7DNCg==",
+      "dev": true,
+      "requires": {
+        "cssnano-preset-default": "^5.2.9",
+        "lilconfig": "^2.0.3",
+        "yaml": "^1.10.2"
+      }
+    },
+    "cssnano-preset-default": {
+      "version": "5.2.9",
+      "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.2.9.tgz",
+      "integrity": "sha512-/4qcQcAfFEg+gnXE5NxKmYJ9JcT+8S5SDuJCLYMDN8sM/ymZ+lgLXq5+ohx/7V2brUCkgW2OaoCzOdAN0zvhGw==",
+      "dev": true,
+      "requires": {
+        "css-declaration-sorter": "^6.2.2",
+        "cssnano-utils": "^3.1.0",
+        "postcss-calc": "^8.2.3",
+        "postcss-colormin": "^5.3.0",
+        "postcss-convert-values": "^5.1.1",
+        "postcss-discard-comments": "^5.1.1",
+        "postcss-discard-duplicates": "^5.1.0",
+        "postcss-discard-empty": "^5.1.1",
+        "postcss-discard-overridden": "^5.1.0",
+        "postcss-merge-longhand": "^5.1.5",
+        "postcss-merge-rules": "^5.1.1",
+        "postcss-minify-font-values": "^5.1.0",
+        "postcss-minify-gradients": "^5.1.1",
+        "postcss-minify-params": "^5.1.3",
+        "postcss-minify-selectors": "^5.2.0",
+        "postcss-normalize-charset": "^5.1.0",
+        "postcss-normalize-display-values": "^5.1.0",
+        "postcss-normalize-positions": "^5.1.0",
+        "postcss-normalize-repeat-style": "^5.1.0",
+        "postcss-normalize-string": "^5.1.0",
+        "postcss-normalize-timing-functions": "^5.1.0",
+        "postcss-normalize-unicode": "^5.1.0",
+        "postcss-normalize-url": "^5.1.0",
+        "postcss-normalize-whitespace": "^5.1.1",
+        "postcss-ordered-values": "^5.1.1",
+        "postcss-reduce-initial": "^5.1.0",
+        "postcss-reduce-transforms": "^5.1.0",
+        "postcss-svgo": "^5.1.0",
+        "postcss-unique-selectors": "^5.1.1"
+      }
+    },
+    "cssnano-utils": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.1.0.tgz",
+      "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==",
+      "dev": true
+    },
+    "csso": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz",
+      "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==",
+      "dev": true,
+      "requires": {
+        "css-tree": "^1.1.2"
+      }
+    },
+    "csstype": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz",
+      "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA==",
+      "dev": true
+    },
+    "currently-unhandled": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz",
+      "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=",
+      "dev": true,
+      "optional": true,
+      "requires": {
+        "array-find-index": "^1.0.1"
+      }
+    },
+    "custom-event": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz",
+      "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=",
+      "dev": true
+    },
+    "cyclist": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz",
+      "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=",
+      "dev": true
+    },
+    "d": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
+      "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
+      "dev": true,
+      "requires": {
+        "es5-ext": "^0.10.50",
+        "type": "^1.0.1"
+      }
+    },
+    "dash-ast": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/dash-ast/-/dash-ast-2.0.1.tgz",
+      "integrity": "sha512-5TXltWJGc+RdnabUGzhRae1TRq6m4gr+3K2wQX0is5/F2yS6MJXJvLyI3ErAnsAXuJoGqvfVD5icRgim07DrxQ==",
+      "dev": true
+    },
+    "date-format": {
+      "version": "4.0.10",
+      "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.10.tgz",
+      "integrity": "sha512-RuMIHocrVjF84bUSTcd1uokIsLsOsk1Awb7TexNOI3f48ukCu39mjslWquDTA08VaDMF2umr3MB9ow5EyJTWyA==",
+      "dev": true
+    },
+    "debug": {
+      "version": "4.3.4",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+      "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+      "dev": true,
+      "requires": {
+        "ms": "2.1.2"
+      }
+    },
+    "decache": {
+      "version": "4.6.1",
+      "resolved": "https://registry.npmjs.org/decache/-/decache-4.6.1.tgz",
+      "integrity": "sha512-ohApBM8u9ygepJCjgBrEZSSxPjc0T/PJkD+uNyxXPkqudyUpdXpwJYp0VISm2WrPVzASU6DZyIi6BWdyw7uJ2Q==",
+      "dev": true,
+      "requires": {
+        "callsite": "^1.0.0"
+      }
+    },
+    "decamelize": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+      "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
+      "dev": true
+    },
+    "decode-uri-component": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
+      "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=",
+      "dev": true
+    },
+    "deep-equal": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz",
+      "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==",
+      "dev": true,
+      "requires": {
+        "is-arguments": "^1.0.4",
+        "is-date-object": "^1.0.1",
+        "is-regex": "^1.0.4",
+        "object-is": "^1.0.1",
+        "object-keys": "^1.1.1",
+        "regexp.prototype.flags": "^1.2.0"
+      }
+    },
+    "deep-is": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+      "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+      "dev": true
+    },
+    "deepmerge": {
+      "version": "4.2.2",
+      "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz",
+      "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==",
+      "dev": true
+    },
+    "default-browser-id": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-1.0.4.tgz",
+      "integrity": "sha1-5Z0JpdFXuCi4dsJoFuYcPSosIDo=",
+      "dev": true,
+      "optional": true,
+      "requires": {
+        "bplist-parser": "^0.1.0",
+        "meow": "^3.1.0",
+        "untildify": "^2.0.0"
+      }
+    },
+    "default-gateway": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz",
+      "integrity": "sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA==",
+      "dev": true,
+      "requires": {
+        "execa": "^1.0.0",
+        "ip-regex": "^2.1.0"
+      }
+    },
+    "defaults": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz",
+      "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=",
+      "dev": true,
+      "requires": {
+        "clone": "^1.0.2"
+      }
+    },
+    "define-lazy-prop": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz",
+      "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==",
+      "dev": true
+    },
+    "define-properties": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz",
+      "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==",
+      "dev": true,
+      "requires": {
+        "has-property-descriptors": "^1.0.0",
+        "object-keys": "^1.1.1"
+      }
+    },
+    "define-property": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz",
+      "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==",
+      "dev": true,
+      "requires": {
+        "is-descriptor": "^1.0.2",
+        "isobject": "^3.0.1"
+      },
+      "dependencies": {
+        "is-accessor-descriptor": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+          "dev": true,
+          "requires": {
+            "kind-of": "^6.0.0"
+          }
+        },
+        "is-data-descriptor": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+          "dev": true,
+          "requires": {
+            "kind-of": "^6.0.0"
+          }
+        },
+        "is-descriptor": {
+          "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+          "dev": true,
+          "requires": {
+            "is-accessor-descriptor": "^1.0.0",
+            "is-data-descriptor": "^1.0.0",
+            "kind-of": "^6.0.2"
+          }
+        }
+      }
+    },
+    "del": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz",
+      "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==",
+      "dev": true,
+      "requires": {
+        "@types/glob": "^7.1.1",
+        "globby": "^6.1.0",
+        "is-path-cwd": "^2.0.0",
+        "is-path-in-cwd": "^2.0.0",
+        "p-map": "^2.0.0",
+        "pify": "^4.0.1",
+        "rimraf": "^2.6.3"
+      },
+      "dependencies": {
+        "array-union": {
+          "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
+          "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==",
+          "dev": true,
+          "requires": {
+            "array-uniq": "^1.0.1"
+          }
+        },
+        "globby": {
+          "version": "6.1.0",
+          "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz",
+          "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=",
+          "dev": true,
+          "requires": {
+            "array-union": "^1.0.1",
+            "glob": "^7.0.3",
+            "object-assign": "^4.0.1",
+            "pify": "^2.0.0",
+            "pinkie-promise": "^2.0.0"
+          },
+          "dependencies": {
+            "pify": {
+              "version": "2.3.0",
+              "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+              "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+              "dev": true
+            }
+          }
+        },
+        "p-map": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz",
+          "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==",
+          "dev": true
+        },
+        "rimraf": {
+          "version": "2.7.1",
+          "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+          "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+          "dev": true,
+          "requires": {
+            "glob": "^7.1.3"
+          }
+        }
+      }
+    },
+    "delayed-stream": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+      "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
+      "dev": true
+    },
+    "delegates": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
+      "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=",
+      "dev": true
+    },
+    "depd": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+      "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+      "dev": true
+    },
+    "dependency-graph": {
+      "version": "0.11.0",
+      "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz",
+      "integrity": "sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==",
+      "dev": true
+    },
+    "des.js": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz",
+      "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==",
+      "dev": true,
+      "requires": {
+        "inherits": "^2.0.1",
+        "minimalistic-assert": "^1.0.0"
+      }
+    },
+    "destroy": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
+      "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
+      "dev": true
+    },
+    "detab": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/detab/-/detab-2.0.4.tgz",
+      "integrity": "sha512-8zdsQA5bIkoRECvCrNKPla84lyoR7DSAyf7p0YgXzBO9PDJx8KntPUay7NS6yp+KdxdVtiE5SpHKtbp2ZQyA9g==",
+      "dev": true,
+      "requires": {
+        "repeat-string": "^1.5.4"
+      }
+    },
+    "detect-node": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz",
+      "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==",
+      "dev": true
+    },
+    "detect-package-manager": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/detect-package-manager/-/detect-package-manager-2.0.1.tgz",
+      "integrity": "sha512-j/lJHyoLlWi6G1LDdLgvUtz60Zo5GEj+sVYtTVXnYLDPuzgC3llMxonXym9zIwhhUII8vjdw0LXxavpLqTbl1A==",
+      "dev": true,
+      "requires": {
+        "execa": "^5.1.1"
+      },
+      "dependencies": {
+        "cross-spawn": {
+          "version": "7.0.3",
+          "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
+          "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+          "dev": true,
+          "requires": {
+            "path-key": "^3.1.0",
+            "shebang-command": "^2.0.0",
+            "which": "^2.0.1"
+          }
+        },
+        "execa": {
+          "version": "5.1.1",
+          "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
+          "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
+          "dev": true,
+          "requires": {
+            "cross-spawn": "^7.0.3",
+            "get-stream": "^6.0.0",
+            "human-signals": "^2.1.0",
+            "is-stream": "^2.0.0",
+            "merge-stream": "^2.0.0",
+            "npm-run-path": "^4.0.1",
+            "onetime": "^5.1.2",
+            "signal-exit": "^3.0.3",
+            "strip-final-newline": "^2.0.0"
+          }
+        },
+        "get-stream": {
+          "version": "6.0.1",
+          "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
+          "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
+          "dev": true
+        },
+        "human-signals": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
+          "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
+          "dev": true
+        },
+        "is-stream": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
+          "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
+          "dev": true
+        },
+        "npm-run-path": {
+          "version": "4.0.1",
+          "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
+          "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
+          "dev": true,
+          "requires": {
+            "path-key": "^3.0.0"
+          }
+        },
+        "path-key": {
+          "version": "3.1.1",
+          "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+          "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+          "dev": true
+        },
+        "shebang-command": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+          "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+          "dev": true,
+          "requires": {
+            "shebang-regex": "^3.0.0"
+          }
+        },
+        "shebang-regex": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+          "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+          "dev": true
+        },
+        "which": {
+          "version": "2.0.2",
+          "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+          "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+          "dev": true,
+          "requires": {
+            "isexe": "^2.0.0"
+          }
+        }
+      }
+    },
+    "detect-port": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/detect-port/-/detect-port-1.3.0.tgz",
+      "integrity": "sha512-E+B1gzkl2gqxt1IhUzwjrxBKRqx1UzC3WLONHinn8S3T6lwV/agVCyitiFOsGJ/eYuEUBvD71MZHy3Pv1G9doQ==",
+      "dev": true,
+      "requires": {
+        "address": "^1.0.1",
+        "debug": "^2.6.0"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "2.6.9",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+          "dev": true,
+          "requires": {
+            "ms": "2.0.0"
+          }
+        },
+        "ms": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+          "dev": true
+        }
+      }
+    },
+    "dfa": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/dfa/-/dfa-1.2.0.tgz",
+      "integrity": "sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q==",
+      "dev": true
+    },
+    "di": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz",
+      "integrity": "sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw=",
+      "dev": true
+    },
+    "diffie-hellman": {
+      "version": "5.0.3",
+      "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz",
+      "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==",
+      "dev": true,
+      "requires": {
+        "bn.js": "^4.1.0",
+        "miller-rabin": "^4.0.0",
+        "randombytes": "^2.0.0"
+      },
+      "dependencies": {
+        "bn.js": {
+          "version": "4.12.0",
+          "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
+          "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==",
+          "dev": true
+        }
+      }
+    },
+    "dir-glob": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
+      "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
+      "dev": true,
+      "requires": {
+        "path-type": "^4.0.0"
+      }
+    },
+    "dns-equal": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz",
+      "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=",
+      "dev": true
+    },
+    "dns-packet": {
+      "version": "1.3.4",
+      "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.4.tgz",
+      "integrity": "sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==",
+      "dev": true,
+      "requires": {
+        "ip": "^1.1.0",
+        "safe-buffer": "^5.0.1"
+      }
+    },
+    "dns-txt": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz",
+      "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=",
+      "dev": true,
+      "requires": {
+        "buffer-indexof": "^1.0.0"
+      }
+    },
+    "doctrine": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
+      "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+      "dev": true,
+      "requires": {
+        "esutils": "^2.0.2"
+      }
+    },
+    "dom-accessibility-api": {
+      "version": "0.5.14",
+      "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.14.tgz",
+      "integrity": "sha512-NMt+m9zFMPZe0JcY9gN224Qvk6qLIdqex29clBvc/y75ZBX9YA9wNK3frsYvu2DI1xcCIwxwnX+TlsJ2DSOADg==",
+      "dev": true
+    },
+    "dom-converter": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz",
+      "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==",
+      "dev": true,
+      "requires": {
+        "utila": "~0.4"
+      }
+    },
+    "dom-serialize": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz",
+      "integrity": "sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs=",
+      "dev": true,
+      "requires": {
+        "custom-event": "~1.0.0",
+        "ent": "~2.2.0",
+        "extend": "^3.0.0",
+        "void-elements": "^2.0.0"
+      }
+    },
+    "dom-serializer": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
+      "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==",
+      "dev": true,
+      "requires": {
+        "domelementtype": "^2.0.1",
+        "domhandler": "^4.2.0",
+        "entities": "^2.0.0"
+      }
+    },
+    "dom-walk": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz",
+      "integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==",
+      "dev": true
+    },
+    "domain-browser": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz",
+      "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==",
+      "dev": true
+    },
+    "domelementtype": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
+      "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
+      "dev": true
+    },
+    "domhandler": {
+      "version": "4.3.1",
+      "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz",
+      "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==",
+      "dev": true,
+      "requires": {
+        "domelementtype": "^2.2.0"
+      }
+    },
+    "dommatrix": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/dommatrix/-/dommatrix-1.0.3.tgz",
+      "integrity": "sha512-l32Xp/TLgWb8ReqbVJAFIvXmY7go4nTxxlWiAFyhoQw9RKEOHBZNnyGvJWqDVSPmq3Y9HlM4npqF/T6VMOXhww==",
+      "dev": true
+    },
+    "domutils": {
+      "version": "2.8.0",
+      "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
+      "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
+      "dev": true,
+      "requires": {
+        "dom-serializer": "^1.0.1",
+        "domelementtype": "^2.2.0",
+        "domhandler": "^4.2.0"
+      }
+    },
+    "dot": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/dot/-/dot-1.1.3.tgz",
+      "integrity": "sha512-/nt74Rm+PcfnirXGEdhZleTwGC2LMnuKTeeTIlI82xb5loBBoXNYzr2ezCroPSMtilK8EZIfcNZwOcHN+ib1Lg==",
+      "dev": true
+    },
+    "dot-case": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz",
+      "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==",
+      "dev": true,
+      "requires": {
+        "no-case": "^3.0.4",
+        "tslib": "^2.0.3"
+      }
+    },
+    "dotenv": {
+      "version": "8.6.0",
+      "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz",
+      "integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==",
+      "dev": true
+    },
+    "dotenv-expand": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz",
+      "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==",
+      "dev": true
+    },
+    "duplexer": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz",
+      "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==",
+      "dev": true
+    },
+    "duplexer2": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz",
+      "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=",
+      "dev": true,
+      "requires": {
+        "readable-stream": "^2.0.2"
+      }
+    },
+    "duplexify": {
+      "version": "3.7.1",
+      "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz",
+      "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==",
+      "dev": true,
+      "requires": {
+        "end-of-stream": "^1.0.0",
+        "inherits": "^2.0.1",
+        "readable-stream": "^2.0.0",
+        "stream-shift": "^1.0.0"
+      }
+    },
+    "ee-first": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+      "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=",
+      "dev": true
+    },
+    "electron-to-chromium": {
+      "version": "1.4.137",
+      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.137.tgz",
+      "integrity": "sha512-0Rcpald12O11BUogJagX3HsCN3FE83DSqWjgXoHo5a72KUKMSfI39XBgJpgNNxS9fuGzytaFjE06kZkiVFy2qA==",
+      "dev": true
+    },
+    "elliptic": {
+      "version": "6.5.4",
+      "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz",
+      "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==",
+      "dev": true,
+      "requires": {
+        "bn.js": "^4.11.9",
+        "brorand": "^1.1.0",
+        "hash.js": "^1.0.0",
+        "hmac-drbg": "^1.0.1",
+        "inherits": "^2.0.4",
+        "minimalistic-assert": "^1.0.1",
+        "minimalistic-crypto-utils": "^1.0.1"
+      },
+      "dependencies": {
+        "bn.js": {
+          "version": "4.12.0",
+          "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
+          "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==",
+          "dev": true
+        }
+      }
+    },
+    "emoji-regex": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+      "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+      "dev": true
+    },
+    "emojis-list": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz",
+      "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q=="
+    },
+    "encodeurl": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
+      "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=",
+      "dev": true
+    },
+    "encoding": {
+      "version": "0.1.13",
+      "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz",
+      "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==",
+      "dev": true,
+      "optional": true,
+      "requires": {
+        "iconv-lite": "^0.6.2"
+      },
+      "dependencies": {
+        "iconv-lite": {
+          "version": "0.6.3",
+          "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+          "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "safer-buffer": ">= 2.1.2 < 3.0.0"
+          }
+        }
+      }
+    },
+    "end-of-stream": {
+      "version": "1.4.4",
+      "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
+      "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
+      "dev": true,
+      "requires": {
+        "once": "^1.4.0"
+      }
+    },
+    "engine.io": {
+      "version": "6.2.0",
+      "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.2.0.tgz",
+      "integrity": "sha512-4KzwW3F3bk+KlzSOY57fj/Jx6LyRQ1nbcyIadehl+AnXjKT7gDO0ORdRi/84ixvMKTym6ZKuxvbzN62HDDU1Lg==",
+      "dev": true,
+      "requires": {
+        "@types/cookie": "^0.4.1",
+        "@types/cors": "^2.8.12",
+        "@types/node": ">=10.0.0",
+        "accepts": "~1.3.4",
+        "base64id": "2.0.0",
+        "cookie": "~0.4.1",
+        "cors": "~2.8.5",
+        "debug": "~4.3.1",
+        "engine.io-parser": "~5.0.3",
+        "ws": "~8.2.3"
+      },
+      "dependencies": {
+        "cookie": {
+          "version": "0.4.2",
+          "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
+          "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==",
+          "dev": true
+        },
+        "ws": {
+          "version": "8.2.3",
+          "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz",
+          "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==",
+          "dev": true
+        }
+      }
+    },
+    "engine.io-parser": {
+      "version": "5.0.4",
+      "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.4.tgz",
+      "integrity": "sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg==",
+      "dev": true
+    },
+    "enhanced-resolve": {
+      "version": "5.9.3",
+      "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.3.tgz",
+      "integrity": "sha512-Bq9VSor+kjvW3f9/MiiR4eE3XYgOl7/rS8lnSxbRbF3kS0B2r+Y9w5krBWxZgDxASVZbdYrn5wT4j/Wb0J9qow==",
+      "dev": true,
+      "requires": {
+        "graceful-fs": "^4.2.4",
+        "tapable": "^2.2.0"
+      }
+    },
+    "enquirer": {
+      "version": "2.3.6",
+      "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz",
+      "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==",
+      "dev": true,
+      "requires": {
+        "ansi-colors": "^4.1.1"
+      }
+    },
+    "ent": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz",
+      "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=",
+      "dev": true
+    },
+    "entities": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
+      "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
+      "dev": true
+    },
+    "env-paths": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
+      "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
+      "dev": true
+    },
+    "err-code": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz",
+      "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==",
+      "dev": true
+    },
+    "errno": {
+      "version": "0.1.8",
+      "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz",
+      "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==",
+      "dev": true,
+      "requires": {
+        "prr": "~1.0.1"
+      }
+    },
+    "error-ex": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+      "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+      "dev": true,
+      "requires": {
+        "is-arrayish": "^0.2.1"
+      }
+    },
+    "es-abstract": {
+      "version": "1.20.1",
+      "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.1.tgz",
+      "integrity": "sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA==",
+      "dev": true,
+      "requires": {
+        "call-bind": "^1.0.2",
+        "es-to-primitive": "^1.2.1",
+        "function-bind": "^1.1.1",
+        "function.prototype.name": "^1.1.5",
+        "get-intrinsic": "^1.1.1",
+        "get-symbol-description": "^1.0.0",
+        "has": "^1.0.3",
+        "has-property-descriptors": "^1.0.0",
+        "has-symbols": "^1.0.3",
+        "internal-slot": "^1.0.3",
+        "is-callable": "^1.2.4",
+        "is-negative-zero": "^2.0.2",
+        "is-regex": "^1.1.4",
+        "is-shared-array-buffer": "^1.0.2",
+        "is-string": "^1.0.7",
+        "is-weakref": "^1.0.2",
+        "object-inspect": "^1.12.0",
+        "object-keys": "^1.1.1",
+        "object.assign": "^4.1.2",
+        "regexp.prototype.flags": "^1.4.3",
+        "string.prototype.trimend": "^1.0.5",
+        "string.prototype.trimstart": "^1.0.5",
+        "unbox-primitive": "^1.0.2"
+      }
+    },
+    "es-array-method-boxes-properly": {
       "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
-      "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=",
+      "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz",
+      "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==",
+      "dev": true
+    },
+    "es-get-iterator": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.2.tgz",
+      "integrity": "sha512-+DTO8GYwbMCwbywjimwZMHp8AuYXOS2JZFWoi2AlPOS3ebnII9w/NLpNZtA7A0YLaVDw+O7KFCeoIV7OPvM7hQ==",
+      "dev": true,
+      "requires": {
+        "call-bind": "^1.0.2",
+        "get-intrinsic": "^1.1.0",
+        "has-symbols": "^1.0.1",
+        "is-arguments": "^1.1.0",
+        "is-map": "^2.0.2",
+        "is-set": "^2.0.2",
+        "is-string": "^1.0.5",
+        "isarray": "^2.0.5"
+      },
+      "dependencies": {
+        "isarray": {
+          "version": "2.0.5",
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
+          "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
+          "dev": true
+        }
+      }
+    },
+    "es-module-lexer": {
+      "version": "0.7.1",
+      "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.7.1.tgz",
+      "integrity": "sha512-MgtWFl5No+4S3TmhDmCz2ObFGm6lEpTnzbQi+Dd+pw4mlTIZTmM2iAs5gRlmx5zS9luzobCSBSI90JM/1/JgOw==",
+      "dev": true
+    },
+    "es-shim-unscopables": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz",
+      "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==",
+      "dev": true,
+      "requires": {
+        "has": "^1.0.3"
+      }
+    },
+    "es-to-primitive": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
+      "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
+      "dev": true,
+      "requires": {
+        "is-callable": "^1.1.4",
+        "is-date-object": "^1.0.1",
+        "is-symbol": "^1.0.2"
+      }
+    },
+    "es5-ext": {
+      "version": "0.10.61",
+      "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.61.tgz",
+      "integrity": "sha512-yFhIqQAzu2Ca2I4SE2Au3rxVfmohU9Y7wqGR+s7+H7krk26NXhIRAZDgqd6xqjCEFUomDEA3/Bo/7fKmIkW1kA==",
+      "dev": true,
+      "requires": {
+        "es6-iterator": "^2.0.3",
+        "es6-symbol": "^3.1.3",
+        "next-tick": "^1.1.0"
+      }
+    },
+    "es5-shim": {
+      "version": "4.6.7",
+      "resolved": "https://registry.npmjs.org/es5-shim/-/es5-shim-4.6.7.tgz",
+      "integrity": "sha512-jg21/dmlrNQI7JyyA2w7n+yifSxBng0ZralnSfVZjoCawgNTCnS+yBCyVM9DL5itm7SUnDGgv7hcq2XCZX4iRQ==",
+      "dev": true
+    },
+    "es6-iterator": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
+      "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=",
+      "dev": true,
+      "requires": {
+        "d": "1",
+        "es5-ext": "^0.10.35",
+        "es6-symbol": "^3.1.1"
+      }
+    },
+    "es6-map": {
+      "version": "0.1.5",
+      "resolved": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz",
+      "integrity": "sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=",
+      "dev": true,
+      "requires": {
+        "d": "1",
+        "es5-ext": "~0.10.14",
+        "es6-iterator": "~2.0.1",
+        "es6-set": "~0.1.5",
+        "es6-symbol": "~3.1.1",
+        "event-emitter": "~0.3.5"
+      }
+    },
+    "es6-set": {
+      "version": "0.1.5",
+      "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz",
+      "integrity": "sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=",
+      "dev": true,
+      "requires": {
+        "d": "1",
+        "es5-ext": "~0.10.14",
+        "es6-iterator": "~2.0.1",
+        "es6-symbol": "3.1.1",
+        "event-emitter": "~0.3.5"
+      },
+      "dependencies": {
+        "es6-symbol": {
+          "version": "3.1.1",
+          "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz",
+          "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=",
+          "dev": true,
+          "requires": {
+            "d": "1",
+            "es5-ext": "~0.10.14"
+          }
+        }
+      }
+    },
+    "es6-shim": {
+      "version": "0.35.6",
+      "resolved": "https://registry.npmjs.org/es6-shim/-/es6-shim-0.35.6.tgz",
+      "integrity": "sha512-EmTr31wppcaIAgblChZiuN/l9Y7DPyw8Xtbg7fIVngn6zMW+IEBJDJngeKC3x6wr0V/vcA2wqeFnaw1bFJbDdA==",
       "dev": true
     },
-    "bootstrap": {
-      "version": "4.6.1",
-      "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.6.1.tgz",
-      "integrity": "sha512-0dj+VgI9Ecom+rvvpNZ4MUZJz8dcX7WCX+eTID9+/8HgOkv3dsRzi8BGeZJCQU6flWQVYxwTQnEZFrmJSEO7og=="
+    "es6-symbol": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz",
+      "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==",
+      "dev": true,
+      "requires": {
+        "d": "^1.0.1",
+        "ext": "^1.1.2"
+      }
+    },
+    "esbuild-android-arm64": {
+      "version": "0.13.8",
+      "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.13.8.tgz",
+      "integrity": "sha512-AilbChndywpk7CdKkNSZ9klxl+9MboLctXd9LwLo3b0dawmOF/i/t2U5d8LM6SbT1Xw36F8yngSUPrd8yPs2RA==",
+      "dev": true,
+      "optional": true
+    },
+    "esbuild-darwin-64": {
+      "version": "0.13.8",
+      "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.13.8.tgz",
+      "integrity": "sha512-b6sdiT84zV5LVaoF+UoMVGJzR/iE2vNUfUDfFQGrm4LBwM/PWXweKpuu6RD9mcyCq18cLxkP6w/LD/w9DtX3ng==",
+      "dev": true,
+      "optional": true
+    },
+    "esbuild-darwin-arm64": {
+      "version": "0.13.8",
+      "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.13.8.tgz",
+      "integrity": "sha512-R8YuPiiJayuJJRUBG4H0VwkEKo6AvhJs2m7Tl0JaIer3u1FHHXwGhMxjJDmK+kXwTFPriSysPvcobXC/UrrZCQ==",
+      "dev": true,
+      "optional": true
     },
-    "brace-expansion": {
-      "version": "1.1.11",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
-      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+    "esbuild-freebsd-64": {
+      "version": "0.13.8",
+      "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.13.8.tgz",
+      "integrity": "sha512-zBn6urrn8FnKC+YSgDxdof9jhPCeU8kR/qaamlV4gI8R3KUaUK162WYM7UyFVAlj9N0MyD3AtB+hltzu4cysTw==",
       "dev": true,
-      "requires": {
-        "balanced-match": "^1.0.0",
-        "concat-map": "0.0.1"
-      }
+      "optional": true
     },
-    "braces": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
-      "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+    "esbuild-freebsd-arm64": {
+      "version": "0.13.8",
+      "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.13.8.tgz",
+      "integrity": "sha512-pWW2slN7lGlkx0MOEBoUGwRX5UgSCLq3dy2c8RIOpiHtA87xAUpDBvZK10MykbT+aMfXc0NI2lu1X+6kI34xng==",
       "dev": true,
-      "requires": {
-        "fill-range": "^7.0.1"
-      }
+      "optional": true
     },
-    "browserslist": {
-      "version": "4.18.1",
-      "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.18.1.tgz",
-      "integrity": "sha512-8ScCzdpPwR2wQh8IT82CA2VgDwjHyqMovPBZSNH54+tm4Jk2pCuv90gmAdH6J84OCRWi0b4gMe6O6XPXuJnjgQ==",
+    "esbuild-linux-32": {
+      "version": "0.13.8",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.13.8.tgz",
+      "integrity": "sha512-T0I0ueeKVO/Is0CAeSEOG9s2jeNNb8jrrMwG9QBIm3UU18MRB60ERgkS2uV3fZ1vP2F8i3Z2e3Zju4lg9dhVmw==",
       "dev": true,
-      "requires": {
-        "caniuse-lite": "^1.0.30001280",
-        "electron-to-chromium": "^1.3.896",
-        "escalade": "^3.1.1",
-        "node-releases": "^2.0.1",
-        "picocolors": "^1.0.0"
-      }
+      "optional": true
     },
-    "buffer": {
-      "version": "5.7.1",
-      "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
-      "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
+    "esbuild-linux-64": {
+      "version": "0.13.8",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.13.8.tgz",
+      "integrity": "sha512-Bm8SYmFtvfDCIu9sjKppFXzRXn2BVpuCinU1ChTuMtdKI/7aPpXIrkqBNOgPTOQO9AylJJc1Zw6EvtKORhn64w==",
       "dev": true,
-      "requires": {
-        "base64-js": "^1.3.1",
-        "ieee754": "^1.1.13"
-      }
+      "optional": true
     },
-    "buffer-from": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
-      "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
-      "dev": true
+    "esbuild-linux-arm": {
+      "version": "0.13.8",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.13.8.tgz",
+      "integrity": "sha512-4/HfcC40LJ4GPyboHA+db0jpFarTB628D1ifU+/5bunIgY+t6mHkJWyxWxAAE8wl/ZIuRYB9RJFdYpu1AXGPdg==",
+      "dev": true,
+      "optional": true
     },
-    "buffer-indexof": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz",
-      "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==",
-      "dev": true
+    "esbuild-linux-arm64": {
+      "version": "0.13.8",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.13.8.tgz",
+      "integrity": "sha512-X4pWZ+SL+FJ09chWFgRNO3F+YtvAQRcWh0uxKqZSWKiWodAB20flsW/OWFYLXBKiVCTeoGMvENZS/GeVac7+tQ==",
+      "dev": true,
+      "optional": true
     },
-    "builtins": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz",
-      "integrity": "sha1-y5T662HIaWRR2zZTThQi+U8K7og=",
-      "dev": true
+    "esbuild-linux-mips64le": {
+      "version": "0.13.8",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.13.8.tgz",
+      "integrity": "sha512-o7e0D+sqHKT31v+mwFircJFjwSKVd2nbkHEn4l9xQ1hLR+Bv8rnt3HqlblY3+sBdlrOTGSwz0ReROlKUMJyldA==",
+      "dev": true,
+      "optional": true
     },
-    "bytes": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
-      "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=",
-      "dev": true
+    "esbuild-linux-ppc64le": {
+      "version": "0.13.8",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.13.8.tgz",
+      "integrity": "sha512-eZSQ0ERsWkukJp2px/UWJHVNuy0lMoz/HZcRWAbB6reoaBw7S9vMzYNUnflfL3XA6WDs+dZn3ekHE4Y2uWLGig==",
+      "dev": true,
+      "optional": true
     },
-    "cacache": {
-      "version": "15.2.0",
-      "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.2.0.tgz",
-      "integrity": "sha512-uKoJSHmnrqXgthDFx/IU6ED/5xd+NNGe+Bb+kLZy7Ku4P+BaiWEUflAKPZ7eAzsYGcsAGASJZsybXp+quEcHTw==",
+    "esbuild-netbsd-64": {
+      "version": "0.13.8",
+      "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.13.8.tgz",
+      "integrity": "sha512-gZX4kP7gVvOrvX0ZwgHmbuHczQUwqYppxqtoyC7VNd80t5nBHOFXVhWo2Ad/Lms0E8b+wwgI/WjZFTCpUHOg9Q==",
       "dev": true,
-      "requires": {
-        "@npmcli/move-file": "^1.0.1",
-        "chownr": "^2.0.0",
-        "fs-minipass": "^2.0.0",
-        "glob": "^7.1.4",
-        "infer-owner": "^1.0.4",
-        "lru-cache": "^6.0.0",
-        "minipass": "^3.1.1",
-        "minipass-collect": "^1.0.2",
-        "minipass-flush": "^1.0.5",
-        "minipass-pipeline": "^1.2.2",
-        "mkdirp": "^1.0.3",
-        "p-map": "^4.0.0",
-        "promise-inflight": "^1.0.1",
-        "rimraf": "^3.0.2",
-        "ssri": "^8.0.1",
-        "tar": "^6.0.2",
-        "unique-filename": "^1.1.1"
-      }
+      "optional": true
     },
-    "cache-base": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
-      "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==",
+    "esbuild-openbsd-64": {
+      "version": "0.13.8",
+      "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.13.8.tgz",
+      "integrity": "sha512-afzza308X4WmcebexbTzAgfEWt9MUkdTvwIa8xOu4CM2qGbl2LanqEl8/LUs8jh6Gqw6WsicEK52GPrS9wvkcw==",
       "dev": true,
-      "requires": {
-        "collection-visit": "^1.0.0",
-        "component-emitter": "^1.2.1",
-        "get-value": "^2.0.6",
-        "has-value": "^1.0.0",
-        "isobject": "^3.0.1",
-        "set-value": "^2.0.0",
-        "to-object-path": "^0.3.0",
-        "union-value": "^1.0.0",
-        "unset-value": "^1.0.0"
-      }
+      "optional": true
     },
-    "call-bind": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
-      "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
+    "esbuild-sunos-64": {
+      "version": "0.13.8",
+      "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.13.8.tgz",
+      "integrity": "sha512-mWPZibmBbuMKD+LDN23LGcOZ2EawMYBONMXXHmbuxeT0XxCNwadbCVwUQ/2p5Dp5Kvf6mhrlIffcnWOiCBpiVw==",
       "dev": true,
-      "requires": {
-        "function-bind": "^1.1.1",
-        "get-intrinsic": "^1.0.2"
-      }
+      "optional": true
     },
-    "callsites": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
-      "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+    "esbuild-wasm": {
+      "version": "0.13.8",
+      "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.13.8.tgz",
+      "integrity": "sha512-UbD+3nloiSpJWXTCInZQrqPe8Y+RLfDkY/5kEHiXsw/lmaEvibe69qTzQu16m5R9je/0bF7VYQ5jaEOq0z9lLA==",
       "dev": true
     },
-    "camelcase": {
-      "version": "5.3.1",
-      "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
-      "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
-      "dev": true
+    "esbuild-windows-32": {
+      "version": "0.13.8",
+      "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.13.8.tgz",
+      "integrity": "sha512-QsZ1HnWIcnIEApETZWw8HlOhDSWqdZX2SylU7IzGxOYyVcX7QI06ety/aDcn437mwyO7Ph4RrbhB+2ntM8kX8A==",
+      "dev": true,
+      "optional": true
     },
-    "caniuse-api": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz",
-      "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==",
+    "esbuild-windows-64": {
+      "version": "0.13.8",
+      "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.13.8.tgz",
+      "integrity": "sha512-76Fb57B9eE/JmJi1QmUW0tRLQZfGo0it+JeYoCDTSlbTn7LV44ecOHIMJSSgZADUtRMWT9z0Kz186bnaB3amSg==",
       "dev": true,
-      "requires": {
-        "browserslist": "^4.0.0",
-        "caniuse-lite": "^1.0.0",
-        "lodash.memoize": "^4.1.2",
-        "lodash.uniq": "^4.5.0"
-      }
+      "optional": true
     },
-    "caniuse-lite": {
-      "version": "1.0.30001283",
-      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001283.tgz",
-      "integrity": "sha512-9RoKo841j1GQFSJz/nCXOj0sD7tHBtlowjYlrqIUS812x9/emfBLBt6IyMz1zIaYc/eRL8Cs6HPUVi2Hzq4sIg==",
+    "esbuild-windows-arm64": {
+      "version": "0.13.8",
+      "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.13.8.tgz",
+      "integrity": "sha512-HW6Mtq5eTudllxY2YgT62MrVcn7oq2o8TAoAvDUhyiEmRmDY8tPwAhb1vxw5/cdkbukM3KdMYtksnUhF/ekWeg==",
+      "dev": true,
+      "optional": true
+    },
+    "escalade": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
+      "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
       "dev": true
     },
-    "canonical-path": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/canonical-path/-/canonical-path-1.0.0.tgz",
-      "integrity": "sha512-feylzsbDxi1gPZ1IjystzIQZagYYLvfKrSuygUCgf7z6x790VEzze5QEkdSV1U58RA7Hi0+v6fv4K54atOzATg==",
+    "escape-html": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+      "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=",
       "dev": true
     },
-    "caseless": {
-      "version": "0.12.0",
-      "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
-      "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=",
+    "escape-string-regexp": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+      "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
       "dev": true
     },
-    "chalk": {
-      "version": "2.4.2",
-      "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
-      "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+    "escodegen": {
+      "version": "1.14.3",
+      "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz",
+      "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==",
       "dev": true,
       "requires": {
-        "ansi-styles": "^3.2.1",
-        "escape-string-regexp": "^1.0.5",
-        "supports-color": "^5.3.0"
+        "esprima": "^4.0.1",
+        "estraverse": "^4.2.0",
+        "esutils": "^2.0.2",
+        "optionator": "^0.8.1",
+        "source-map": "~0.6.1"
+      },
+      "dependencies": {
+        "source-map": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+          "dev": true,
+          "optional": true
+        }
       }
     },
-    "chardet": {
-      "version": "0.7.0",
-      "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
-      "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==",
-      "dev": true
-    },
-    "chokidar": {
-      "version": "3.5.2",
-      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz",
-      "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==",
+    "eslint": {
+      "version": "7.32.0",
+      "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz",
+      "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==",
       "dev": true,
-      "requires": {
-        "anymatch": "~3.1.2",
-        "braces": "~3.0.2",
-        "fsevents": "~2.3.2",
-        "glob-parent": "~5.1.2",
-        "is-binary-path": "~2.1.0",
-        "is-glob": "~4.0.1",
-        "normalize-path": "~3.0.0",
-        "readdirp": "~3.6.0"
+      "requires": {
+        "@babel/code-frame": "7.12.11",
+        "@eslint/eslintrc": "^0.4.3",
+        "@humanwhocodes/config-array": "^0.5.0",
+        "ajv": "^6.10.0",
+        "chalk": "^4.0.0",
+        "cross-spawn": "^7.0.2",
+        "debug": "^4.0.1",
+        "doctrine": "^3.0.0",
+        "enquirer": "^2.3.5",
+        "escape-string-regexp": "^4.0.0",
+        "eslint-scope": "^5.1.1",
+        "eslint-utils": "^2.1.0",
+        "eslint-visitor-keys": "^2.0.0",
+        "espree": "^7.3.1",
+        "esquery": "^1.4.0",
+        "esutils": "^2.0.2",
+        "fast-deep-equal": "^3.1.3",
+        "file-entry-cache": "^6.0.1",
+        "functional-red-black-tree": "^1.0.1",
+        "glob-parent": "^5.1.2",
+        "globals": "^13.6.0",
+        "ignore": "^4.0.6",
+        "import-fresh": "^3.0.0",
+        "imurmurhash": "^0.1.4",
+        "is-glob": "^4.0.0",
+        "js-yaml": "^3.13.1",
+        "json-stable-stringify-without-jsonify": "^1.0.1",
+        "levn": "^0.4.1",
+        "lodash.merge": "^4.6.2",
+        "minimatch": "^3.0.4",
+        "natural-compare": "^1.4.0",
+        "optionator": "^0.9.1",
+        "progress": "^2.0.0",
+        "regexpp": "^3.1.0",
+        "semver": "^7.2.1",
+        "strip-ansi": "^6.0.0",
+        "strip-json-comments": "^3.1.0",
+        "table": "^6.0.9",
+        "text-table": "^0.2.0",
+        "v8-compile-cache": "^2.0.3"
       },
       "dependencies": {
+        "@babel/code-frame": {
+          "version": "7.12.11",
+          "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz",
+          "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==",
+          "dev": true,
+          "requires": {
+            "@babel/highlight": "^7.10.4"
+          }
+        },
+        "ansi-styles": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+          "dev": true,
+          "requires": {
+            "color-convert": "^2.0.1"
+          }
+        },
+        "chalk": {
+          "version": "4.1.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+          "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "dev": true,
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
+        },
+        "cross-spawn": {
+          "version": "7.0.3",
+          "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
+          "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+          "dev": true,
+          "requires": {
+            "path-key": "^3.1.0",
+            "shebang-command": "^2.0.0",
+            "which": "^2.0.1"
+          }
+        },
+        "escape-string-regexp": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+          "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+          "dev": true
+        },
+        "eslint-utils": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz",
+          "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==",
+          "dev": true,
+          "requires": {
+            "eslint-visitor-keys": "^1.1.0"
+          },
+          "dependencies": {
+            "eslint-visitor-keys": {
+              "version": "1.3.0",
+              "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
+              "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
+              "dev": true
+            }
+          }
+        },
         "glob-parent": {
           "version": "5.1.2",
           "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
@@ -3231,3028 +13866,3661 @@
           "requires": {
             "is-glob": "^4.0.1"
           }
-        }
-      }
-    },
-    "chownr": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
-      "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
-      "dev": true
-    },
-    "chrome-trace-event": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz",
-      "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==",
-      "dev": true
-    },
-    "circular-dependency-plugin": {
-      "version": "5.2.2",
-      "resolved": "https://registry.npmjs.org/circular-dependency-plugin/-/circular-dependency-plugin-5.2.2.tgz",
-      "integrity": "sha512-g38K9Cm5WRwlaH6g03B9OEz/0qRizI+2I7n+Gz+L5DxXJAPAiWQvwlYNm1V1jkdpUv95bOe/ASm2vfi/G560jQ==",
-      "dev": true
-    },
-    "class-utils": {
-      "version": "0.3.6",
-      "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
-      "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==",
-      "dev": true,
-      "requires": {
-        "arr-union": "^3.1.0",
-        "define-property": "^0.2.5",
-        "isobject": "^3.0.0",
-        "static-extend": "^0.1.1"
-      },
-      "dependencies": {
-        "define-property": {
-          "version": "0.2.5",
-          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
-          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+        },
+        "globals": {
+          "version": "13.15.0",
+          "resolved": "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz",
+          "integrity": "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==",
           "dev": true,
           "requires": {
-            "is-descriptor": "^0.1.0"
+            "type-fest": "^0.20.2"
           }
-        }
-      }
-    },
-    "clean-stack": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
-      "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==",
-      "dev": true
-    },
-    "cli-cursor": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
-      "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
-      "dev": true,
-      "requires": {
-        "restore-cursor": "^3.1.0"
-      }
-    },
-    "cli-spinners": {
-      "version": "2.6.1",
-      "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz",
-      "integrity": "sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==",
-      "dev": true
-    },
-    "cli-width": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz",
-      "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==",
-      "dev": true
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
+        "ignore": {
+          "version": "4.0.6",
+          "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
+          "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
+          "dev": true
+        },
+        "levn": {
+          "version": "0.4.1",
+          "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+          "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+          "dev": true,
+          "requires": {
+            "prelude-ls": "^1.2.1",
+            "type-check": "~0.4.0"
+          }
+        },
+        "optionator": {
+          "version": "0.9.1",
+          "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
+          "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==",
+          "dev": true,
+          "requires": {
+            "deep-is": "^0.1.3",
+            "fast-levenshtein": "^2.0.6",
+            "levn": "^0.4.1",
+            "prelude-ls": "^1.2.1",
+            "type-check": "^0.4.0",
+            "word-wrap": "^1.2.3"
+          }
+        },
+        "path-key": {
+          "version": "3.1.1",
+          "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+          "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+          "dev": true
+        },
+        "prelude-ls": {
+          "version": "1.2.1",
+          "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+          "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+          "dev": true
+        },
+        "shebang-command": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+          "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+          "dev": true,
+          "requires": {
+            "shebang-regex": "^3.0.0"
+          }
+        },
+        "shebang-regex": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+          "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+          "dev": true
+        },
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
+        },
+        "type-check": {
+          "version": "0.4.0",
+          "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+          "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+          "dev": true,
+          "requires": {
+            "prelude-ls": "^1.2.1"
+          }
+        },
+        "type-fest": {
+          "version": "0.20.2",
+          "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+          "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+          "dev": true
+        },
+        "which": {
+          "version": "2.0.2",
+          "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+          "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+          "dev": true,
+          "requires": {
+            "isexe": "^2.0.0"
+          }
+        }
+      }
     },
-    "cliui": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
-      "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
+    "eslint-plugin-storybook": {
+      "version": "0.5.12",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-storybook/-/eslint-plugin-storybook-0.5.12.tgz",
+      "integrity": "sha512-ojuNKnrZFrQpm5N5Lp8UR0VEn4HtLjlNn6nxQAYlmTsEXNigtId1XPuMbXAsvFcEmv3RTb5l+9tZgkhSURfACg==",
       "dev": true,
       "requires": {
-        "string-width": "^3.1.0",
-        "strip-ansi": "^5.2.0",
-        "wrap-ansi": "^5.1.0"
+        "@storybook/csf": "^0.0.1",
+        "@typescript-eslint/experimental-utils": "^5.3.0",
+        "requireindex": "^1.1.0"
       },
       "dependencies": {
-        "string-width": {
-          "version": "3.1.0",
-          "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
-          "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+        "@storybook/csf": {
+          "version": "0.0.1",
+          "resolved": "https://registry.npmjs.org/@storybook/csf/-/csf-0.0.1.tgz",
+          "integrity": "sha512-USTLkZze5gkel8MYCujSRBVIrUQ3YPBrLOx7GNk/0wttvVtlzWXAq9eLbQ4p/NicGxP+3T7KPEMVV//g+yubpw==",
           "dev": true,
           "requires": {
-            "emoji-regex": "^7.0.1",
-            "is-fullwidth-code-point": "^2.0.0",
-            "strip-ansi": "^5.1.0"
+            "lodash": "^4.17.15"
+          }
+        },
+        "@typescript-eslint/experimental-utils": {
+          "version": "5.26.0",
+          "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.26.0.tgz",
+          "integrity": "sha512-OgUGXC/teXD8PYOkn33RSwBJPVwL0I2ipm5OHr9g9cfAhVrPC2DxQiWqaq88MNO5mbr/ZWnav3EVBpuwDreS5Q==",
+          "dev": true,
+          "requires": {
+            "@typescript-eslint/utils": "5.26.0"
           }
         }
       }
     },
-    "clone": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz",
-      "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=",
-      "dev": true
-    },
-    "clone-deep": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz",
-      "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==",
-      "dev": true,
-      "requires": {
-        "is-plain-object": "^2.0.4",
-        "kind-of": "^6.0.2",
-        "shallow-clone": "^3.0.0"
-      }
-    },
-    "code-point-at": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
-      "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
-      "dev": true
-    },
-    "collection-visit": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
-      "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=",
-      "dev": true,
-      "requires": {
-        "map-visit": "^1.0.0",
-        "object-visit": "^1.0.0"
-      }
-    },
-    "color-convert": {
-      "version": "1.9.3",
-      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
-      "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+    "eslint-scope": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
+      "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
       "dev": true,
       "requires": {
-        "color-name": "1.1.3"
+        "esrecurse": "^4.3.0",
+        "estraverse": "^4.1.1"
       }
     },
-    "color-name": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-      "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
-      "dev": true
-    },
-    "colord": {
-      "version": "2.9.1",
-      "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.1.tgz",
-      "integrity": "sha512-4LBMSt09vR0uLnPVkOUBnmxgoaeN4ewRbx801wY/bXcltXfpR/G46OdWn96XpYmCWuYvO46aBZP4NgX8HpNAcw==",
-      "dev": true
-    },
-    "colorette": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz",
-      "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==",
-      "dev": true
-    },
-    "colors": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz",
-      "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==",
-      "dev": true
-    },
-    "combined-stream": {
-      "version": "1.0.8",
-      "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
-      "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+    "eslint-utils": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz",
+      "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==",
       "dev": true,
       "requires": {
-        "delayed-stream": "~1.0.0"
+        "eslint-visitor-keys": "^2.0.0"
       }
     },
-    "commander": {
-      "version": "7.2.0",
-      "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
-      "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
-      "dev": true
-    },
-    "commondir": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
-      "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=",
-      "dev": true
-    },
-    "component-emitter": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
-      "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==",
+    "eslint-visitor-keys": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
+      "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
       "dev": true
     },
-    "compressible": {
-      "version": "2.0.18",
-      "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz",
-      "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==",
-      "dev": true,
-      "requires": {
-        "mime-db": ">= 1.43.0 < 2"
-      }
-    },
-    "compression": {
-      "version": "1.7.4",
-      "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz",
-      "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==",
+    "espree": {
+      "version": "7.3.1",
+      "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz",
+      "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==",
       "dev": true,
       "requires": {
-        "accepts": "~1.3.5",
-        "bytes": "3.0.0",
-        "compressible": "~2.0.16",
-        "debug": "2.6.9",
-        "on-headers": "~1.0.2",
-        "safe-buffer": "5.1.2",
-        "vary": "~1.1.2"
+        "acorn": "^7.4.0",
+        "acorn-jsx": "^5.3.1",
+        "eslint-visitor-keys": "^1.3.0"
       },
       "dependencies": {
-        "debug": {
-          "version": "2.6.9",
-          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
-          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
-          "dev": true,
-          "requires": {
-            "ms": "2.0.0"
-          }
+        "acorn": {
+          "version": "7.4.1",
+          "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
+          "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
+          "dev": true
         },
-        "ms": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+        "eslint-visitor-keys": {
+          "version": "1.3.0",
+          "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
+          "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
           "dev": true
         }
       }
     },
-    "concat-map": {
-      "version": "0.0.1",
-      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
-      "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+    "esprima": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+      "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
       "dev": true
     },
-    "connect": {
-      "version": "3.7.0",
-      "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz",
-      "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==",
+    "esquery": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz",
+      "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==",
       "dev": true,
       "requires": {
-        "debug": "2.6.9",
-        "finalhandler": "1.1.2",
-        "parseurl": "~1.3.3",
-        "utils-merge": "1.0.1"
+        "estraverse": "^5.1.0"
       },
       "dependencies": {
-        "debug": {
-          "version": "2.6.9",
-          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
-          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
-          "dev": true,
-          "requires": {
-            "ms": "2.0.0"
-          }
-        },
-        "ms": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+        "estraverse": {
+          "version": "5.3.0",
+          "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+          "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
           "dev": true
         }
       }
     },
-    "connect-history-api-fallback": {
-      "version": "1.6.0",
-      "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz",
-      "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==",
-      "dev": true
-    },
-    "console-control-strings": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
-      "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
-      "dev": true
-    },
-    "content-disposition": {
-      "version": "0.5.3",
-      "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
-      "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
+    "esrecurse": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+      "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
       "dev": true,
       "requires": {
-        "safe-buffer": "5.1.2"
+        "estraverse": "^5.2.0"
+      },
+      "dependencies": {
+        "estraverse": {
+          "version": "5.3.0",
+          "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+          "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+          "dev": true
+        }
       }
     },
-    "content-type": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
-      "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==",
+    "estraverse": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
+      "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
       "dev": true
     },
-    "convert-source-map": {
-      "version": "1.8.0",
-      "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz",
-      "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==",
-      "dev": true,
-      "requires": {
-        "safe-buffer": "~5.1.1"
-      }
-    },
-    "cookie": {
-      "version": "0.4.0",
-      "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
-      "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==",
+    "estree-is-function": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/estree-is-function/-/estree-is-function-1.0.0.tgz",
+      "integrity": "sha512-nSCWn1jkSq2QAtkaVLJZY2ezwcFO161HVc174zL1KPW3RJ+O6C3eJb8Nx7OXzvhoEv+nLgSR1g71oWUHUDTrJA==",
       "dev": true
     },
-    "cookie-signature": {
-      "version": "1.0.6",
-      "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
-      "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=",
+    "esutils": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+      "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
       "dev": true
     },
-    "copy-anything": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.3.tgz",
-      "integrity": "sha512-GK6QUtisv4fNS+XcI7shX0Gx9ORg7QqIznyfho79JTnX1XhLiyZHfftvGiziqzRiEi/Bjhgpi+D2o7HxJFPnDQ==",
+    "etag": {
+      "version": "1.8.1",
+      "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+      "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=",
+      "dev": true
+    },
+    "event-emitter": {
+      "version": "0.3.5",
+      "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz",
+      "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=",
       "dev": true,
       "requires": {
-        "is-what": "^3.12.0"
+        "d": "1",
+        "es5-ext": "~0.10.14"
       }
     },
-    "copy-descriptor": {
-      "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
-      "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=",
-      "dev": true
-    },
-    "copy-webpack-plugin": {
-      "version": "9.0.1",
-      "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-9.0.1.tgz",
-      "integrity": "sha512-14gHKKdYIxF84jCEgPgYXCPpldbwpxxLbCmA7LReY7gvbaT555DgeBWBgBZM116tv/fO6RRJrsivBqRyRlukhw==",
+    "event-stream": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-4.0.1.tgz",
+      "integrity": "sha512-qACXdu/9VHPBzcyhdOWR5/IahhGMf0roTeZJfzz077GwylcDd90yOHLouhmv7GJ5XzPi6ekaQWd8AvPP2nOvpA==",
       "dev": true,
       "requires": {
-        "fast-glob": "^3.2.5",
-        "glob-parent": "^6.0.0",
-        "globby": "^11.0.3",
-        "normalize-path": "^3.0.0",
-        "p-limit": "^3.1.0",
-        "schema-utils": "^3.0.0",
-        "serialize-javascript": "^6.0.0"
-      },
-      "dependencies": {
-        "p-limit": {
-          "version": "3.1.0",
-          "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
-          "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
-          "dev": true,
-          "requires": {
-            "yocto-queue": "^0.1.0"
-          }
-        }
+        "duplexer": "^0.1.1",
+        "from": "^0.1.7",
+        "map-stream": "0.0.7",
+        "pause-stream": "^0.0.11",
+        "split": "^1.0.1",
+        "stream-combiner": "^0.2.2",
+        "through": "^2.3.8"
       }
     },
-    "core-js": {
-      "version": "3.16.0",
-      "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.16.0.tgz",
-      "integrity": "sha512-5+5VxRFmSf97nM8Jr2wzOwLqRo6zphH2aX+7KsAUONObyzakDNq2G/bgbhinxB4PoV9L3aXQYhiDKyIKWd2c8g==",
+    "eventemitter-asyncresource": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/eventemitter-asyncresource/-/eventemitter-asyncresource-1.0.0.tgz",
+      "integrity": "sha512-39F7TBIV0G7gTelxwbEqnwhp90eqCPON1k0NwNfwhgKn4Co4ybUbj2pECcXT0B3ztRKZ7Pw1JujUUgmQJHcVAQ==",
       "dev": true
     },
-    "core-js-compat": {
-      "version": "3.19.2",
-      "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.19.2.tgz",
-      "integrity": "sha512-ObBY1W5vx/LFFMaL1P5Udo4Npib6fu+cMokeziWkA8Tns4FcDemKF5j9JvaI5JhdkW8EQJQGJN1EcrzmEwuAqQ==",
+    "eventemitter3": {
+      "version": "4.0.7",
+      "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
+      "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
+      "dev": true
+    },
+    "events": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
+      "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
+      "dev": true
+    },
+    "eventsource": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.1.1.tgz",
+      "integrity": "sha512-qV5ZC0h7jYIAOhArFJgSfdyz6rALJyb270714o7ZtNnw2WSJ+eexhKtE0O8LYPRsHZHf2osHKZBxGPvm3kPkCA==",
       "dev": true,
       "requires": {
-        "browserslist": "^4.18.1",
-        "semver": "7.0.0"
-      },
-      "dependencies": {
-        "semver": {
-          "version": "7.0.0",
-          "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz",
-          "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==",
-          "dev": true
-        }
+        "original": "^1.0.0"
       }
     },
-    "core-util-is": {
+    "evp_bytestokey": {
       "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
-      "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
-    },
-    "cors": {
-      "version": "2.8.5",
-      "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
-      "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
+      "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz",
+      "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==",
       "dev": true,
       "requires": {
-        "object-assign": "^4",
-        "vary": "^1"
+        "md5.js": "^1.3.4",
+        "safe-buffer": "^5.1.1"
       }
     },
-    "cosmiconfig": {
-      "version": "7.0.1",
-      "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz",
-      "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==",
+    "exec-sh": {
+      "version": "0.3.6",
+      "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.6.tgz",
+      "integrity": "sha512-nQn+hI3yp+oD0huYhKwvYI32+JFeq+XkNcD1GAo3Y/MjxsfVGmrrzrnzjWiNY6f+pUCP440fThsFh5gZrRAU/w==",
+      "dev": true
+    },
+    "execa": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz",
+      "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==",
       "dev": true,
       "requires": {
-        "@types/parse-json": "^4.0.0",
-        "import-fresh": "^3.2.1",
-        "parse-json": "^5.0.0",
-        "path-type": "^4.0.0",
-        "yaml": "^1.10.0"
+        "cross-spawn": "^6.0.0",
+        "get-stream": "^4.0.0",
+        "is-stream": "^1.1.0",
+        "npm-run-path": "^2.0.0",
+        "p-finally": "^1.0.0",
+        "signal-exit": "^3.0.0",
+        "strip-eof": "^1.0.0"
       }
     },
-    "critters": {
-      "version": "0.0.12",
-      "resolved": "https://registry.npmjs.org/critters/-/critters-0.0.12.tgz",
-      "integrity": "sha512-ujxKtKc/mWpjrOKeaACTaQ1aP0O31M0ZPWhfl85jZF1smPU4Ivb9va5Ox2poif4zVJQQo0LCFlzGtEZAsCAPcw==",
+    "expand-brackets": {
+      "version": "2.1.4",
+      "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
+      "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=",
       "dev": true,
       "requires": {
-        "chalk": "^4.1.0",
-        "css-select": "^4.1.3",
-        "parse5": "^6.0.1",
-        "parse5-htmlparser2-tree-adapter": "^6.0.1",
-        "postcss": "^8.3.7",
-        "pretty-bytes": "^5.3.0"
+        "debug": "^2.3.3",
+        "define-property": "^0.2.5",
+        "extend-shallow": "^2.0.1",
+        "posix-character-classes": "^0.1.0",
+        "regex-not": "^1.0.0",
+        "snapdragon": "^0.8.1",
+        "to-regex": "^3.0.1"
       },
       "dependencies": {
-        "ansi-styles": {
-          "version": "4.3.0",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
-          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+        "debug": {
+          "version": "2.6.9",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
           "dev": true,
           "requires": {
-            "color-convert": "^2.0.1"
+            "ms": "2.0.0"
           }
         },
-        "chalk": {
-          "version": "4.1.2",
-          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
-          "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+        "define-property": {
+          "version": "0.2.5",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
           "dev": true,
           "requires": {
-            "ansi-styles": "^4.1.0",
-            "supports-color": "^7.1.0"
+            "is-descriptor": "^0.1.0"
           }
         },
-        "color-convert": {
+        "extend-shallow": {
           "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
-          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
           "dev": true,
           "requires": {
-            "color-name": "~1.1.4"
+            "is-extendable": "^0.1.0"
           }
         },
-        "color-name": {
-          "version": "1.1.4",
-          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
-          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
-          "dev": true
-        },
-        "has-flag": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
-          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
-          "dev": true
-        },
-        "parse5": {
-          "version": "6.0.1",
-          "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
-          "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==",
+        "ms": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
           "dev": true
-        },
-        "supports-color": {
-          "version": "7.2.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
-          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
-          "dev": true,
-          "requires": {
-            "has-flag": "^4.0.0"
-          }
         }
       }
     },
-    "cross-spawn": {
-      "version": "6.0.5",
-      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
-      "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
+    "export-nehuba": {
+      "version": "0.0.12",
+      "resolved": "https://registry.npmjs.org/export-nehuba/-/export-nehuba-0.0.12.tgz",
+      "integrity": "sha512-pf3hAwpXaOqlfBfgmPLYQ+uLqJ+ElyvE1bDrrCrf5Qf0Otsekw+8CcyAJhP5O15Yacmhe7Py3G96tw5bbvZyIA==",
+      "requires": {
+        "pako": "^1.0.6"
+      }
+    },
+    "express": {
+      "version": "4.18.1",
+      "resolved": "https://registry.npmjs.org/express/-/express-4.18.1.tgz",
+      "integrity": "sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==",
       "dev": true,
       "requires": {
-        "nice-try": "^1.0.4",
-        "path-key": "^2.0.1",
-        "semver": "^5.5.0",
-        "shebang-command": "^1.2.0",
-        "which": "^1.2.9"
+        "accepts": "~1.3.8",
+        "array-flatten": "1.1.1",
+        "body-parser": "1.20.0",
+        "content-disposition": "0.5.4",
+        "content-type": "~1.0.4",
+        "cookie": "0.5.0",
+        "cookie-signature": "1.0.6",
+        "debug": "2.6.9",
+        "depd": "2.0.0",
+        "encodeurl": "~1.0.2",
+        "escape-html": "~1.0.3",
+        "etag": "~1.8.1",
+        "finalhandler": "1.2.0",
+        "fresh": "0.5.2",
+        "http-errors": "2.0.0",
+        "merge-descriptors": "1.0.1",
+        "methods": "~1.1.2",
+        "on-finished": "2.4.1",
+        "parseurl": "~1.3.3",
+        "path-to-regexp": "0.1.7",
+        "proxy-addr": "~2.0.7",
+        "qs": "6.10.3",
+        "range-parser": "~1.2.1",
+        "safe-buffer": "5.2.1",
+        "send": "0.18.0",
+        "serve-static": "1.15.0",
+        "setprototypeof": "1.2.0",
+        "statuses": "2.0.1",
+        "type-is": "~1.6.18",
+        "utils-merge": "1.0.1",
+        "vary": "~1.1.2"
       },
       "dependencies": {
-        "semver": {
-          "version": "5.7.1",
-          "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
-          "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+        "array-flatten": {
+          "version": "1.1.1",
+          "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
+          "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
+          "dev": true
+        },
+        "debug": {
+          "version": "2.6.9",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+          "dev": true,
+          "requires": {
+            "ms": "2.0.0"
+          }
+        },
+        "ms": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+          "dev": true
+        },
+        "safe-buffer": {
+          "version": "5.2.1",
+          "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+          "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
           "dev": true
         }
       }
     },
-    "css": {
-      "version": "2.2.4",
-      "resolved": "https://registry.npmjs.org/css/-/css-2.2.4.tgz",
-      "integrity": "sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==",
+    "ext": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/ext/-/ext-1.6.0.tgz",
+      "integrity": "sha512-sdBImtzkq2HpkdRLtlLWDa6w4DX22ijZLKx8BMPUuKe1c5lbN6xwQDQCxSfxBQnHZ13ls/FH0MQZx/q/gr6FQg==",
       "dev": true,
       "requires": {
-        "inherits": "^2.0.3",
-        "source-map": "^0.6.1",
-        "source-map-resolve": "^0.5.2",
-        "urix": "^0.1.0"
-      },
-      "dependencies": {
-        "source-map": {
-          "version": "0.6.1",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+        "type": "^2.5.0"
+      },
+      "dependencies": {
+        "type": {
+          "version": "2.6.0",
+          "resolved": "https://registry.npmjs.org/type/-/type-2.6.0.tgz",
+          "integrity": "sha512-eiDBDOmkih5pMbo9OqsqPRGMljLodLcwd5XD5JbtNB0o89xZAwynY9EdCDsJU7LtcVCClu9DvM7/0Ep1hYX3EQ==",
           "dev": true
         }
       }
     },
-    "css-blank-pseudo": {
-      "version": "0.1.4",
-      "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-0.1.4.tgz",
-      "integrity": "sha512-LHz35Hr83dnFeipc7oqFDmsjHdljj3TQtxGGiNWSOsTLIAubSm4TEz8qCaKFpk7idaQ1GfWscF4E6mgpBysA1w==",
+    "extend": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+      "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+      "dev": true
+    },
+    "extend-shallow": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
+      "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
       "dev": true,
       "requires": {
-        "postcss": "^7.0.5"
+        "assign-symbols": "^1.0.0",
+        "is-extendable": "^1.0.1"
       },
       "dependencies": {
-        "picocolors": {
-          "version": "0.2.1",
-          "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz",
-          "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==",
-          "dev": true
-        },
-        "postcss": {
-          "version": "7.0.39",
-          "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz",
-          "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==",
+        "is-extendable": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+          "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
           "dev": true,
           "requires": {
-            "picocolors": "^0.2.1",
-            "source-map": "^0.6.1"
+            "is-plain-object": "^2.0.4"
           }
-        },
-        "source-map": {
-          "version": "0.6.1",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
-          "dev": true
         }
       }
     },
-    "css-declaration-sorter": {
-      "version": "6.1.3",
-      "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.1.3.tgz",
-      "integrity": "sha512-SvjQjNRZgh4ULK1LDJ2AduPKUKxIqmtU7ZAyi47BTV+M90Qvxr9AB6lKlLbDUfXqI9IQeYA8LbAsCZPpJEV3aA==",
+    "external-editor": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz",
+      "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==",
       "dev": true,
       "requires": {
-        "timsort": "^0.3.0"
+        "chardet": "^0.7.0",
+        "iconv-lite": "^0.4.24",
+        "tmp": "^0.0.33"
       }
     },
-    "css-has-pseudo": {
-      "version": "0.10.0",
-      "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-0.10.0.tgz",
-      "integrity": "sha512-Z8hnfsZu4o/kt+AuFzeGpLVhFOGO9mluyHBaA2bA8aCGTwah5sT3WV/fTHH8UNZUytOIImuGPrl/prlb4oX4qQ==",
+    "extglob": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
+      "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
       "dev": true,
       "requires": {
-        "postcss": "^7.0.6",
-        "postcss-selector-parser": "^5.0.0-rc.4"
+        "array-unique": "^0.3.2",
+        "define-property": "^1.0.0",
+        "expand-brackets": "^2.1.4",
+        "extend-shallow": "^2.0.1",
+        "fragment-cache": "^0.2.1",
+        "regex-not": "^1.0.0",
+        "snapdragon": "^0.8.1",
+        "to-regex": "^3.0.1"
       },
       "dependencies": {
-        "cssesc": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz",
-          "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==",
-          "dev": true
+        "define-property": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+          "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+          "dev": true,
+          "requires": {
+            "is-descriptor": "^1.0.0"
+          }
         },
-        "picocolors": {
-          "version": "0.2.1",
-          "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz",
-          "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==",
-          "dev": true
+        "extend-shallow": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+          "dev": true,
+          "requires": {
+            "is-extendable": "^0.1.0"
+          }
         },
-        "postcss": {
-          "version": "7.0.39",
-          "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz",
-          "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==",
+        "is-accessor-descriptor": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
           "dev": true,
           "requires": {
-            "picocolors": "^0.2.1",
-            "source-map": "^0.6.1"
+            "kind-of": "^6.0.0"
           }
         },
-        "postcss-selector-parser": {
-          "version": "5.0.0",
-          "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz",
-          "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==",
+        "is-data-descriptor": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
           "dev": true,
           "requires": {
-            "cssesc": "^2.0.0",
-            "indexes-of": "^1.0.1",
-            "uniq": "^1.0.1"
+            "kind-of": "^6.0.0"
           }
         },
-        "source-map": {
-          "version": "0.6.1",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
-          "dev": true
+        "is-descriptor": {
+          "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+          "dev": true,
+          "requires": {
+            "is-accessor-descriptor": "^1.0.0",
+            "is-data-descriptor": "^1.0.0",
+            "kind-of": "^6.0.2"
+          }
         }
       }
     },
-    "css-loader": {
-      "version": "6.2.0",
-      "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.2.0.tgz",
-      "integrity": "sha512-/rvHfYRjIpymZblf49w8jYcRo2y9gj6rV8UroHGmBxKrIyGLokpycyKzp9OkitvqT29ZSpzJ0Ic7SpnJX3sC8g==",
+    "fancy-log": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-2.0.0.tgz",
+      "integrity": "sha512-9CzxZbACXMUXW13tS0tI8XsGGmxWzO2DmYrGuBJOJ8k8q2K7hwfJA5qHjuPPe8wtsco33YR9wc+Rlr5wYFvhSA==",
       "dev": true,
       "requires": {
-        "icss-utils": "^5.1.0",
-        "postcss": "^8.2.15",
-        "postcss-modules-extract-imports": "^3.0.0",
-        "postcss-modules-local-by-default": "^4.0.0",
-        "postcss-modules-scope": "^3.0.0",
-        "postcss-modules-values": "^4.0.0",
-        "postcss-value-parser": "^4.1.0",
-        "semver": "^7.3.5"
+        "color-support": "^1.1.3"
       }
     },
-    "css-minimizer-webpack-plugin": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-3.0.2.tgz",
-      "integrity": "sha512-B3I5e17RwvKPJwsxjjWcdgpU/zqylzK1bPVghcmpFHRL48DXiBgrtqz1BJsn68+t/zzaLp9kYAaEDvQ7GyanFQ==",
+    "fast-deep-equal": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+      "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
+    },
+    "fast-glob": {
+      "version": "3.2.11",
+      "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz",
+      "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==",
       "dev": true,
       "requires": {
-        "cssnano": "^5.0.6",
-        "jest-worker": "^27.0.2",
-        "p-limit": "^3.0.2",
-        "postcss": "^8.3.5",
-        "schema-utils": "^3.0.0",
-        "serialize-javascript": "^6.0.0",
-        "source-map": "^0.6.1"
+        "@nodelib/fs.stat": "^2.0.2",
+        "@nodelib/fs.walk": "^1.2.3",
+        "glob-parent": "^5.1.2",
+        "merge2": "^1.3.0",
+        "micromatch": "^4.0.4"
       },
       "dependencies": {
-        "p-limit": {
-          "version": "3.1.0",
-          "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
-          "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+        "glob-parent": {
+          "version": "5.1.2",
+          "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+          "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
           "dev": true,
           "requires": {
-            "yocto-queue": "^0.1.0"
+            "is-glob": "^4.0.1"
           }
-        },
-        "source-map": {
-          "version": "0.6.1",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
-          "dev": true
         }
       }
     },
-    "css-parse": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/css-parse/-/css-parse-2.0.0.tgz",
-      "integrity": "sha1-pGjuZnwW2BzPBcWMONKpfHgNv9Q=",
+    "fast-json-stable-stringify": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+      "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="
+    },
+    "fast-levenshtein": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+      "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
+      "dev": true
+    },
+    "fastq": {
+      "version": "1.13.0",
+      "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz",
+      "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==",
+      "dev": true,
+      "requires": {
+        "reusify": "^1.0.4"
+      }
+    },
+    "fault": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/fault/-/fault-1.0.4.tgz",
+      "integrity": "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==",
+      "dev": true,
+      "requires": {
+        "format": "^0.2.0"
+      }
+    },
+    "faye-websocket": {
+      "version": "0.11.4",
+      "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz",
+      "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==",
+      "dev": true,
+      "requires": {
+        "websocket-driver": ">=0.5.1"
+      }
+    },
+    "fb-watchman": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz",
+      "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==",
+      "dev": true,
+      "requires": {
+        "bser": "2.1.1"
+      }
+    },
+    "fetch-retry": {
+      "version": "5.0.2",
+      "resolved": "https://registry.npmjs.org/fetch-retry/-/fetch-retry-5.0.2.tgz",
+      "integrity": "sha512-57Hmu+1kc6pKFUGVIobT7qw3NeAzY/uNN26bSevERLVvf6VGFR/ooDCOFBHMNDgAxBiU2YJq1D0vFzc6U1DcPw==",
+      "dev": true
+    },
+    "figgy-pudding": {
+      "version": "3.5.2",
+      "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz",
+      "integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==",
+      "dev": true
+    },
+    "figures": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz",
+      "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==",
+      "dev": true,
+      "requires": {
+        "escape-string-regexp": "^1.0.5"
+      }
+    },
+    "file-entry-cache": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
+      "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
+      "dev": true,
+      "requires": {
+        "flat-cache": "^3.0.4"
+      }
+    },
+    "file-loader": {
+      "version": "6.2.0",
+      "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz",
+      "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==",
+      "requires": {
+        "loader-utils": "^2.0.0",
+        "schema-utils": "^3.0.0"
+      }
+    },
+    "file-system-cache": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/file-system-cache/-/file-system-cache-1.1.0.tgz",
+      "integrity": "sha512-IzF5MBq+5CR0jXx5RxPe4BICl/oEhBSXKaL9fLhAXrIfIUS77Hr4vzrYyqYMHN6uTt+BOqi3fDCTjjEBCjERKw==",
       "dev": true,
       "requires": {
-        "css": "^2.0.0"
+        "fs-extra": "^10.1.0",
+        "ramda": "^0.28.0"
       }
     },
-    "css-prefers-color-scheme": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-3.1.1.tgz",
-      "integrity": "sha512-MTu6+tMs9S3EUqzmqLXEcgNRbNkkD/TGFvowpeoWJn5Vfq7FMgsmRQs9X5NXAURiOBmOxm/lLjsDNXDE6k9bhg==",
+    "fill-range": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+      "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
       "dev": true,
       "requires": {
-        "postcss": "^7.0.5"
+        "to-regex-range": "^5.0.1"
+      }
+    },
+    "finalhandler": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
+      "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
+      "dev": true,
+      "requires": {
+        "debug": "2.6.9",
+        "encodeurl": "~1.0.2",
+        "escape-html": "~1.0.3",
+        "on-finished": "2.4.1",
+        "parseurl": "~1.3.3",
+        "statuses": "2.0.1",
+        "unpipe": "~1.0.0"
       },
       "dependencies": {
-        "picocolors": {
-          "version": "0.2.1",
-          "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz",
-          "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==",
-          "dev": true
-        },
-        "postcss": {
-          "version": "7.0.39",
-          "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz",
-          "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==",
+        "debug": {
+          "version": "2.6.9",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
           "dev": true,
           "requires": {
-            "picocolors": "^0.2.1",
-            "source-map": "^0.6.1"
+            "ms": "2.0.0"
           }
         },
-        "source-map": {
-          "version": "0.6.1",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+        "ms": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
           "dev": true
         }
       }
     },
-    "css-select": {
-      "version": "4.1.3",
-      "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.1.3.tgz",
-      "integrity": "sha512-gT3wBNd9Nj49rAbmtFHj1cljIAOLYSX1nZ8CB7TBO3INYckygm5B7LISU/szY//YmdiSLbJvDLOx9VnMVpMBxA==",
+    "find-cache-dir": {
+      "version": "3.3.1",
+      "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz",
+      "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==",
       "dev": true,
       "requires": {
-        "boolbase": "^1.0.0",
-        "css-what": "^5.0.0",
-        "domhandler": "^4.2.0",
-        "domutils": "^2.6.0",
-        "nth-check": "^2.0.0"
+        "commondir": "^1.0.1",
+        "make-dir": "^3.0.2",
+        "pkg-dir": "^4.1.0"
       }
     },
-    "css-tree": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz",
-      "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==",
+    "find-up": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+      "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
       "dev": true,
       "requires": {
-        "mdn-data": "2.0.14",
-        "source-map": "^0.6.1"
-      },
-      "dependencies": {
-        "source-map": {
-          "version": "0.6.1",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
-          "dev": true
-        }
+        "locate-path": "^5.0.0",
+        "path-exists": "^4.0.0"
       }
     },
-    "css-what": {
-      "version": "5.1.0",
-      "resolved": "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz",
-      "integrity": "sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw==",
+    "findit2": {
+      "version": "2.2.3",
+      "resolved": "https://registry.npmjs.org/findit2/-/findit2-2.2.3.tgz",
+      "integrity": "sha1-WKRmaX34piBc39vzlVNri9d3pfY=",
       "dev": true
     },
-    "cssdb": {
-      "version": "4.4.0",
-      "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-4.4.0.tgz",
-      "integrity": "sha512-LsTAR1JPEM9TpGhl/0p3nQecC2LJ0kD8X5YARu1hk/9I1gril5vDtMZyNxcEpxxDj34YNck/ucjuoUd66K03oQ==",
+    "flat-cache": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz",
+      "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==",
+      "dev": true,
+      "requires": {
+        "flatted": "^3.1.0",
+        "rimraf": "^3.0.2"
+      }
+    },
+    "flatted": {
+      "version": "3.2.5",
+      "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz",
+      "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==",
       "dev": true
     },
-    "cssesc": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
-      "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
+    "flatten": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/flatten/-/flatten-1.0.3.tgz",
+      "integrity": "sha512-dVsPA/UwQ8+2uoFe5GHtiBMu48dWLTdsuEd7CKGlZlD78r1TTWBvDuFaFGKCo/ZfEr95Uk56vZoX86OsHkUeIg==",
       "dev": true
     },
-    "cssnano": {
-      "version": "5.0.12",
-      "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.0.12.tgz",
-      "integrity": "sha512-U38V4x2iJ3ijPdeWqUrEr4eKBB5PbEKsNP5T8xcik2Au3LeMtiMHX0i2Hu9k51FcKofNZumbrcdC6+a521IUHg==",
+    "flush-write-stream": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz",
+      "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==",
       "dev": true,
       "requires": {
-        "cssnano-preset-default": "^5.1.8",
-        "is-resolvable": "^1.1.0",
-        "lilconfig": "^2.0.3",
-        "yaml": "^1.10.2"
-      }
-    },
-    "cssnano-preset-default": {
-      "version": "5.1.8",
-      "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.1.8.tgz",
-      "integrity": "sha512-zWMlP0+AMPBVE852SqTrP0DnhTcTA2C1wAF92TKZ3Va+aUVqLIhkqKlnJIXXdqXD7RN+S1ujuWmNpvrJBiM/vg==",
-      "dev": true,
-      "requires": {
-        "css-declaration-sorter": "^6.0.3",
-        "cssnano-utils": "^2.0.1",
-        "postcss-calc": "^8.0.0",
-        "postcss-colormin": "^5.2.1",
-        "postcss-convert-values": "^5.0.2",
-        "postcss-discard-comments": "^5.0.1",
-        "postcss-discard-duplicates": "^5.0.1",
-        "postcss-discard-empty": "^5.0.1",
-        "postcss-discard-overridden": "^5.0.1",
-        "postcss-merge-longhand": "^5.0.4",
-        "postcss-merge-rules": "^5.0.3",
-        "postcss-minify-font-values": "^5.0.1",
-        "postcss-minify-gradients": "^5.0.3",
-        "postcss-minify-params": "^5.0.2",
-        "postcss-minify-selectors": "^5.1.0",
-        "postcss-normalize-charset": "^5.0.1",
-        "postcss-normalize-display-values": "^5.0.1",
-        "postcss-normalize-positions": "^5.0.1",
-        "postcss-normalize-repeat-style": "^5.0.1",
-        "postcss-normalize-string": "^5.0.1",
-        "postcss-normalize-timing-functions": "^5.0.1",
-        "postcss-normalize-unicode": "^5.0.1",
-        "postcss-normalize-url": "^5.0.3",
-        "postcss-normalize-whitespace": "^5.0.1",
-        "postcss-ordered-values": "^5.0.2",
-        "postcss-reduce-initial": "^5.0.2",
-        "postcss-reduce-transforms": "^5.0.1",
-        "postcss-svgo": "^5.0.3",
-        "postcss-unique-selectors": "^5.0.2"
+        "inherits": "^2.0.3",
+        "readable-stream": "^2.3.6"
       }
     },
-    "cssnano-utils": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-2.0.1.tgz",
-      "integrity": "sha512-i8vLRZTnEH9ubIyfdZCAdIdgnHAUeQeByEeQ2I7oTilvP9oHO6RScpeq3GsFUVqeB8uZgOQ9pw8utofNn32hhQ==",
-      "dev": true
-    },
-    "csso": {
-      "version": "4.2.0",
-      "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz",
-      "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==",
+    "focus-lock": {
+      "version": "0.8.1",
+      "resolved": "https://registry.npmjs.org/focus-lock/-/focus-lock-0.8.1.tgz",
+      "integrity": "sha512-/LFZOIo82WDsyyv7h7oc0MJF9ACOvDRdx9rWPZ2pgMfNWu/z8hQDBtOchuB/0BVLmuFOZjV02YwUVzNsWx/EzA==",
       "dev": true,
       "requires": {
-        "css-tree": "^1.1.2"
+        "tslib": "^1.9.3"
+      },
+      "dependencies": {
+        "tslib": {
+          "version": "1.14.1",
+          "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+          "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+          "dev": true
+        }
       }
     },
-    "custom-event": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz",
-      "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=",
+    "follow-redirects": {
+      "version": "1.15.0",
+      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.0.tgz",
+      "integrity": "sha512-aExlJShTV4qOUOL7yF1U5tvLCB0xQuudbf6toyYA0E/acBNw71mvjFTnLaRp50aQaYocMR0a/RMMBIHeZnGyjQ==",
+      "dev": true
+    },
+    "for-in": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
+      "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=",
       "dev": true
     },
-    "dashdash": {
-      "version": "1.14.1",
-      "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
-      "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
+    "fork-ts-checker-webpack-plugin": {
+      "version": "6.5.2",
+      "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.2.tgz",
+      "integrity": "sha512-m5cUmF30xkZ7h4tWUgTAcEaKmUW7tfyUyTqNNOz7OxWJ0v1VWKTcOvH8FWHUwSjlW/356Ijc9vi3XfcPstpQKA==",
       "dev": true,
       "requires": {
-        "assert-plus": "^1.0.0"
+        "@babel/code-frame": "^7.8.3",
+        "@types/json-schema": "^7.0.5",
+        "chalk": "^4.1.0",
+        "chokidar": "^3.4.2",
+        "cosmiconfig": "^6.0.0",
+        "deepmerge": "^4.2.2",
+        "fs-extra": "^9.0.0",
+        "glob": "^7.1.6",
+        "memfs": "^3.1.2",
+        "minimatch": "^3.0.4",
+        "schema-utils": "2.7.0",
+        "semver": "^7.3.2",
+        "tapable": "^1.0.0"
+      },
+      "dependencies": {
+        "ansi-styles": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+          "dev": true,
+          "requires": {
+            "color-convert": "^2.0.1"
+          }
+        },
+        "chalk": {
+          "version": "4.1.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+          "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "dev": true,
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
+        },
+        "cosmiconfig": {
+          "version": "6.0.0",
+          "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz",
+          "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==",
+          "dev": true,
+          "requires": {
+            "@types/parse-json": "^4.0.0",
+            "import-fresh": "^3.1.0",
+            "parse-json": "^5.0.0",
+            "path-type": "^4.0.0",
+            "yaml": "^1.7.2"
+          }
+        },
+        "fs-extra": {
+          "version": "9.1.0",
+          "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
+          "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
+          "dev": true,
+          "requires": {
+            "at-least-node": "^1.0.0",
+            "graceful-fs": "^4.2.0",
+            "jsonfile": "^6.0.1",
+            "universalify": "^2.0.0"
+          }
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
+        "schema-utils": {
+          "version": "2.7.0",
+          "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz",
+          "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==",
+          "dev": true,
+          "requires": {
+            "@types/json-schema": "^7.0.4",
+            "ajv": "^6.12.2",
+            "ajv-keywords": "^3.4.1"
+          }
+        },
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
+        },
+        "tapable": {
+          "version": "1.1.3",
+          "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",
+          "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==",
+          "dev": true
+        }
       }
     },
-    "date-format": {
-      "version": "4.0.3",
-      "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.3.tgz",
-      "integrity": "sha512-7P3FyqDcfeznLZp2b+OMitV9Sz2lUnsT87WaTat9nVwqsBkTzPG3lPLNwW3en6F4pHUiWzr6vb8CLhjdK9bcxQ==",
-      "dev": true
-    },
-    "debug": {
-      "version": "4.3.3",
-      "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz",
-      "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==",
+    "form-data": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz",
+      "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==",
       "dev": true,
       "requires": {
-        "ms": "2.1.2"
+        "asynckit": "^0.4.0",
+        "combined-stream": "^1.0.8",
+        "mime-types": "^2.1.12"
       }
     },
-    "decamelize": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
-      "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
+    "format": {
+      "version": "0.2.2",
+      "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz",
+      "integrity": "sha1-1hcBB+nv3E7TDJ3DkBbflCtctYs=",
       "dev": true
     },
-    "decode-uri-component": {
+    "forwarded": {
       "version": "0.2.0",
-      "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
-      "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=",
+      "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
+      "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
       "dev": true
     },
-    "deep-equal": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz",
-      "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==",
+    "fragment-cache": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
+      "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=",
       "dev": true,
       "requires": {
-        "is-arguments": "^1.0.4",
-        "is-date-object": "^1.0.1",
-        "is-regex": "^1.0.4",
-        "object-is": "^1.0.1",
-        "object-keys": "^1.1.1",
-        "regexp.prototype.flags": "^1.2.0"
+        "map-cache": "^0.2.2"
       }
     },
-    "deep-is": {
-      "version": "0.1.4",
-      "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
-      "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+    "fresh": {
+      "version": "0.5.2",
+      "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+      "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=",
+      "dev": true
+    },
+    "from": {
+      "version": "0.1.7",
+      "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz",
+      "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=",
       "dev": true
     },
-    "default-gateway": {
-      "version": "4.2.0",
-      "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz",
-      "integrity": "sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA==",
+    "from2": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz",
+      "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=",
       "dev": true,
       "requires": {
-        "execa": "^1.0.0",
-        "ip-regex": "^2.1.0"
+        "inherits": "^2.0.1",
+        "readable-stream": "^2.0.0"
       }
     },
-    "defaults": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz",
-      "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=",
+    "fs-extra": {
+      "version": "10.1.0",
+      "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
+      "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
       "dev": true,
       "requires": {
-        "clone": "^1.0.2"
+        "graceful-fs": "^4.2.0",
+        "jsonfile": "^6.0.1",
+        "universalify": "^2.0.0"
       }
     },
-    "define-lazy-prop": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz",
-      "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==",
-      "dev": true
-    },
-    "define-properties": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
-      "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
+    "fs-minipass": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
+      "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
       "dev": true,
       "requires": {
-        "object-keys": "^1.0.12"
+        "minipass": "^3.0.0"
       }
     },
-    "define-property": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz",
-      "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==",
-      "dev": true,
-      "requires": {
-        "is-descriptor": "^1.0.2",
-        "isobject": "^3.0.1"
-      },
-      "dependencies": {
-        "is-accessor-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
-          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
-          "dev": true,
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-data-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
-          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
-          "dev": true,
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-descriptor": {
-          "version": "1.0.2",
-          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
-          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
-          "dev": true,
-          "requires": {
-            "is-accessor-descriptor": "^1.0.0",
-            "is-data-descriptor": "^1.0.0",
-            "kind-of": "^6.0.2"
-          }
-        }
-      }
+    "fs-monkey": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz",
+      "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==",
+      "dev": true
     },
-    "del": {
-      "version": "4.1.1",
-      "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz",
-      "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==",
+    "fs-write-stream-atomic": {
+      "version": "1.0.10",
+      "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz",
+      "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=",
       "dev": true,
       "requires": {
-        "@types/glob": "^7.1.1",
-        "globby": "^6.1.0",
-        "is-path-cwd": "^2.0.0",
-        "is-path-in-cwd": "^2.0.0",
-        "p-map": "^2.0.0",
-        "pify": "^4.0.1",
-        "rimraf": "^2.6.3"
-      },
-      "dependencies": {
-        "array-union": {
-          "version": "1.0.2",
-          "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
-          "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=",
-          "dev": true,
-          "requires": {
-            "array-uniq": "^1.0.1"
-          }
-        },
-        "globby": {
-          "version": "6.1.0",
-          "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz",
-          "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=",
-          "dev": true,
-          "requires": {
-            "array-union": "^1.0.1",
-            "glob": "^7.0.3",
-            "object-assign": "^4.0.1",
-            "pify": "^2.0.0",
-            "pinkie-promise": "^2.0.0"
-          },
-          "dependencies": {
-            "pify": {
-              "version": "2.3.0",
-              "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
-              "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
-              "dev": true
-            }
-          }
-        },
-        "p-map": {
-          "version": "2.1.0",
-          "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz",
-          "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==",
-          "dev": true
-        },
-        "rimraf": {
-          "version": "2.7.1",
-          "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
-          "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
-          "dev": true,
-          "requires": {
-            "glob": "^7.1.3"
-          }
-        }
+        "graceful-fs": "^4.1.2",
+        "iferr": "^0.1.5",
+        "imurmurhash": "^0.1.4",
+        "readable-stream": "1 || 2"
       }
     },
-    "delayed-stream": {
+    "fs.realpath": {
       "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
-      "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
+      "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+      "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
       "dev": true
     },
-    "delegates": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
-      "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=",
+    "fsevents": {
+      "version": "2.3.2",
+      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+      "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+      "dev": true,
+      "optional": true
+    },
+    "function-bind": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+      "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
       "dev": true
     },
-    "depd": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
-      "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=",
+    "function.prototype.name": {
+      "version": "1.1.5",
+      "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz",
+      "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==",
+      "dev": true,
+      "requires": {
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.3",
+        "es-abstract": "^1.19.0",
+        "functions-have-names": "^1.2.2"
+      }
+    },
+    "functional-red-black-tree": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
+      "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
       "dev": true
     },
-    "dependency-graph": {
-      "version": "0.11.0",
-      "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz",
-      "integrity": "sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==",
+    "functions-have-names": {
+      "version": "1.2.3",
+      "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
+      "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",
       "dev": true
     },
-    "destroy": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
-      "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=",
+    "gauge": {
+      "version": "4.0.4",
+      "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz",
+      "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==",
+      "dev": true,
+      "requires": {
+        "aproba": "^1.0.3 || ^2.0.0",
+        "color-support": "^1.1.3",
+        "console-control-strings": "^1.1.0",
+        "has-unicode": "^2.0.1",
+        "signal-exit": "^3.0.7",
+        "string-width": "^4.2.3",
+        "strip-ansi": "^6.0.1",
+        "wide-align": "^1.1.5"
+      }
+    },
+    "gensync": {
+      "version": "1.0.0-beta.2",
+      "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
+      "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
       "dev": true
     },
-    "detect-node": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz",
-      "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==",
+    "get-assigned-identifiers": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/get-assigned-identifiers/-/get-assigned-identifiers-1.2.0.tgz",
+      "integrity": "sha512-mBBwmeGTrxEMO4pMaaf/uUEFHnYtwr8FTe8Y/mer4rcV/bye0qGm6pw1bGZFGStxC5O76c5ZAVBGnqHmOaJpdQ==",
       "dev": true
     },
-    "di": {
-      "version": "0.0.1",
-      "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz",
-      "integrity": "sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw=",
+    "get-caller-file": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+      "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
       "dev": true
     },
-    "dir-glob": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
-      "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
+    "get-intrinsic": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz",
+      "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==",
       "dev": true,
       "requires": {
-        "path-type": "^4.0.0"
+        "function-bind": "^1.1.1",
+        "has": "^1.0.3",
+        "has-symbols": "^1.0.1"
       }
     },
-    "dns-equal": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz",
-      "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=",
+    "get-package-type": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz",
+      "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==",
       "dev": true
     },
-    "dns-packet": {
-      "version": "1.3.4",
-      "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.4.tgz",
-      "integrity": "sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==",
+    "get-stdin": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz",
+      "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=",
+      "dev": true,
+      "optional": true
+    },
+    "get-stream": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
+      "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
       "dev": true,
       "requires": {
-        "ip": "^1.1.0",
-        "safe-buffer": "^5.0.1"
+        "pump": "^3.0.0"
       }
     },
-    "dns-txt": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz",
-      "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=",
+    "get-symbol-description": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz",
+      "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==",
       "dev": true,
       "requires": {
-        "buffer-indexof": "^1.0.0"
+        "call-bind": "^1.0.2",
+        "get-intrinsic": "^1.1.1"
       }
     },
-    "doctrine": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
-      "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+    "get-value": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
+      "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=",
+      "dev": true
+    },
+    "github-slugger": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.4.0.tgz",
+      "integrity": "sha512-w0dzqw/nt51xMVmlaV1+JRzN+oCa1KfcgGEWhxUG16wbdA+Xnt/yoFO8Z8x/V82ZcZ0wy6ln9QDup5avbhiDhQ==",
+      "dev": true
+    },
+    "glob": {
+      "version": "7.1.7",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz",
+      "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==",
       "dev": true,
       "requires": {
-        "esutils": "^2.0.2"
+        "fs.realpath": "^1.0.0",
+        "inflight": "^1.0.4",
+        "inherits": "2",
+        "minimatch": "^3.0.4",
+        "once": "^1.3.0",
+        "path-is-absolute": "^1.0.0"
       }
     },
-    "dom-serialize": {
-      "version": "2.2.1",
-      "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz",
-      "integrity": "sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs=",
+    "glob-parent": {
+      "version": "6.0.2",
+      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+      "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
       "dev": true,
       "requires": {
-        "custom-event": "~1.0.0",
-        "ent": "~2.2.0",
-        "extend": "^3.0.0",
-        "void-elements": "^2.0.0"
+        "is-glob": "^4.0.3"
       }
     },
-    "dom-serializer": {
-      "version": "1.3.2",
-      "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz",
-      "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==",
+    "glob-promise": {
+      "version": "3.4.0",
+      "resolved": "https://registry.npmjs.org/glob-promise/-/glob-promise-3.4.0.tgz",
+      "integrity": "sha512-q08RJ6O+eJn+dVanerAndJwIcumgbDdYiUT7zFQl3Wm1xD6fBKtah7H8ZJChj4wP+8C+QfeVy8xautR7rdmKEw==",
       "dev": true,
       "requires": {
-        "domelementtype": "^2.0.1",
-        "domhandler": "^4.2.0",
-        "entities": "^2.0.0"
+        "@types/glob": "*"
       }
     },
-    "domelementtype": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz",
-      "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==",
+    "glob-to-regexp": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz",
+      "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==",
       "dev": true
     },
-    "domhandler": {
-      "version": "4.2.2",
-      "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.2.2.tgz",
-      "integrity": "sha512-PzE9aBMsdZO8TK4BnuJwH0QT41wgMbRzuZrHUcpYncEjmQazq8QEaBWgLG7ZyC/DAZKEgglpIA6j4Qn/HmxS3w==",
+    "global": {
+      "version": "4.4.0",
+      "resolved": "https://registry.npmjs.org/global/-/global-4.4.0.tgz",
+      "integrity": "sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==",
       "dev": true,
       "requires": {
-        "domelementtype": "^2.2.0"
+        "min-document": "^2.19.0",
+        "process": "^0.11.10"
       }
     },
-    "domutils": {
-      "version": "2.8.0",
-      "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
-      "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
+    "globals": {
+      "version": "11.12.0",
+      "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
+      "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
+      "dev": true
+    },
+    "globalthis": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz",
+      "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==",
       "dev": true,
       "requires": {
-        "dom-serializer": "^1.0.1",
-        "domelementtype": "^2.2.0",
-        "domhandler": "^4.2.0"
+        "define-properties": "^1.1.3"
       }
     },
-    "ecc-jsbn": {
-      "version": "0.1.2",
-      "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
-      "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
+    "globby": {
+      "version": "11.1.0",
+      "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
+      "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
       "dev": true,
       "requires": {
-        "jsbn": "~0.1.0",
-        "safer-buffer": "^2.1.0"
+        "array-union": "^2.1.0",
+        "dir-glob": "^3.0.1",
+        "fast-glob": "^3.2.9",
+        "ignore": "^5.2.0",
+        "merge2": "^1.4.1",
+        "slash": "^3.0.0"
       }
     },
-    "ee-first": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
-      "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=",
+    "graceful-fs": {
+      "version": "4.2.10",
+      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
+      "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==",
       "dev": true
     },
-    "electron-to-chromium": {
-      "version": "1.4.5",
-      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.5.tgz",
-      "integrity": "sha512-YKaB+t8ul5crdh6OeqT2qXdxJGI0fAYb6/X8pDIyye+c3a7ndOCk5gVeKX+ABwivCGNS56vOAif3TN0qJMpEHw==",
+    "handle-thing": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz",
+      "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==",
       "dev": true
     },
-    "emoji-regex": {
-      "version": "7.0.3",
-      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
-      "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
-      "dev": true
+    "handlebars": {
+      "version": "4.7.7",
+      "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz",
+      "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==",
+      "dev": true,
+      "requires": {
+        "minimist": "^1.2.5",
+        "neo-async": "^2.6.0",
+        "source-map": "^0.6.1",
+        "uglify-js": "^3.1.4",
+        "wordwrap": "^1.0.0"
+      },
+      "dependencies": {
+        "source-map": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+          "dev": true
+        }
+      }
     },
-    "emojis-list": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz",
-      "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q=="
+    "has": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+      "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+      "dev": true,
+      "requires": {
+        "function-bind": "^1.1.1"
+      }
     },
-    "encodeurl": {
+    "has-bigints": {
       "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
-      "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=",
+      "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz",
+      "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==",
       "dev": true
     },
-    "encoding": {
-      "version": "0.1.13",
-      "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz",
-      "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==",
+    "has-flag": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+      "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+      "dev": true
+    },
+    "has-glob": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/has-glob/-/has-glob-1.0.0.tgz",
+      "integrity": "sha1-mqqe7b/7G6OZCnsAEPtnjuAIEgc=",
       "dev": true,
-      "optional": true,
       "requires": {
-        "iconv-lite": "^0.6.2"
+        "is-glob": "^3.0.0"
       },
       "dependencies": {
-        "iconv-lite": {
-          "version": "0.6.3",
-          "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
-          "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+        "is-glob": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
+          "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
           "dev": true,
-          "optional": true,
           "requires": {
-            "safer-buffer": ">= 2.1.2 < 3.0.0"
+            "is-extglob": "^2.1.0"
           }
         }
       }
     },
-    "end-of-stream": {
-      "version": "1.4.4",
-      "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
-      "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
+    "has-property-descriptors": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz",
+      "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==",
       "dev": true,
       "requires": {
-        "once": "^1.4.0"
+        "get-intrinsic": "^1.1.1"
       }
     },
-    "engine.io": {
-      "version": "6.1.3",
-      "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.1.3.tgz",
-      "integrity": "sha512-rqs60YwkvWTLLnfazqgZqLa/aKo+9cueVfEi/dZ8PyGyaf8TLOxj++4QMIgeG3Gn0AhrWiFXvghsoY9L9h25GA==",
+    "has-symbols": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
+      "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
+      "dev": true
+    },
+    "has-tostringtag": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz",
+      "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==",
       "dev": true,
       "requires": {
-        "@types/cookie": "^0.4.1",
-        "@types/cors": "^2.8.12",
-        "@types/node": ">=10.0.0",
-        "accepts": "~1.3.4",
-        "base64id": "2.0.0",
-        "cookie": "~0.4.1",
-        "cors": "~2.8.5",
-        "debug": "~4.3.1",
-        "engine.io-parser": "~5.0.3",
-        "ws": "~8.2.3"
-      },
-      "dependencies": {
-        "cookie": {
-          "version": "0.4.2",
-          "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
-          "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==",
-          "dev": true
-        },
-        "ws": {
-          "version": "8.2.3",
-          "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz",
-          "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==",
-          "dev": true
-        }
+        "has-symbols": "^1.0.2"
       }
     },
-    "engine.io-parser": {
-      "version": "5.0.3",
-      "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.3.tgz",
-      "integrity": "sha512-BtQxwF27XUNnSafQLvDi0dQ8s3i6VgzSoQMJacpIcGNrlUdfHSKbgm3jmjCVvQluGzqwujQMPAoMai3oYSTurg==",
+    "has-unicode": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
+      "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=",
+      "dev": true
+    },
+    "has-value": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz",
+      "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=",
       "dev": true,
       "requires": {
-        "@socket.io/base64-arraybuffer": "~1.0.2"
+        "get-value": "^2.0.6",
+        "has-values": "^1.0.0",
+        "isobject": "^3.0.0"
       }
     },
-    "enhanced-resolve": {
-      "version": "5.8.3",
-      "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.8.3.tgz",
-      "integrity": "sha512-EGAbGvH7j7Xt2nc0E7D99La1OiEs8LnyimkRgwExpUMScN6O+3x9tIWs7PLQZVNx4YD+00skHXPXi1yQHpAmZA==",
+    "has-values": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz",
+      "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=",
       "dev": true,
       "requires": {
-        "graceful-fs": "^4.2.4",
-        "tapable": "^2.2.0"
+        "is-number": "^3.0.0",
+        "kind-of": "^4.0.0"
+      },
+      "dependencies": {
+        "is-number": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
+          "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
+          "dev": true,
+          "requires": {
+            "kind-of": "^3.0.2"
+          },
+          "dependencies": {
+            "kind-of": {
+              "version": "3.2.2",
+              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+              "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+              "dev": true,
+              "requires": {
+                "is-buffer": "^1.1.5"
+              }
+            }
+          }
+        },
+        "kind-of": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz",
+          "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=",
+          "dev": true,
+          "requires": {
+            "is-buffer": "^1.1.5"
+          }
+        }
       }
     },
-    "enquirer": {
-      "version": "2.3.6",
-      "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz",
-      "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==",
+    "hash-base": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz",
+      "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==",
       "dev": true,
       "requires": {
-        "ansi-colors": "^4.1.1"
-      }
-    },
-    "ent": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz",
-      "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=",
-      "dev": true
-    },
-    "entities": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
-      "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
-      "dev": true
-    },
-    "env-paths": {
-      "version": "2.2.1",
-      "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
-      "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
-      "dev": true
-    },
-    "err-code": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz",
-      "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==",
-      "dev": true
+        "inherits": "^2.0.4",
+        "readable-stream": "^3.6.0",
+        "safe-buffer": "^5.2.0"
+      },
+      "dependencies": {
+        "readable-stream": {
+          "version": "3.6.0",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+          "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+          "dev": true,
+          "requires": {
+            "inherits": "^2.0.3",
+            "string_decoder": "^1.1.1",
+            "util-deprecate": "^1.0.1"
+          }
+        },
+        "safe-buffer": {
+          "version": "5.2.1",
+          "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+          "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+          "dev": true
+        }
+      }
     },
-    "errno": {
-      "version": "0.1.8",
-      "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz",
-      "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==",
+    "hash.js": {
+      "version": "1.1.7",
+      "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz",
+      "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==",
       "dev": true,
       "requires": {
-        "prr": "~1.0.1"
+        "inherits": "^2.0.3",
+        "minimalistic-assert": "^1.0.1"
       }
     },
-    "error-ex": {
-      "version": "1.3.2",
-      "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
-      "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+    "hast-to-hyperscript": {
+      "version": "9.0.1",
+      "resolved": "https://registry.npmjs.org/hast-to-hyperscript/-/hast-to-hyperscript-9.0.1.tgz",
+      "integrity": "sha512-zQgLKqF+O2F72S1aa4y2ivxzSlko3MAvxkwG8ehGmNiqd98BIN3JM1rAJPmplEyLmGLO2QZYJtIneOSZ2YbJuA==",
       "dev": true,
       "requires": {
-        "is-arrayish": "^0.2.1"
+        "@types/unist": "^2.0.3",
+        "comma-separated-tokens": "^1.0.0",
+        "property-information": "^5.3.0",
+        "space-separated-tokens": "^1.0.0",
+        "style-to-object": "^0.3.0",
+        "unist-util-is": "^4.0.0",
+        "web-namespaces": "^1.0.0"
       }
     },
-    "es-module-lexer": {
-      "version": "0.7.1",
-      "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.7.1.tgz",
-      "integrity": "sha512-MgtWFl5No+4S3TmhDmCz2ObFGm6lEpTnzbQi+Dd+pw4mlTIZTmM2iAs5gRlmx5zS9luzobCSBSI90JM/1/JgOw==",
-      "dev": true
-    },
-    "esbuild-android-arm64": {
-      "version": "0.13.8",
-      "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.13.8.tgz",
-      "integrity": "sha512-AilbChndywpk7CdKkNSZ9klxl+9MboLctXd9LwLo3b0dawmOF/i/t2U5d8LM6SbT1Xw36F8yngSUPrd8yPs2RA==",
-      "dev": true,
-      "optional": true
-    },
-    "esbuild-darwin-64": {
-      "version": "0.13.8",
-      "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.13.8.tgz",
-      "integrity": "sha512-b6sdiT84zV5LVaoF+UoMVGJzR/iE2vNUfUDfFQGrm4LBwM/PWXweKpuu6RD9mcyCq18cLxkP6w/LD/w9DtX3ng==",
-      "dev": true,
-      "optional": true
-    },
-    "esbuild-darwin-arm64": {
-      "version": "0.13.8",
-      "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.13.8.tgz",
-      "integrity": "sha512-R8YuPiiJayuJJRUBG4H0VwkEKo6AvhJs2m7Tl0JaIer3u1FHHXwGhMxjJDmK+kXwTFPriSysPvcobXC/UrrZCQ==",
-      "dev": true,
-      "optional": true
-    },
-    "esbuild-freebsd-64": {
-      "version": "0.13.8",
-      "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.13.8.tgz",
-      "integrity": "sha512-zBn6urrn8FnKC+YSgDxdof9jhPCeU8kR/qaamlV4gI8R3KUaUK162WYM7UyFVAlj9N0MyD3AtB+hltzu4cysTw==",
-      "dev": true,
-      "optional": true
-    },
-    "esbuild-freebsd-arm64": {
-      "version": "0.13.8",
-      "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.13.8.tgz",
-      "integrity": "sha512-pWW2slN7lGlkx0MOEBoUGwRX5UgSCLq3dy2c8RIOpiHtA87xAUpDBvZK10MykbT+aMfXc0NI2lu1X+6kI34xng==",
-      "dev": true,
-      "optional": true
-    },
-    "esbuild-linux-32": {
-      "version": "0.13.8",
-      "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.13.8.tgz",
-      "integrity": "sha512-T0I0ueeKVO/Is0CAeSEOG9s2jeNNb8jrrMwG9QBIm3UU18MRB60ERgkS2uV3fZ1vP2F8i3Z2e3Zju4lg9dhVmw==",
-      "dev": true,
-      "optional": true
-    },
-    "esbuild-linux-64": {
-      "version": "0.13.8",
-      "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.13.8.tgz",
-      "integrity": "sha512-Bm8SYmFtvfDCIu9sjKppFXzRXn2BVpuCinU1ChTuMtdKI/7aPpXIrkqBNOgPTOQO9AylJJc1Zw6EvtKORhn64w==",
+    "hast-util-from-parse5": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-6.0.1.tgz",
+      "integrity": "sha512-jeJUWiN5pSxW12Rh01smtVkZgZr33wBokLzKLwinYOUfSzm1Nl/c3GUGebDyOKjdsRgMvoVbV0VpAcpjF4NrJA==",
       "dev": true,
-      "optional": true
+      "requires": {
+        "@types/parse5": "^5.0.0",
+        "hastscript": "^6.0.0",
+        "property-information": "^5.0.0",
+        "vfile": "^4.0.0",
+        "vfile-location": "^3.2.0",
+        "web-namespaces": "^1.0.0"
+      }
     },
-    "esbuild-linux-arm": {
-      "version": "0.13.8",
-      "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.13.8.tgz",
-      "integrity": "sha512-4/HfcC40LJ4GPyboHA+db0jpFarTB628D1ifU+/5bunIgY+t6mHkJWyxWxAAE8wl/ZIuRYB9RJFdYpu1AXGPdg==",
-      "dev": true,
-      "optional": true
+    "hast-util-parse-selector": {
+      "version": "2.2.5",
+      "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz",
+      "integrity": "sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==",
+      "dev": true
     },
-    "esbuild-linux-arm64": {
-      "version": "0.13.8",
-      "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.13.8.tgz",
-      "integrity": "sha512-X4pWZ+SL+FJ09chWFgRNO3F+YtvAQRcWh0uxKqZSWKiWodAB20flsW/OWFYLXBKiVCTeoGMvENZS/GeVac7+tQ==",
-      "dev": true,
-      "optional": true
+    "hast-util-raw": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-6.0.1.tgz",
+      "integrity": "sha512-ZMuiYA+UF7BXBtsTBNcLBF5HzXzkyE6MLzJnL605LKE8GJylNjGc4jjxazAHUtcwT5/CEt6afRKViYB4X66dig==",
+      "dev": true,
+      "requires": {
+        "@types/hast": "^2.0.0",
+        "hast-util-from-parse5": "^6.0.0",
+        "hast-util-to-parse5": "^6.0.0",
+        "html-void-elements": "^1.0.0",
+        "parse5": "^6.0.0",
+        "unist-util-position": "^3.0.0",
+        "vfile": "^4.0.0",
+        "web-namespaces": "^1.0.0",
+        "xtend": "^4.0.0",
+        "zwitch": "^1.0.0"
+      },
+      "dependencies": {
+        "parse5": {
+          "version": "6.0.1",
+          "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
+          "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==",
+          "dev": true
+        }
+      }
     },
-    "esbuild-linux-mips64le": {
-      "version": "0.13.8",
-      "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.13.8.tgz",
-      "integrity": "sha512-o7e0D+sqHKT31v+mwFircJFjwSKVd2nbkHEn4l9xQ1hLR+Bv8rnt3HqlblY3+sBdlrOTGSwz0ReROlKUMJyldA==",
+    "hast-util-to-parse5": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-6.0.0.tgz",
+      "integrity": "sha512-Lu5m6Lgm/fWuz8eWnrKezHtVY83JeRGaNQ2kn9aJgqaxvVkFCZQBEhgodZUDUvoodgyROHDb3r5IxAEdl6suJQ==",
       "dev": true,
-      "optional": true
+      "requires": {
+        "hast-to-hyperscript": "^9.0.0",
+        "property-information": "^5.0.0",
+        "web-namespaces": "^1.0.0",
+        "xtend": "^4.0.0",
+        "zwitch": "^1.0.0"
+      }
     },
-    "esbuild-linux-ppc64le": {
-      "version": "0.13.8",
-      "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.13.8.tgz",
-      "integrity": "sha512-eZSQ0ERsWkukJp2px/UWJHVNuy0lMoz/HZcRWAbB6reoaBw7S9vMzYNUnflfL3XA6WDs+dZn3ekHE4Y2uWLGig==",
+    "hastscript": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-6.0.0.tgz",
+      "integrity": "sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==",
       "dev": true,
-      "optional": true
+      "requires": {
+        "@types/hast": "^2.0.0",
+        "comma-separated-tokens": "^1.0.0",
+        "hast-util-parse-selector": "^2.0.0",
+        "property-information": "^5.0.0",
+        "space-separated-tokens": "^1.0.0"
+      }
     },
-    "esbuild-netbsd-64": {
-      "version": "0.13.8",
-      "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.13.8.tgz",
-      "integrity": "sha512-gZX4kP7gVvOrvX0ZwgHmbuHczQUwqYppxqtoyC7VNd80t5nBHOFXVhWo2Ad/Lms0E8b+wwgI/WjZFTCpUHOg9Q==",
+    "hdr-histogram-js": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/hdr-histogram-js/-/hdr-histogram-js-2.0.3.tgz",
+      "integrity": "sha512-Hkn78wwzWHNCp2uarhzQ2SGFLU3JY8SBDDd3TAABK4fc30wm+MuPOrg5QVFVfkKOQd6Bfz3ukJEI+q9sXEkK1g==",
       "dev": true,
-      "optional": true
+      "requires": {
+        "@assemblyscript/loader": "^0.10.1",
+        "base64-js": "^1.2.0",
+        "pako": "^1.0.3"
+      }
     },
-    "esbuild-openbsd-64": {
-      "version": "0.13.8",
-      "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.13.8.tgz",
-      "integrity": "sha512-afzza308X4WmcebexbTzAgfEWt9MUkdTvwIa8xOu4CM2qGbl2LanqEl8/LUs8jh6Gqw6WsicEK52GPrS9wvkcw==",
-      "dev": true,
-      "optional": true
+    "hdr-histogram-percentiles-obj": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/hdr-histogram-percentiles-obj/-/hdr-histogram-percentiles-obj-3.0.0.tgz",
+      "integrity": "sha512-7kIufnBqdsBGcSZLPJwqHT3yhk1QTsSlFsVD3kx5ixH/AlgBs9yM1q6DPhXZ8f8gtdqgh7N7/5btRLpQsS2gHw==",
+      "dev": true
     },
-    "esbuild-sunos-64": {
-      "version": "0.13.8",
-      "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.13.8.tgz",
-      "integrity": "sha512-mWPZibmBbuMKD+LDN23LGcOZ2EawMYBONMXXHmbuxeT0XxCNwadbCVwUQ/2p5Dp5Kvf6mhrlIffcnWOiCBpiVw==",
-      "dev": true,
-      "optional": true
+    "he": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
+      "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
+      "dev": true
     },
-    "esbuild-wasm": {
-      "version": "0.13.8",
-      "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.13.8.tgz",
-      "integrity": "sha512-UbD+3nloiSpJWXTCInZQrqPe8Y+RLfDkY/5kEHiXsw/lmaEvibe69qTzQu16m5R9je/0bF7VYQ5jaEOq0z9lLA==",
+    "highlight.js": {
+      "version": "10.7.3",
+      "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz",
+      "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==",
       "dev": true
     },
-    "esbuild-windows-32": {
-      "version": "0.13.8",
-      "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.13.8.tgz",
-      "integrity": "sha512-QsZ1HnWIcnIEApETZWw8HlOhDSWqdZX2SylU7IzGxOYyVcX7QI06ety/aDcn437mwyO7Ph4RrbhB+2ntM8kX8A==",
+    "hmac-drbg": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
+      "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=",
       "dev": true,
-      "optional": true
+      "requires": {
+        "hash.js": "^1.0.3",
+        "minimalistic-assert": "^1.0.0",
+        "minimalistic-crypto-utils": "^1.0.1"
+      }
     },
-    "esbuild-windows-64": {
-      "version": "0.13.8",
-      "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.13.8.tgz",
-      "integrity": "sha512-76Fb57B9eE/JmJi1QmUW0tRLQZfGo0it+JeYoCDTSlbTn7LV44ecOHIMJSSgZADUtRMWT9z0Kz186bnaB3amSg==",
+    "hosted-git-info": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz",
+      "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==",
       "dev": true,
-      "optional": true
+      "requires": {
+        "lru-cache": "^6.0.0"
+      }
     },
-    "esbuild-windows-arm64": {
-      "version": "0.13.8",
-      "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.13.8.tgz",
-      "integrity": "sha512-HW6Mtq5eTudllxY2YgT62MrVcn7oq2o8TAoAvDUhyiEmRmDY8tPwAhb1vxw5/cdkbukM3KdMYtksnUhF/ekWeg==",
+    "hpack.js": {
+      "version": "2.1.6",
+      "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz",
+      "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=",
       "dev": true,
-      "optional": true
-    },
-    "escalade": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
-      "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw=="
-    },
-    "escape-html": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
-      "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=",
-      "dev": true
+      "requires": {
+        "inherits": "^2.0.1",
+        "obuf": "^1.0.0",
+        "readable-stream": "^2.0.1",
+        "wbuf": "^1.1.0"
+      }
     },
-    "escape-string-regexp": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
-      "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
+    "html-entities": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.4.0.tgz",
+      "integrity": "sha512-8nxjcBcd8wovbeKx7h3wTji4e6+rhaVuPNpMqwWgnHh+N9ToqsCs6XztWRBPQ+UtzsoMAdKZtUENoVzU/EMtZA==",
       "dev": true
     },
-    "eslint": {
-      "version": "7.32.0",
-      "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz",
-      "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==",
-      "dev": true,
-      "requires": {
-        "@babel/code-frame": "7.12.11",
-        "@eslint/eslintrc": "^0.4.3",
-        "@humanwhocodes/config-array": "^0.5.0",
-        "ajv": "^6.10.0",
-        "chalk": "^4.0.0",
-        "cross-spawn": "^7.0.2",
-        "debug": "^4.0.1",
-        "doctrine": "^3.0.0",
-        "enquirer": "^2.3.5",
-        "escape-string-regexp": "^4.0.0",
-        "eslint-scope": "^5.1.1",
-        "eslint-utils": "^2.1.0",
-        "eslint-visitor-keys": "^2.0.0",
-        "espree": "^7.3.1",
-        "esquery": "^1.4.0",
-        "esutils": "^2.0.2",
-        "fast-deep-equal": "^3.1.3",
-        "file-entry-cache": "^6.0.1",
-        "functional-red-black-tree": "^1.0.1",
-        "glob-parent": "^5.1.2",
-        "globals": "^13.6.0",
-        "ignore": "^4.0.6",
-        "import-fresh": "^3.0.0",
-        "imurmurhash": "^0.1.4",
-        "is-glob": "^4.0.0",
-        "js-yaml": "^3.13.1",
-        "json-stable-stringify-without-jsonify": "^1.0.1",
-        "levn": "^0.4.1",
-        "lodash.merge": "^4.6.2",
-        "minimatch": "^3.0.4",
-        "natural-compare": "^1.4.0",
-        "optionator": "^0.9.1",
-        "progress": "^2.0.0",
-        "regexpp": "^3.1.0",
-        "semver": "^7.2.1",
-        "strip-ansi": "^6.0.0",
-        "strip-json-comments": "^3.1.0",
-        "table": "^6.0.9",
-        "text-table": "^0.2.0",
-        "v8-compile-cache": "^2.0.3"
-      },
-      "dependencies": {
-        "@babel/code-frame": {
-          "version": "7.12.11",
-          "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz",
-          "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==",
-          "dev": true,
-          "requires": {
-            "@babel/highlight": "^7.10.4"
-          }
+    "html-escaper": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
+      "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
+      "dev": true
+    },
+    "html-minifier-terser": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz",
+      "integrity": "sha512-ZPr5MNObqnV/T9akshPKbVgyOqLmy+Bxo7juKCfTfnjNniTAMdy4hz21YQqoofMBJD2kdREaqPPdThoR78Tgxg==",
+      "dev": true,
+      "requires": {
+        "camel-case": "^4.1.1",
+        "clean-css": "^4.2.3",
+        "commander": "^4.1.1",
+        "he": "^1.2.0",
+        "param-case": "^3.0.3",
+        "relateurl": "^0.2.7",
+        "terser": "^4.6.3"
+      },
+      "dependencies": {
+        "commander": {
+          "version": "4.1.1",
+          "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
+          "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==",
+          "dev": true
         },
-        "ansi-regex": {
-          "version": "5.0.1",
-          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
-          "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+        "source-map": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
           "dev": true
         },
-        "ansi-styles": {
-          "version": "4.3.0",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
-          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+        "terser": {
+          "version": "4.8.0",
+          "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz",
+          "integrity": "sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==",
           "dev": true,
           "requires": {
-            "color-convert": "^2.0.1"
+            "commander": "^2.20.0",
+            "source-map": "~0.6.1",
+            "source-map-support": "~0.5.12"
+          },
+          "dependencies": {
+            "commander": {
+              "version": "2.20.3",
+              "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+              "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+              "dev": true
+            }
           }
-        },
-        "chalk": {
-          "version": "4.1.2",
-          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
-          "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+        }
+      }
+    },
+    "html-void-elements": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-1.0.5.tgz",
+      "integrity": "sha512-uE/TxKuyNIcx44cIWnjr/rfIATDH7ZaOMmstu0CwhFG1Dunhlp4OC6/NMbhiwoq5BpW0ubi303qnEk/PZj614w==",
+      "dev": true
+    },
+    "html-webpack-plugin": {
+      "version": "4.5.2",
+      "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-4.5.2.tgz",
+      "integrity": "sha512-q5oYdzjKUIPQVjOosjgvCHQOv9Ett9CYYHlgvJeXG0qQvdSojnBq4vAdQBwn1+yGveAwHCoe/rMR86ozX3+c2A==",
+      "dev": true,
+      "requires": {
+        "@types/html-minifier-terser": "^5.0.0",
+        "@types/tapable": "^1.0.5",
+        "@types/webpack": "^4.41.8",
+        "html-minifier-terser": "^5.0.1",
+        "loader-utils": "^1.2.3",
+        "lodash": "^4.17.20",
+        "pretty-error": "^2.1.1",
+        "tapable": "^1.1.3",
+        "util.promisify": "1.0.0"
+      },
+      "dependencies": {
+        "json5": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
+          "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
           "dev": true,
           "requires": {
-            "ansi-styles": "^4.1.0",
-            "supports-color": "^7.1.0"
+            "minimist": "^1.2.0"
           }
         },
-        "color-convert": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
-          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+        "loader-utils": {
+          "version": "1.4.0",
+          "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
+          "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==",
           "dev": true,
           "requires": {
-            "color-name": "~1.1.4"
+            "big.js": "^5.2.2",
+            "emojis-list": "^3.0.0",
+            "json5": "^1.0.1"
           }
         },
-        "color-name": {
-          "version": "1.1.4",
-          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
-          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+        "tapable": {
+          "version": "1.1.3",
+          "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",
+          "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==",
           "dev": true
-        },
-        "cross-spawn": {
-          "version": "7.0.3",
-          "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
-          "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+        }
+      }
+    },
+    "htmlparser2": {
+      "version": "8.0.1",
+      "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.1.tgz",
+      "integrity": "sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==",
+      "dev": true,
+      "requires": {
+        "domelementtype": "^2.3.0",
+        "domhandler": "^5.0.2",
+        "domutils": "^3.0.1",
+        "entities": "^4.3.0"
+      },
+      "dependencies": {
+        "dom-serializer": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
+          "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
           "dev": true,
           "requires": {
-            "path-key": "^3.1.0",
-            "shebang-command": "^2.0.0",
-            "which": "^2.0.1"
+            "domelementtype": "^2.3.0",
+            "domhandler": "^5.0.2",
+            "entities": "^4.2.0"
           }
         },
-        "escape-string-regexp": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
-          "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
-          "dev": true
-        },
-        "eslint-utils": {
-          "version": "2.1.0",
-          "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz",
-          "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==",
+        "domhandler": {
+          "version": "5.0.3",
+          "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
+          "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
           "dev": true,
           "requires": {
-            "eslint-visitor-keys": "^1.1.0"
-          },
-          "dependencies": {
-            "eslint-visitor-keys": {
-              "version": "1.3.0",
-              "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
-              "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
-              "dev": true
-            }
+            "domelementtype": "^2.3.0"
           }
         },
-        "glob-parent": {
-          "version": "5.1.2",
-          "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
-          "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+        "domutils": {
+          "version": "3.0.1",
+          "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz",
+          "integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==",
           "dev": true,
           "requires": {
-            "is-glob": "^4.0.1"
+            "dom-serializer": "^2.0.0",
+            "domelementtype": "^2.3.0",
+            "domhandler": "^5.0.1"
           }
         },
-        "globals": {
-          "version": "13.12.0",
-          "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.0.tgz",
-          "integrity": "sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg==",
+        "entities": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/entities/-/entities-4.3.0.tgz",
+          "integrity": "sha512-/iP1rZrSEJ0DTlPiX+jbzlA3eVkY/e8L8SozroF395fIqE3TYF/Nz7YOMAawta+vLmyJ/hkGNNPcSbMADCCXbg==",
+          "dev": true
+        }
+      }
+    },
+    "http-auth": {
+      "version": "4.1.9",
+      "resolved": "https://registry.npmjs.org/http-auth/-/http-auth-4.1.9.tgz",
+      "integrity": "sha512-kvPYxNGc9EKGTXvOMnTBQw2RZfuiSihK/mLw/a4pbtRueTE45S55Lw/3k5CktIf7Ak0veMKEIteDj4YkNmCzmQ==",
+      "dev": true,
+      "requires": {
+        "apache-crypt": "^1.1.2",
+        "apache-md5": "^1.0.6",
+        "bcryptjs": "^2.4.3",
+        "uuid": "^8.3.2"
+      }
+    },
+    "http-auth-connect": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/http-auth-connect/-/http-auth-connect-1.0.5.tgz",
+      "integrity": "sha512-zykAOKpVAXyzhOLm6+xyB/RtRcfN3uDfH4Al73DIfeSb6B7nr0WToLI6UyyM6ohtcLmbBPksWXzVbEDStz8ObQ==",
+      "dev": true
+    },
+    "http-cache-semantics": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz",
+      "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==",
+      "dev": true
+    },
+    "http-deceiver": {
+      "version": "1.2.7",
+      "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz",
+      "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=",
+      "dev": true
+    },
+    "http-errors": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
+      "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
+      "dev": true,
+      "requires": {
+        "depd": "2.0.0",
+        "inherits": "2.0.4",
+        "setprototypeof": "1.2.0",
+        "statuses": "2.0.1",
+        "toidentifier": "1.0.1"
+      }
+    },
+    "http-parser-js": {
+      "version": "0.5.6",
+      "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.6.tgz",
+      "integrity": "sha512-vDlkRPDJn93swjcjqMSaGSPABbIarsr1TLAui/gLDXzV5VsJNdXNzMYDyNBLQkjWQCJ1uizu8T2oDMhmGt0PRA==",
+      "dev": true
+    },
+    "http-proxy": {
+      "version": "1.18.1",
+      "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz",
+      "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==",
+      "dev": true,
+      "requires": {
+        "eventemitter3": "^4.0.0",
+        "follow-redirects": "^1.0.0",
+        "requires-port": "^1.0.0"
+      }
+    },
+    "http-proxy-agent": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz",
+      "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==",
+      "dev": true,
+      "requires": {
+        "@tootallnate/once": "1",
+        "agent-base": "6",
+        "debug": "4"
+      }
+    },
+    "http-proxy-middleware": {
+      "version": "0.19.1",
+      "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz",
+      "integrity": "sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==",
+      "dev": true,
+      "requires": {
+        "http-proxy": "^1.17.0",
+        "is-glob": "^4.0.0",
+        "lodash": "^4.17.11",
+        "micromatch": "^3.1.10"
+      },
+      "dependencies": {
+        "braces": {
+          "version": "2.3.2",
+          "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
+          "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
           "dev": true,
           "requires": {
-            "type-fest": "^0.20.2"
+            "arr-flatten": "^1.1.0",
+            "array-unique": "^0.3.2",
+            "extend-shallow": "^2.0.1",
+            "fill-range": "^4.0.0",
+            "isobject": "^3.0.1",
+            "repeat-element": "^1.1.2",
+            "snapdragon": "^0.8.1",
+            "snapdragon-node": "^2.0.1",
+            "split-string": "^3.0.2",
+            "to-regex": "^3.0.1"
+          },
+          "dependencies": {
+            "extend-shallow": {
+              "version": "2.0.1",
+              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+              "dev": true,
+              "requires": {
+                "is-extendable": "^0.1.0"
+              }
+            }
           }
         },
-        "has-flag": {
+        "fill-range": {
           "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
-          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
-          "dev": true
-        },
-        "ignore": {
-          "version": "4.0.6",
-          "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
-          "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
-          "dev": true
-        },
-        "path-key": {
-          "version": "3.1.1",
-          "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
-          "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
-          "dev": true
-        },
-        "shebang-command": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
-          "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+          "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
+          "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
           "dev": true,
           "requires": {
-            "shebang-regex": "^3.0.0"
+            "extend-shallow": "^2.0.1",
+            "is-number": "^3.0.0",
+            "repeat-string": "^1.6.1",
+            "to-regex-range": "^2.1.0"
+          },
+          "dependencies": {
+            "extend-shallow": {
+              "version": "2.0.1",
+              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+              "dev": true,
+              "requires": {
+                "is-extendable": "^0.1.0"
+              }
+            }
           }
         },
-        "shebang-regex": {
+        "is-number": {
           "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
-          "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
-          "dev": true
-        },
-        "strip-ansi": {
-          "version": "6.0.1",
-          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
-          "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+          "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
+          "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
           "dev": true,
           "requires": {
-            "ansi-regex": "^5.0.1"
+            "kind-of": "^3.0.2"
+          },
+          "dependencies": {
+            "kind-of": {
+              "version": "3.2.2",
+              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+              "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+              "dev": true,
+              "requires": {
+                "is-buffer": "^1.1.5"
+              }
+            }
           }
         },
-        "supports-color": {
-          "version": "7.2.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
-          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+        "micromatch": {
+          "version": "3.1.10",
+          "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
+          "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
           "dev": true,
           "requires": {
-            "has-flag": "^4.0.0"
+            "arr-diff": "^4.0.0",
+            "array-unique": "^0.3.2",
+            "braces": "^2.3.1",
+            "define-property": "^2.0.2",
+            "extend-shallow": "^3.0.2",
+            "extglob": "^2.0.4",
+            "fragment-cache": "^0.2.1",
+            "kind-of": "^6.0.2",
+            "nanomatch": "^1.2.9",
+            "object.pick": "^1.3.0",
+            "regex-not": "^1.0.0",
+            "snapdragon": "^0.8.1",
+            "to-regex": "^3.0.2"
           }
         },
-        "type-fest": {
-          "version": "0.20.2",
-          "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
-          "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
-          "dev": true
-        },
-        "which": {
-          "version": "2.0.2",
-          "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
-          "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+        "to-regex-range": {
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
+          "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
           "dev": true,
           "requires": {
-            "isexe": "^2.0.0"
+            "is-number": "^3.0.0",
+            "repeat-string": "^1.6.1"
           }
         }
       }
     },
-    "eslint-scope": {
-      "version": "5.1.1",
-      "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
-      "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
-      "dev": true,
-      "requires": {
-        "esrecurse": "^4.3.0",
-        "estraverse": "^4.1.1"
-      }
+    "https-browserify": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz",
+      "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=",
+      "dev": true
     },
-    "eslint-utils": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz",
-      "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==",
+    "https-proxy-agent": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz",
+      "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==",
       "dev": true,
       "requires": {
-        "eslint-visitor-keys": "^2.0.0"
+        "agent-base": "6",
+        "debug": "4"
       }
     },
-    "eslint-visitor-keys": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
-      "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
+    "human-signals": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz",
+      "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==",
       "dev": true
     },
-    "espree": {
-      "version": "7.3.1",
-      "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz",
-      "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==",
+    "humanize-ms": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz",
+      "integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=",
       "dev": true,
       "requires": {
-        "acorn": "^7.4.0",
-        "acorn-jsx": "^5.3.1",
-        "eslint-visitor-keys": "^1.3.0"
-      },
-      "dependencies": {
-        "acorn": {
-          "version": "7.4.1",
-          "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
-          "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
-          "dev": true
-        },
-        "eslint-visitor-keys": {
-          "version": "1.3.0",
-          "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
-          "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
-          "dev": true
-        }
+        "ms": "^2.0.0"
       }
     },
-    "esprima": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
-      "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
-      "dev": true
-    },
-    "esquery": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz",
-      "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==",
+    "i18next": {
+      "version": "21.8.4",
+      "resolved": "https://registry.npmjs.org/i18next/-/i18next-21.8.4.tgz",
+      "integrity": "sha512-b3LQ5n9V1juu8UItb5x1QTI4OTvNqsNs/wetwQlBvfijEqks+N5HKMKSoevf8w0/RGUrDQ7g4cvVzF8WBp9pUw==",
       "dev": true,
       "requires": {
-        "estraverse": "^5.1.0"
+        "@babel/runtime": "^7.17.2"
       },
       "dependencies": {
-        "estraverse": {
-          "version": "5.3.0",
-          "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
-          "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
-          "dev": true
+        "@babel/runtime": {
+          "version": "7.18.0",
+          "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.0.tgz",
+          "integrity": "sha512-YMQvx/6nKEaucl0MY56mwIG483xk8SDNdlUwb2Ts6FUpr7fm85DxEmsY18LXBNhcTz6tO6JwZV8w1W06v8UKeg==",
+          "dev": true,
+          "requires": {
+            "regenerator-runtime": "^0.13.4"
+          }
         }
       }
     },
-    "esrecurse": {
-      "version": "4.3.0",
-      "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
-      "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+    "iconv-lite": {
+      "version": "0.4.24",
+      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+      "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
       "dev": true,
       "requires": {
-        "estraverse": "^5.2.0"
-      },
-      "dependencies": {
-        "estraverse": {
-          "version": "5.3.0",
-          "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
-          "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
-          "dev": true
-        }
+        "safer-buffer": ">= 2.1.2 < 3"
       }
     },
-    "estraverse": {
-      "version": "4.3.0",
-      "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
-      "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
-      "dev": true
-    },
-    "esutils": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
-      "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
-      "dev": true
-    },
-    "etag": {
-      "version": "1.8.1",
-      "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
-      "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=",
+    "icss-utils": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz",
+      "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==",
       "dev": true
     },
-    "eventemitter-asyncresource": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/eventemitter-asyncresource/-/eventemitter-asyncresource-1.0.0.tgz",
-      "integrity": "sha512-39F7TBIV0G7gTelxwbEqnwhp90eqCPON1k0NwNfwhgKn4Co4ybUbj2pECcXT0B3ztRKZ7Pw1JujUUgmQJHcVAQ==",
+    "ieee754": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+      "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
       "dev": true
     },
-    "eventemitter3": {
-      "version": "4.0.7",
-      "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
-      "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
+    "iferr": {
+      "version": "0.1.5",
+      "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz",
+      "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=",
       "dev": true
     },
-    "events": {
-      "version": "3.3.0",
-      "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
-      "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
+    "ignore": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz",
+      "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==",
       "dev": true
     },
-    "eventsource": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.1.0.tgz",
-      "integrity": "sha512-VSJjT5oCNrFvCS6igjzPAt5hBzQ2qPBFIbJ03zLI9SE0mxwZpMw6BfJrbFHm1a141AavMEB8JHmBhWAd66PfCg==",
+    "ignore-walk": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-4.0.1.tgz",
+      "integrity": "sha512-rzDQLaW4jQbh2YrOFlJdCtX8qgJTehFRYiUB2r1osqTeDzV/3+Jh8fz1oAPzUThf3iku8Ds4IDqawI5d8mUiQw==",
       "dev": true,
       "requires": {
-        "original": "^1.0.0"
+        "minimatch": "^3.0.4"
       }
     },
-    "execa": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz",
-      "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==",
+    "image-size": {
+      "version": "0.5.5",
+      "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz",
+      "integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=",
       "dev": true,
-      "requires": {
-        "cross-spawn": "^6.0.0",
-        "get-stream": "^4.0.0",
-        "is-stream": "^1.1.0",
-        "npm-run-path": "^2.0.0",
-        "p-finally": "^1.0.0",
-        "signal-exit": "^3.0.0",
-        "strip-eof": "^1.0.0"
-      }
+      "optional": true
     },
-    "expand-brackets": {
-      "version": "2.1.4",
-      "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
-      "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=",
-      "dev": true,
-      "requires": {
-        "debug": "^2.3.3",
-        "define-property": "^0.2.5",
-        "extend-shallow": "^2.0.1",
-        "posix-character-classes": "^0.1.0",
-        "regex-not": "^1.0.0",
-        "snapdragon": "^0.8.1",
-        "to-regex": "^3.0.1"
-      },
-      "dependencies": {
-        "debug": {
-          "version": "2.6.9",
-          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
-          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
-          "dev": true,
-          "requires": {
-            "ms": "2.0.0"
-          }
-        },
-        "define-property": {
-          "version": "0.2.5",
-          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
-          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
-          "dev": true,
-          "requires": {
-            "is-descriptor": "^0.1.0"
-          }
-        },
-        "extend-shallow": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-          "dev": true,
-          "requires": {
-            "is-extendable": "^0.1.0"
-          }
-        },
-        "ms": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
-          "dev": true
-        }
-      }
+    "immediate": {
+      "version": "3.0.6",
+      "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
+      "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps="
     },
-    "export-nehuba": {
-      "version": "0.0.12",
-      "resolved": "https://registry.npmjs.org/export-nehuba/-/export-nehuba-0.0.12.tgz",
-      "integrity": "sha512-pf3hAwpXaOqlfBfgmPLYQ+uLqJ+ElyvE1bDrrCrf5Qf0Otsekw+8CcyAJhP5O15Yacmhe7Py3G96tw5bbvZyIA==",
+    "import-fresh": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
+      "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+      "dev": true,
       "requires": {
-        "pako": "^1.0.6"
+        "parent-module": "^1.0.0",
+        "resolve-from": "^4.0.0"
       }
     },
-    "express": {
-      "version": "4.17.1",
-      "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
-      "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
+    "import-local": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz",
+      "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==",
       "dev": true,
       "requires": {
-        "accepts": "~1.3.7",
-        "array-flatten": "1.1.1",
-        "body-parser": "1.19.0",
-        "content-disposition": "0.5.3",
-        "content-type": "~1.0.4",
-        "cookie": "0.4.0",
-        "cookie-signature": "1.0.6",
-        "debug": "2.6.9",
-        "depd": "~1.1.2",
-        "encodeurl": "~1.0.2",
-        "escape-html": "~1.0.3",
-        "etag": "~1.8.1",
-        "finalhandler": "~1.1.2",
-        "fresh": "0.5.2",
-        "merge-descriptors": "1.0.1",
-        "methods": "~1.1.2",
-        "on-finished": "~2.3.0",
-        "parseurl": "~1.3.3",
-        "path-to-regexp": "0.1.7",
-        "proxy-addr": "~2.0.5",
-        "qs": "6.7.0",
-        "range-parser": "~1.2.1",
-        "safe-buffer": "5.1.2",
-        "send": "0.17.1",
-        "serve-static": "1.14.1",
-        "setprototypeof": "1.1.1",
-        "statuses": "~1.5.0",
-        "type-is": "~1.6.18",
-        "utils-merge": "1.0.1",
-        "vary": "~1.1.2"
+        "pkg-dir": "^3.0.0",
+        "resolve-cwd": "^2.0.0"
       },
       "dependencies": {
-        "array-flatten": {
-          "version": "1.1.1",
-          "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
-          "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=",
-          "dev": true
+        "find-up": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
+          "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+          "dev": true,
+          "requires": {
+            "locate-path": "^3.0.0"
+          }
         },
-        "debug": {
-          "version": "2.6.9",
-          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
-          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+        "locate-path": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
+          "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
           "dev": true,
           "requires": {
-            "ms": "2.0.0"
+            "p-locate": "^3.0.0",
+            "path-exists": "^3.0.0"
           }
         },
-        "ms": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+        "p-locate": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
+          "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+          "dev": true,
+          "requires": {
+            "p-limit": "^2.0.0"
+          }
+        },
+        "path-exists": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+          "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
           "dev": true
-        }
-      }
-    },
-    "extend": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
-      "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
-      "dev": true
-    },
-    "extend-shallow": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
-      "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
-      "dev": true,
-      "requires": {
-        "assign-symbols": "^1.0.0",
-        "is-extendable": "^1.0.1"
-      },
-      "dependencies": {
-        "is-extendable": {
-          "version": "1.0.1",
-          "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
-          "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+        },
+        "pkg-dir": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz",
+          "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==",
           "dev": true,
           "requires": {
-            "is-plain-object": "^2.0.4"
+            "find-up": "^3.0.0"
           }
         }
       }
     },
-    "external-editor": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz",
-      "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==",
+    "imurmurhash": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+      "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
+      "dev": true
+    },
+    "indent-string": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
+      "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
+      "dev": true
+    },
+    "indexes-of": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz",
+      "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=",
+      "dev": true
+    },
+    "infer-owner": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz",
+      "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==",
+      "dev": true
+    },
+    "inflight": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+      "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
       "dev": true,
       "requires": {
-        "chardet": "^0.7.0",
-        "iconv-lite": "^0.4.24",
-        "tmp": "^0.0.33"
+        "once": "^1.3.0",
+        "wrappy": "1"
       }
     },
-    "extglob": {
+    "inherits": {
       "version": "2.0.4",
-      "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
-      "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
+      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+    },
+    "ini": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz",
+      "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==",
+      "dev": true
+    },
+    "inline-style-parser": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz",
+      "integrity": "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==",
+      "dev": true
+    },
+    "inquirer": {
+      "version": "8.1.2",
+      "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.1.2.tgz",
+      "integrity": "sha512-DHLKJwLPNgkfwNmsuEUKSejJFbkv0FMO9SMiQbjI3n5NQuCrSIBqP66ggqyz2a6t2qEolKrMjhQ3+W/xXgUQ+Q==",
       "dev": true,
       "requires": {
-        "array-unique": "^0.3.2",
-        "define-property": "^1.0.0",
-        "expand-brackets": "^2.1.4",
-        "extend-shallow": "^2.0.1",
-        "fragment-cache": "^0.2.1",
-        "regex-not": "^1.0.0",
-        "snapdragon": "^0.8.1",
-        "to-regex": "^3.0.1"
+        "ansi-escapes": "^4.2.1",
+        "chalk": "^4.1.1",
+        "cli-cursor": "^3.1.0",
+        "cli-width": "^3.0.0",
+        "external-editor": "^3.0.3",
+        "figures": "^3.0.0",
+        "lodash": "^4.17.21",
+        "mute-stream": "0.0.8",
+        "ora": "^5.3.0",
+        "run-async": "^2.4.0",
+        "rxjs": "^7.2.0",
+        "string-width": "^4.1.0",
+        "strip-ansi": "^6.0.0",
+        "through": "^2.3.6"
       },
       "dependencies": {
-        "define-property": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
-          "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+        "ansi-styles": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
           "dev": true,
           "requires": {
-            "is-descriptor": "^1.0.0"
+            "color-convert": "^2.0.1"
           }
         },
-        "extend-shallow": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+        "chalk": {
+          "version": "4.1.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+          "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
           "dev": true,
           "requires": {
-            "is-extendable": "^0.1.0"
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
           }
         },
-        "is-accessor-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
-          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
           "dev": true,
           "requires": {
-            "kind-of": "^6.0.0"
+            "color-name": "~1.1.4"
           }
         },
-        "is-data-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
-          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
+        "rxjs": {
+          "version": "7.5.5",
+          "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.5.tgz",
+          "integrity": "sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw==",
           "dev": true,
           "requires": {
-            "kind-of": "^6.0.0"
+            "tslib": "^2.1.0"
           }
         },
-        "is-descriptor": {
-          "version": "1.0.2",
-          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
-          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
           "dev": true,
           "requires": {
-            "is-accessor-descriptor": "^1.0.0",
-            "is-data-descriptor": "^1.0.0",
-            "kind-of": "^6.0.2"
+            "has-flag": "^4.0.0"
           }
         }
       }
     },
-    "extsprintf": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
-      "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=",
+    "inside": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/inside/-/inside-1.0.0.tgz",
+      "integrity": "sha1-20Xpk1c82z23C5gy6ChbrUZCR3A=",
+      "dev": true
+    },
+    "internal-ip": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz",
+      "integrity": "sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg==",
+      "dev": true,
+      "requires": {
+        "default-gateway": "^4.2.0",
+        "ipaddr.js": "^1.9.0"
+      }
+    },
+    "internal-slot": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz",
+      "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==",
+      "dev": true,
+      "requires": {
+        "get-intrinsic": "^1.1.0",
+        "has": "^1.0.3",
+        "side-channel": "^1.0.4"
+      }
+    },
+    "interpret": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz",
+      "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==",
+      "dev": true
+    },
+    "ip": {
+      "version": "1.1.8",
+      "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz",
+      "integrity": "sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg==",
+      "dev": true
+    },
+    "ip-regex": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz",
+      "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=",
+      "dev": true
+    },
+    "ipaddr.js": {
+      "version": "1.9.1",
+      "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+      "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
       "dev": true
     },
-    "fast-deep-equal": {
-      "version": "3.1.3",
-      "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
-      "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
+    "is-absolute-url": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz",
+      "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==",
+      "dev": true
     },
-    "fast-glob": {
-      "version": "3.2.7",
-      "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz",
-      "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==",
+    "is-accessor-descriptor": {
+      "version": "0.1.6",
+      "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+      "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
       "dev": true,
       "requires": {
-        "@nodelib/fs.stat": "^2.0.2",
-        "@nodelib/fs.walk": "^1.2.3",
-        "glob-parent": "^5.1.2",
-        "merge2": "^1.3.0",
-        "micromatch": "^4.0.4"
+        "kind-of": "^3.0.2"
       },
       "dependencies": {
-        "glob-parent": {
-          "version": "5.1.2",
-          "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
-          "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
           "dev": true,
           "requires": {
-            "is-glob": "^4.0.1"
+            "is-buffer": "^1.1.5"
           }
         }
       }
     },
-    "fast-json-stable-stringify": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
-      "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="
-    },
-    "fast-levenshtein": {
-      "version": "2.0.6",
-      "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
-      "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
+    "is-alphabetical": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz",
+      "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==",
       "dev": true
     },
-    "fastq": {
-      "version": "1.13.0",
-      "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz",
-      "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==",
+    "is-alphanumerical": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz",
+      "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==",
       "dev": true,
       "requires": {
-        "reusify": "^1.0.4"
+        "is-alphabetical": "^1.0.0",
+        "is-decimal": "^1.0.0"
       }
     },
-    "faye-websocket": {
-      "version": "0.11.4",
-      "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz",
-      "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==",
+    "is-arguments": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz",
+      "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==",
       "dev": true,
       "requires": {
-        "websocket-driver": ">=0.5.1"
+        "call-bind": "^1.0.2",
+        "has-tostringtag": "^1.0.0"
       }
     },
-    "figures": {
-      "version": "3.2.0",
-      "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz",
-      "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==",
+    "is-arrayish": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+      "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
+      "dev": true
+    },
+    "is-bigint": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz",
+      "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==",
       "dev": true,
       "requires": {
-        "escape-string-regexp": "^1.0.5"
+        "has-bigints": "^1.0.1"
       }
     },
-    "file-entry-cache": {
-      "version": "6.0.1",
-      "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
-      "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
+    "is-binary-path": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+      "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
       "dev": true,
       "requires": {
-        "flat-cache": "^3.0.4"
+        "binary-extensions": "^2.0.0"
       }
     },
-    "file-loader": {
-      "version": "6.2.0",
-      "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz",
-      "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==",
+    "is-boolean-object": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz",
+      "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==",
+      "dev": true,
       "requires": {
-        "loader-utils": "^2.0.0",
-        "schema-utils": "^3.0.0"
+        "call-bind": "^1.0.2",
+        "has-tostringtag": "^1.0.0"
       }
     },
-    "fill-range": {
-      "version": "7.0.1",
-      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
-      "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+    "is-buffer": {
+      "version": "1.1.6",
+      "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
+      "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
+      "dev": true
+    },
+    "is-callable": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz",
+      "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==",
+      "dev": true
+    },
+    "is-ci": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz",
+      "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==",
       "dev": true,
       "requires": {
-        "to-regex-range": "^5.0.1"
+        "ci-info": "^2.0.0"
       }
     },
-    "finalhandler": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
-      "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
+    "is-core-module": {
+      "version": "2.9.0",
+      "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz",
+      "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==",
       "dev": true,
       "requires": {
-        "debug": "2.6.9",
-        "encodeurl": "~1.0.2",
-        "escape-html": "~1.0.3",
-        "on-finished": "~2.3.0",
-        "parseurl": "~1.3.3",
-        "statuses": "~1.5.0",
-        "unpipe": "~1.0.0"
+        "has": "^1.0.3"
+      }
+    },
+    "is-data-descriptor": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+      "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
+      "dev": true,
+      "requires": {
+        "kind-of": "^3.0.2"
       },
       "dependencies": {
-        "debug": {
-          "version": "2.6.9",
-          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
-          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
           "dev": true,
           "requires": {
-            "ms": "2.0.0"
+            "is-buffer": "^1.1.5"
           }
-        },
-        "ms": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+        }
+      }
+    },
+    "is-date-object": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz",
+      "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==",
+      "dev": true,
+      "requires": {
+        "has-tostringtag": "^1.0.0"
+      }
+    },
+    "is-decimal": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz",
+      "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==",
+      "dev": true
+    },
+    "is-descriptor": {
+      "version": "0.1.6",
+      "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+      "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+      "dev": true,
+      "requires": {
+        "is-accessor-descriptor": "^0.1.6",
+        "is-data-descriptor": "^0.1.4",
+        "kind-of": "^5.0.0"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "5.1.0",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+          "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
           "dev": true
         }
       }
     },
-    "find-cache-dir": {
-      "version": "3.3.1",
-      "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz",
-      "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==",
+    "is-docker": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
+      "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==",
+      "dev": true
+    },
+    "is-dom": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/is-dom/-/is-dom-1.1.0.tgz",
+      "integrity": "sha512-u82f6mvhYxRPKpw8V1N0W8ce1xXwOrQtgGcxl6UCL5zBmZu3is/18K0rR7uFCnMDuAsS/3W54mGL4vsaFUQlEQ==",
+      "dev": true,
+      "requires": {
+        "is-object": "^1.0.1",
+        "is-window": "^1.0.2"
+      }
+    },
+    "is-extendable": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+      "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=",
+      "dev": true
+    },
+    "is-extglob": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+      "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+      "dev": true
+    },
+    "is-finite": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz",
+      "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==",
+      "dev": true,
+      "optional": true
+    },
+    "is-fullwidth-code-point": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+      "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+      "dev": true
+    },
+    "is-function": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.2.tgz",
+      "integrity": "sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ==",
+      "dev": true
+    },
+    "is-glob": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+      "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+      "dev": true,
+      "requires": {
+        "is-extglob": "^2.1.1"
+      }
+    },
+    "is-hexadecimal": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz",
+      "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==",
+      "dev": true
+    },
+    "is-interactive": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz",
+      "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==",
+      "dev": true
+    },
+    "is-lambda": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz",
+      "integrity": "sha1-PZh3iZ5qU+/AFgUEzeFfgubwYdU=",
+      "dev": true
+    },
+    "is-map": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz",
+      "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==",
+      "dev": true
+    },
+    "is-negative-zero": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz",
+      "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==",
+      "dev": true
+    },
+    "is-number": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+      "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+      "dev": true
+    },
+    "is-number-object": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz",
+      "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==",
+      "dev": true,
+      "requires": {
+        "has-tostringtag": "^1.0.0"
+      }
+    },
+    "is-object": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.2.tgz",
+      "integrity": "sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA==",
+      "dev": true
+    },
+    "is-path-cwd": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz",
+      "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==",
+      "dev": true
+    },
+    "is-path-in-cwd": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz",
+      "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==",
       "dev": true,
       "requires": {
-        "commondir": "^1.0.1",
-        "make-dir": "^3.0.2",
-        "pkg-dir": "^4.1.0"
+        "is-path-inside": "^2.1.0"
       }
     },
-    "find-up": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
-      "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+    "is-path-inside": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz",
+      "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==",
       "dev": true,
       "requires": {
-        "locate-path": "^3.0.0"
+        "path-is-inside": "^1.0.2"
       }
     },
-    "flat-cache": {
-      "version": "3.0.4",
-      "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz",
-      "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==",
+    "is-plain-obj": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz",
+      "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==",
+      "dev": true
+    },
+    "is-plain-object": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
+      "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
       "dev": true,
       "requires": {
-        "flatted": "^3.1.0",
-        "rimraf": "^3.0.2"
+        "isobject": "^3.0.1"
       }
     },
-    "flatted": {
-      "version": "3.2.4",
-      "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.4.tgz",
-      "integrity": "sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw==",
-      "dev": true
-    },
-    "flatten": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/flatten/-/flatten-1.0.3.tgz",
-      "integrity": "sha512-dVsPA/UwQ8+2uoFe5GHtiBMu48dWLTdsuEd7CKGlZlD78r1TTWBvDuFaFGKCo/ZfEr95Uk56vZoX86OsHkUeIg==",
-      "dev": true
+    "is-regex": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz",
+      "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==",
+      "dev": true,
+      "requires": {
+        "call-bind": "^1.0.2",
+        "has-tostringtag": "^1.0.0"
+      }
     },
-    "follow-redirects": {
-      "version": "1.14.9",
-      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz",
-      "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==",
+    "is-set": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz",
+      "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==",
       "dev": true
     },
-    "for-in": {
+    "is-shared-array-buffer": {
       "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
-      "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=",
-      "dev": true
+      "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz",
+      "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==",
+      "dev": true,
+      "requires": {
+        "call-bind": "^1.0.2"
+      }
     },
-    "forever-agent": {
-      "version": "0.6.1",
-      "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
-      "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=",
+    "is-stream": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
+      "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=",
       "dev": true
     },
-    "form-data": {
-      "version": "2.3.3",
-      "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
-      "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
+    "is-string": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz",
+      "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==",
       "dev": true,
       "requires": {
-        "asynckit": "^0.4.0",
-        "combined-stream": "^1.0.6",
-        "mime-types": "^2.1.12"
+        "has-tostringtag": "^1.0.0"
       }
     },
-    "forwarded": {
-      "version": "0.2.0",
-      "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
-      "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
-      "dev": true
-    },
-    "fragment-cache": {
-      "version": "0.2.1",
-      "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
-      "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=",
+    "is-symbol": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz",
+      "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==",
       "dev": true,
       "requires": {
-        "map-cache": "^0.2.2"
+        "has-symbols": "^1.0.2"
       }
     },
-    "fresh": {
-      "version": "0.5.2",
-      "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
-      "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=",
+    "is-typedarray": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
+      "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
       "dev": true
     },
-    "fs-extra": {
-      "version": "10.0.1",
-      "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.1.tgz",
-      "integrity": "sha512-NbdoVMZso2Lsrn/QwLXOy6rm0ufY2zEOKCDzJR/0kBsb0E6qed0P3iYK+Ath3BfvXEeu4JhEtXLgILx5psUfag==",
+    "is-unicode-supported": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
+      "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==",
+      "dev": true
+    },
+    "is-utf8": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
+      "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=",
       "dev": true,
-      "requires": {
-        "graceful-fs": "^4.2.0",
-        "jsonfile": "^6.0.1",
-        "universalify": "^2.0.0"
-      }
+      "optional": true
     },
-    "fs-minipass": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
-      "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
+    "is-weakref": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz",
+      "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==",
       "dev": true,
       "requires": {
-        "minipass": "^3.0.0"
+        "call-bind": "^1.0.2"
       }
     },
-    "fs-monkey": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz",
-      "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==",
+    "is-what": {
+      "version": "3.14.1",
+      "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz",
+      "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==",
       "dev": true
     },
-    "fs.realpath": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
-      "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+    "is-whitespace-character": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/is-whitespace-character/-/is-whitespace-character-1.0.4.tgz",
+      "integrity": "sha512-SDweEzfIZM0SJV0EUga669UTKlmL0Pq8Lno0QDQsPnvECB3IM2aP0gdx5TrU0A01MAPfViaZiI2V1QMZLaKK5w==",
       "dev": true
     },
-    "fsevents": {
-      "version": "2.3.2",
-      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
-      "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
-      "dev": true,
-      "optional": true
+    "is-window": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/is-window/-/is-window-1.0.2.tgz",
+      "integrity": "sha1-LIlspT25feRdPDMTOmXYyfVjSA0=",
+      "dev": true
     },
-    "function-bind": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
-      "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
+    "is-windows": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
+      "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==",
       "dev": true
     },
-    "functional-red-black-tree": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
-      "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
+    "is-word-character": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/is-word-character/-/is-word-character-1.0.4.tgz",
+      "integrity": "sha512-5SMO8RVennx3nZrqtKwCGyyetPE9VDba5ugvKLaD4KopPG5kR4mQ7tNt/r7feL5yt5h3lpuBbIUmCOG2eSzXHA==",
       "dev": true
     },
-    "gauge": {
-      "version": "2.7.4",
-      "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
-      "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
+    "is-wsl": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
+      "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
       "dev": true,
       "requires": {
-        "aproba": "^1.0.3",
-        "console-control-strings": "^1.0.0",
-        "has-unicode": "^2.0.0",
-        "object-assign": "^4.1.0",
-        "signal-exit": "^3.0.0",
-        "string-width": "^1.0.1",
-        "strip-ansi": "^3.0.1",
-        "wide-align": "^1.1.0"
-      },
-      "dependencies": {
-        "ansi-regex": {
-          "version": "2.1.1",
-          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
-          "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
-          "dev": true
-        },
-        "is-fullwidth-code-point": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
-          "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
-          "dev": true,
-          "requires": {
-            "number-is-nan": "^1.0.0"
-          }
-        },
-        "string-width": {
-          "version": "1.0.2",
-          "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
-          "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
-          "dev": true,
-          "requires": {
-            "code-point-at": "^1.0.0",
-            "is-fullwidth-code-point": "^1.0.0",
-            "strip-ansi": "^3.0.0"
-          }
-        },
-        "strip-ansi": {
-          "version": "3.0.1",
-          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
-          "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
-          "dev": true,
-          "requires": {
-            "ansi-regex": "^2.0.0"
-          }
-        }
+        "is-docker": "^2.0.0"
       }
     },
-    "gensync": {
-      "version": "1.0.0-beta.2",
-      "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
-      "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+    "isarray": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+      "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
+    },
+    "isbinaryfile": {
+      "version": "4.0.10",
+      "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz",
+      "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==",
       "dev": true
     },
-    "get-caller-file": {
-      "version": "2.0.5",
-      "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
-      "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="
+    "isexe": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+      "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+      "dev": true
     },
-    "get-intrinsic": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz",
-      "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==",
+    "isobject": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+      "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
+      "dev": true
+    },
+    "isomorphic-unfetch": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/isomorphic-unfetch/-/isomorphic-unfetch-3.1.0.tgz",
+      "integrity": "sha512-geDJjpoZ8N0kWexiwkX8F9NkTsXhetLPVbZFQ+JTW239QNOwvB0gniuR1Wc6f0AMTn7/mFGyXvHTifrCp/GH8Q==",
       "dev": true,
       "requires": {
-        "function-bind": "^1.1.1",
-        "has": "^1.0.3",
-        "has-symbols": "^1.0.1"
+        "node-fetch": "^2.6.1",
+        "unfetch": "^4.2.0"
       }
     },
-    "get-stream": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
-      "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
+    "istanbul-lib-coverage": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz",
+      "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==",
+      "dev": true
+    },
+    "istanbul-lib-instrument": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz",
+      "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==",
       "dev": true,
       "requires": {
-        "pump": "^3.0.0"
+        "@babel/core": "^7.7.5",
+        "@istanbuljs/schema": "^0.1.2",
+        "istanbul-lib-coverage": "^3.0.0",
+        "semver": "^6.3.0"
+      },
+      "dependencies": {
+        "semver": {
+          "version": "6.3.0",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+          "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+          "dev": true
+        }
       }
     },
-    "get-value": {
-      "version": "2.0.6",
-      "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
-      "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=",
-      "dev": true
-    },
-    "getpass": {
-      "version": "0.1.7",
-      "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
-      "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
+    "istanbul-lib-report": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz",
+      "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==",
       "dev": true,
       "requires": {
-        "assert-plus": "^1.0.0"
+        "istanbul-lib-coverage": "^3.0.0",
+        "make-dir": "^3.0.0",
+        "supports-color": "^7.1.0"
+      },
+      "dependencies": {
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
+        }
       }
     },
-    "glob": {
-      "version": "7.1.7",
-      "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz",
-      "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==",
+    "istanbul-lib-source-maps": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz",
+      "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==",
       "dev": true,
       "requires": {
-        "fs.realpath": "^1.0.0",
-        "inflight": "^1.0.4",
-        "inherits": "2",
-        "minimatch": "^3.0.4",
-        "once": "^1.3.0",
-        "path-is-absolute": "^1.0.0"
+        "debug": "^4.1.1",
+        "istanbul-lib-coverage": "^3.0.0",
+        "source-map": "^0.6.1"
+      },
+      "dependencies": {
+        "source-map": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+          "dev": true
+        }
       }
     },
-    "glob-parent": {
-      "version": "6.0.2",
-      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
-      "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+    "istanbul-reports": {
+      "version": "3.1.4",
+      "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.4.tgz",
+      "integrity": "sha512-r1/DshN4KSE7xWEknZLLLLDn5CJybV3nw01VTkp6D5jzLuELlcbudfj/eSQFvrKsJuTVCGnePO7ho82Nw9zzfw==",
       "dev": true,
       "requires": {
-        "is-glob": "^4.0.3"
+        "html-escaper": "^2.0.0",
+        "istanbul-lib-report": "^3.0.0"
       }
     },
-    "glob-to-regexp": {
-      "version": "0.4.1",
-      "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz",
-      "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==",
-      "dev": true
-    },
-    "globals": {
-      "version": "11.12.0",
-      "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
-      "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
+    "iterate-iterator": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/iterate-iterator/-/iterate-iterator-1.0.2.tgz",
+      "integrity": "sha512-t91HubM4ZDQ70M9wqp+pcNpu8OyJ9UAtXntT/Bcsvp5tZMnz9vRa+IunKXeI8AnfZMTv0jNuVEmGeLSMjVvfPw==",
       "dev": true
     },
-    "globby": {
-      "version": "11.0.4",
-      "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz",
-      "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==",
+    "iterate-value": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/iterate-value/-/iterate-value-1.0.2.tgz",
+      "integrity": "sha512-A6fMAio4D2ot2r/TYzr4yUWrmwNdsN5xL7+HUiyACE4DXm+q8HtPcnFTp+NnW3k4N05tZ7FVYFFb2CR13NxyHQ==",
       "dev": true,
       "requires": {
-        "array-union": "^2.1.0",
-        "dir-glob": "^3.0.1",
-        "fast-glob": "^3.1.1",
-        "ignore": "^5.1.4",
-        "merge2": "^1.3.0",
-        "slash": "^3.0.0"
+        "es-get-iterator": "^1.0.2",
+        "iterate-iterator": "^1.0.1"
       }
     },
-    "graceful-fs": {
-      "version": "4.2.8",
-      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz",
-      "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==",
-      "dev": true
-    },
-    "handle-thing": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz",
-      "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==",
-      "dev": true
-    },
-    "har-schema": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
-      "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=",
+    "jasmine-core": {
+      "version": "3.8.0",
+      "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.8.0.tgz",
+      "integrity": "sha512-zl0nZWDrmbCiKns0NcjkFGYkVTGCPUgoHypTaj+G2AzaWus7QGoXARSlYsSle2VRpSdfJmM+hzmFKzQNhF2kHg==",
       "dev": true
     },
-    "har-validator": {
-      "version": "5.1.5",
-      "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz",
-      "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==",
+    "jasmine-marbles": {
+      "version": "0.8.4",
+      "resolved": "https://registry.npmjs.org/jasmine-marbles/-/jasmine-marbles-0.8.4.tgz",
+      "integrity": "sha512-zbtuXABpSWrSswYPiZ5m6EQhluNmKcRQs+82AqJHSN+PMx3ASpDmTvRfqe9Pk2hPh9Ge5zrzOsorIlw3kdwTXQ==",
       "dev": true,
       "requires": {
-        "ajv": "^6.12.3",
-        "har-schema": "^2.0.0"
+        "lodash": "^4.17.20"
       }
     },
-    "has": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
-      "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+    "jest-haste-map": {
+      "version": "26.6.2",
+      "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-26.6.2.tgz",
+      "integrity": "sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w==",
       "dev": true,
       "requires": {
-        "function-bind": "^1.1.1"
+        "@jest/types": "^26.6.2",
+        "@types/graceful-fs": "^4.1.2",
+        "@types/node": "*",
+        "anymatch": "^3.0.3",
+        "fb-watchman": "^2.0.0",
+        "fsevents": "^2.1.2",
+        "graceful-fs": "^4.2.4",
+        "jest-regex-util": "^26.0.0",
+        "jest-serializer": "^26.6.2",
+        "jest-util": "^26.6.2",
+        "jest-worker": "^26.6.2",
+        "micromatch": "^4.0.2",
+        "sane": "^4.0.3",
+        "walker": "^1.0.7"
+      },
+      "dependencies": {
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
+        "jest-worker": {
+          "version": "26.6.2",
+          "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz",
+          "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==",
+          "dev": true,
+          "requires": {
+            "@types/node": "*",
+            "merge-stream": "^2.0.0",
+            "supports-color": "^7.0.0"
+          }
+        },
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
+        }
       }
     },
-    "has-flag": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-      "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
-      "dev": true
-    },
-    "has-symbols": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz",
-      "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==",
-      "dev": true
-    },
-    "has-tostringtag": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz",
-      "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==",
+    "jest-mock": {
+      "version": "27.5.1",
+      "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.5.1.tgz",
+      "integrity": "sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==",
       "dev": true,
       "requires": {
-        "has-symbols": "^1.0.2"
+        "@jest/types": "^27.5.1",
+        "@types/node": "*"
+      },
+      "dependencies": {
+        "@jest/types": {
+          "version": "27.5.1",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz",
+          "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==",
+          "dev": true,
+          "requires": {
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^3.0.0",
+            "@types/node": "*",
+            "@types/yargs": "^16.0.0",
+            "chalk": "^4.0.0"
+          }
+        },
+        "@types/yargs": {
+          "version": "16.0.4",
+          "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz",
+          "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==",
+          "dev": true,
+          "requires": {
+            "@types/yargs-parser": "*"
+          }
+        },
+        "ansi-styles": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+          "dev": true,
+          "requires": {
+            "color-convert": "^2.0.1"
+          }
+        },
+        "chalk": {
+          "version": "4.1.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+          "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "dev": true,
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
+        }
       }
     },
-    "has-unicode": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
-      "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=",
+    "jest-regex-util": {
+      "version": "26.0.0",
+      "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-26.0.0.tgz",
+      "integrity": "sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A==",
       "dev": true
     },
-    "has-value": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz",
-      "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=",
+    "jest-serializer": {
+      "version": "26.6.2",
+      "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-26.6.2.tgz",
+      "integrity": "sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g==",
       "dev": true,
       "requires": {
-        "get-value": "^2.0.6",
-        "has-values": "^1.0.0",
-        "isobject": "^3.0.0"
+        "@types/node": "*",
+        "graceful-fs": "^4.2.4"
       }
     },
-    "has-values": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz",
-      "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=",
+    "jest-util": {
+      "version": "26.6.2",
+      "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz",
+      "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==",
       "dev": true,
       "requires": {
-        "is-number": "^3.0.0",
-        "kind-of": "^4.0.0"
+        "@jest/types": "^26.6.2",
+        "@types/node": "*",
+        "chalk": "^4.0.0",
+        "graceful-fs": "^4.2.4",
+        "is-ci": "^2.0.0",
+        "micromatch": "^4.0.2"
       },
       "dependencies": {
-        "is-number": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
-          "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
+        "ansi-styles": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
           "dev": true,
           "requires": {
-            "kind-of": "^3.0.2"
-          },
-          "dependencies": {
-            "kind-of": {
-              "version": "3.2.2",
-              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-              "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-              "dev": true,
-              "requires": {
-                "is-buffer": "^1.1.5"
-              }
-            }
+            "color-convert": "^2.0.1"
+          }
+        },
+        "chalk": {
+          "version": "4.1.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+          "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "dev": true,
+          "requires": {
+            "color-name": "~1.1.4"
           }
         },
-        "kind-of": {
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
+        },
+        "has-flag": {
           "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz",
-          "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
           "dev": true,
           "requires": {
-            "is-buffer": "^1.1.5"
+            "has-flag": "^4.0.0"
           }
         }
       }
     },
-    "hbp-connectivity-component": {
-      "version": "0.5.2",
-      "resolved": "https://registry.npmjs.org/hbp-connectivity-component/-/hbp-connectivity-component-0.5.2.tgz",
-      "integrity": "sha512-lGFkfuEWbuor9sqBnJxBcArdQ04WeHPvNAuHdaJL8bHMWxbAlnEOrPGn4AkdQO587rWD78cVw3LwEFSBm3O1Bg==",
+    "jest-worker": {
+      "version": "27.5.1",
+      "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz",
+      "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==",
+      "dev": true,
       "requires": {
-        "@stencil/core": "^1.16.5",
-        "@types/node": "^14.0.24",
-        "bootstrap": "^4.4.1",
-        "jszip": "^3.5.0"
+        "@types/node": "*",
+        "merge-stream": "^2.0.0",
+        "supports-color": "^8.0.0"
       },
       "dependencies": {
-        "@types/node": {
-          "version": "14.17.34",
-          "resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.34.tgz",
-          "integrity": "sha512-USUftMYpmuMzeWobskoPfzDi+vkpe0dvcOBRNOscFrGxVp4jomnRxWuVohgqBow2xyIPC0S3gjxV/5079jhmDg=="
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
+        "supports-color": {
+          "version": "8.1.1",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+          "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
         }
       }
     },
-    "hdr-histogram-js": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/hdr-histogram-js/-/hdr-histogram-js-2.0.1.tgz",
-      "integrity": "sha512-uPZxl1dAFnjUFHWLZmt93vUUvtHeaBay9nVNHu38SdOjMSF/4KqJUqa1Seuj08ptU1rEb6AHvB41X8n/zFZ74Q==",
-      "dev": true,
-      "requires": {
-        "@assemblyscript/loader": "^0.10.1",
-        "base64-js": "^1.2.0",
-        "pako": "^1.0.3"
-      }
+    "js-string-escape": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/js-string-escape/-/js-string-escape-1.0.1.tgz",
+      "integrity": "sha1-4mJbrbwNZ8dTPp7cEGjFh65BN+8=",
+      "dev": true
     },
-    "hdr-histogram-percentiles-obj": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/hdr-histogram-percentiles-obj/-/hdr-histogram-percentiles-obj-3.0.0.tgz",
-      "integrity": "sha512-7kIufnBqdsBGcSZLPJwqHT3yhk1QTsSlFsVD3kx5ixH/AlgBs9yM1q6DPhXZ8f8gtdqgh7N7/5btRLpQsS2gHw==",
+    "js-tokens": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+      "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
       "dev": true
     },
-    "hosted-git-info": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz",
-      "integrity": "sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg==",
+    "js-yaml": {
+      "version": "3.14.1",
+      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
+      "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
       "dev": true,
       "requires": {
-        "lru-cache": "^6.0.0"
+        "argparse": "^1.0.7",
+        "esprima": "^4.0.0"
       }
     },
-    "hpack.js": {
-      "version": "2.1.6",
-      "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz",
-      "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=",
-      "dev": true,
-      "requires": {
-        "inherits": "^2.0.1",
-        "obuf": "^1.0.0",
-        "readable-stream": "^2.0.1",
-        "wbuf": "^1.1.0"
-      }
+    "jsesc": {
+      "version": "2.5.2",
+      "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
+      "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
+      "dev": true
     },
-    "html-entities": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.4.0.tgz",
-      "integrity": "sha512-8nxjcBcd8wovbeKx7h3wTji4e6+rhaVuPNpMqwWgnHh+N9ToqsCs6XztWRBPQ+UtzsoMAdKZtUENoVzU/EMtZA==",
+    "json-parse-better-errors": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
+      "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
       "dev": true
     },
-    "html-escaper": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
-      "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
+    "json-parse-even-better-errors": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+      "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
       "dev": true
     },
-    "http-cache-semantics": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz",
-      "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==",
+    "json-schema-traverse": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+      "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
+    },
+    "json-stable-stringify-without-jsonify": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+      "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
       "dev": true
     },
-    "http-deceiver": {
-      "version": "1.2.7",
-      "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz",
-      "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=",
+    "json5": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz",
+      "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA=="
+    },
+    "jsonc-parser": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz",
+      "integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==",
       "dev": true
     },
-    "http-errors": {
-      "version": "1.7.2",
-      "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
-      "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
+    "jsonfile": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
+      "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
       "dev": true,
       "requires": {
-        "depd": "~1.1.2",
-        "inherits": "2.0.3",
-        "setprototypeof": "1.1.1",
-        "statuses": ">= 1.5.0 < 2",
-        "toidentifier": "1.0.0"
-      },
-      "dependencies": {
-        "inherits": {
-          "version": "2.0.3",
-          "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
-          "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
-          "dev": true
-        }
+        "graceful-fs": "^4.1.6",
+        "universalify": "^2.0.0"
       }
     },
-    "http-parser-js": {
-      "version": "0.5.5",
-      "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.5.tgz",
-      "integrity": "sha512-x+JVEkO2PoM8qqpbPbOL3cqHPwerep7OwzK7Ay+sMQjKzaKCqWvjoXm5tqMP9tXWWTnTzAjIhXg+J99XYuPhPA==",
+    "jsonparse": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz",
+      "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=",
       "dev": true
     },
-    "http-proxy": {
-      "version": "1.18.1",
-      "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz",
-      "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==",
-      "dev": true,
+    "jszip": {
+      "version": "3.10.0",
+      "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.0.tgz",
+      "integrity": "sha512-LDfVtOLtOxb9RXkYOwPyNBTQDL4eUbqahtoY6x07GiDJHwSYvn8sHHIw8wINImV3MqbMNve2gSuM1DDqEKk09Q==",
       "requires": {
-        "eventemitter3": "^4.0.0",
-        "follow-redirects": "^1.0.0",
-        "requires-port": "^1.0.0"
+        "lie": "~3.3.0",
+        "pako": "~1.0.2",
+        "readable-stream": "~2.3.6",
+        "setimmediate": "^1.0.5"
       }
     },
-    "http-proxy-agent": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz",
-      "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==",
-      "dev": true,
-      "requires": {
-        "@tootallnate/once": "1",
-        "agent-base": "6",
-        "debug": "4"
-      }
+    "junk": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/junk/-/junk-3.1.0.tgz",
+      "integrity": "sha512-pBxcB3LFc8QVgdggvZWyeys+hnrNWg4OcZIU/1X59k5jQdLBlCsYGRQaz234SqoRLTCgMH00fY0xRJH+F9METQ==",
+      "dev": true
     },
-    "http-proxy-middleware": {
-      "version": "0.19.1",
-      "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz",
-      "integrity": "sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==",
+    "karma": {
+      "version": "6.3.20",
+      "resolved": "https://registry.npmjs.org/karma/-/karma-6.3.20.tgz",
+      "integrity": "sha512-HRNQhMuKOwKpjYlWiJP0DUrJOh+QjaI/DTaD8b9rEm4Il3tJ8MijutVZH4ts10LuUFst/CedwTS6vieCN8yTSw==",
       "dev": true,
       "requires": {
-        "http-proxy": "^1.17.0",
-        "is-glob": "^4.0.0",
-        "lodash": "^4.17.11",
-        "micromatch": "^3.1.10"
+        "@colors/colors": "1.5.0",
+        "body-parser": "^1.19.0",
+        "braces": "^3.0.2",
+        "chokidar": "^3.5.1",
+        "connect": "^3.7.0",
+        "di": "^0.0.1",
+        "dom-serialize": "^2.2.1",
+        "glob": "^7.1.7",
+        "graceful-fs": "^4.2.6",
+        "http-proxy": "^1.18.1",
+        "isbinaryfile": "^4.0.8",
+        "lodash": "^4.17.21",
+        "log4js": "^6.4.1",
+        "mime": "^2.5.2",
+        "minimatch": "^3.0.4",
+        "mkdirp": "^0.5.5",
+        "qjobs": "^1.2.0",
+        "range-parser": "^1.2.1",
+        "rimraf": "^3.0.2",
+        "socket.io": "^4.4.1",
+        "source-map": "^0.6.1",
+        "tmp": "^0.2.1",
+        "ua-parser-js": "^0.7.30",
+        "yargs": "^16.1.1"
       },
       "dependencies": {
-        "braces": {
-          "version": "2.3.2",
-          "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
-          "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
+        "ansi-styles": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
           "dev": true,
           "requires": {
-            "arr-flatten": "^1.1.0",
-            "array-unique": "^0.3.2",
-            "extend-shallow": "^2.0.1",
-            "fill-range": "^4.0.0",
-            "isobject": "^3.0.1",
-            "repeat-element": "^1.1.2",
-            "snapdragon": "^0.8.1",
-            "snapdragon-node": "^2.0.1",
-            "split-string": "^3.0.2",
-            "to-regex": "^3.0.1"
-          },
-          "dependencies": {
-            "extend-shallow": {
-              "version": "2.0.1",
-              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-              "dev": true,
-              "requires": {
-                "is-extendable": "^0.1.0"
-              }
-            }
+            "color-convert": "^2.0.1"
+          }
+        },
+        "cliui": {
+          "version": "7.0.4",
+          "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
+          "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
+          "dev": true,
+          "requires": {
+            "string-width": "^4.2.0",
+            "strip-ansi": "^6.0.0",
+            "wrap-ansi": "^7.0.0"
           }
         },
-        "fill-range": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
-          "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "dev": true,
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
+        },
+        "mime": {
+          "version": "2.6.0",
+          "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz",
+          "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==",
+          "dev": true
+        },
+        "mkdirp": {
+          "version": "0.5.6",
+          "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
+          "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
           "dev": true,
           "requires": {
-            "extend-shallow": "^2.0.1",
-            "is-number": "^3.0.0",
-            "repeat-string": "^1.6.1",
-            "to-regex-range": "^2.1.0"
-          },
-          "dependencies": {
-            "extend-shallow": {
-              "version": "2.0.1",
-              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-              "dev": true,
-              "requires": {
-                "is-extendable": "^0.1.0"
-              }
-            }
+            "minimist": "^1.2.6"
           }
         },
-        "is-number": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
-          "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
+        "source-map": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+          "dev": true
+        },
+        "tmp": {
+          "version": "0.2.1",
+          "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz",
+          "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==",
           "dev": true,
           "requires": {
-            "kind-of": "^3.0.2"
-          },
-          "dependencies": {
-            "kind-of": {
-              "version": "3.2.2",
-              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-              "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-              "dev": true,
-              "requires": {
-                "is-buffer": "^1.1.5"
-              }
-            }
+            "rimraf": "^3.0.0"
           }
         },
-        "micromatch": {
-          "version": "3.1.10",
-          "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
-          "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
+        "wrap-ansi": {
+          "version": "7.0.0",
+          "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+          "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
           "dev": true,
           "requires": {
-            "arr-diff": "^4.0.0",
-            "array-unique": "^0.3.2",
-            "braces": "^2.3.1",
-            "define-property": "^2.0.2",
-            "extend-shallow": "^3.0.2",
-            "extglob": "^2.0.4",
-            "fragment-cache": "^0.2.1",
-            "kind-of": "^6.0.2",
-            "nanomatch": "^1.2.9",
-            "object.pick": "^1.3.0",
-            "regex-not": "^1.0.0",
-            "snapdragon": "^0.8.1",
-            "to-regex": "^3.0.2"
+            "ansi-styles": "^4.0.0",
+            "string-width": "^4.1.0",
+            "strip-ansi": "^6.0.0"
           }
         },
-        "to-regex-range": {
-          "version": "2.1.1",
-          "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
-          "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
+        "y18n": {
+          "version": "5.0.8",
+          "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+          "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+          "dev": true
+        },
+        "yargs": {
+          "version": "16.2.0",
+          "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
+          "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
           "dev": true,
           "requires": {
-            "is-number": "^3.0.0",
-            "repeat-string": "^1.6.1"
+            "cliui": "^7.0.2",
+            "escalade": "^3.1.1",
+            "get-caller-file": "^2.0.5",
+            "require-directory": "^2.1.1",
+            "string-width": "^4.2.0",
+            "y18n": "^5.0.5",
+            "yargs-parser": "^20.2.2"
           }
+        },
+        "yargs-parser": {
+          "version": "20.2.9",
+          "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
+          "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
+          "dev": true
         }
       }
     },
-    "http-signature": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
-      "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
+    "karma-chrome-launcher": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-3.1.1.tgz",
+      "integrity": "sha512-hsIglcq1vtboGPAN+DGCISCFOxW+ZVnIqhDQcCMqqCp+4dmJ0Qpq5QAjkbA0X2L9Mi6OBkHi2Srrbmm7pUKkzQ==",
       "dev": true,
       "requires": {
-        "assert-plus": "^1.0.0",
-        "jsprim": "^1.2.2",
-        "sshpk": "^1.7.0"
+        "which": "^1.2.1"
       }
     },
-    "https-proxy-agent": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz",
-      "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==",
+    "karma-coverage": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/karma-coverage/-/karma-coverage-2.0.3.tgz",
+      "integrity": "sha512-atDvLQqvPcLxhED0cmXYdsPMCQuh6Asa9FMZW1bhNqlVEhJoB9qyZ2BY1gu7D/rr5GLGb5QzYO4siQskxaWP/g==",
       "dev": true,
       "requires": {
-        "agent-base": "6",
-        "debug": "4"
+        "istanbul-lib-coverage": "^3.0.0",
+        "istanbul-lib-instrument": "^4.0.1",
+        "istanbul-lib-report": "^3.0.0",
+        "istanbul-lib-source-maps": "^4.0.0",
+        "istanbul-reports": "^3.0.0",
+        "minimatch": "^3.0.4"
       }
     },
-    "humanize-ms": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz",
-      "integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=",
+    "karma-jasmine": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-4.0.2.tgz",
+      "integrity": "sha512-ggi84RMNQffSDmWSyyt4zxzh2CQGwsxvYYsprgyR1j8ikzIduEdOlcLvXjZGwXG/0j41KUXOWsUCBfbEHPWP9g==",
       "dev": true,
       "requires": {
-        "ms": "^2.0.0"
+        "jasmine-core": "^3.6.0"
       }
     },
-    "iconv-lite": {
-      "version": "0.4.24",
-      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
-      "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+    "karma-jasmine-html-reporter": {
+      "version": "1.7.0",
+      "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-1.7.0.tgz",
+      "integrity": "sha512-pzum1TL7j90DTE86eFt48/s12hqwQuiD+e5aXx2Dc9wDEn2LfGq6RoAxEZZjFiN0RDSCOnosEKRZWxbQ+iMpQQ==",
+      "dev": true
+    },
+    "karma-source-map-support": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/karma-source-map-support/-/karma-source-map-support-1.4.0.tgz",
+      "integrity": "sha512-RsBECncGO17KAoJCYXjv+ckIz+Ii9NCi+9enk+rq6XC81ezYkb4/RHE6CTXdA7IOJqoF3wcaLfVG0CPmE5ca6A==",
       "dev": true,
       "requires": {
-        "safer-buffer": ">= 2.1.2 < 3"
+        "source-map-support": "^0.5.5"
       }
     },
-    "icss-utils": {
-      "version": "5.1.0",
-      "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz",
-      "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==",
+    "killable": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz",
+      "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==",
       "dev": true
     },
-    "ieee754": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
-      "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+    "kind-of": {
+      "version": "6.0.3",
+      "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+      "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
       "dev": true
     },
-    "ignore": {
-      "version": "5.1.9",
-      "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.9.tgz",
-      "integrity": "sha512-2zeMQpbKz5dhZ9IwL0gbxSW5w0NK/MSAMtNuhgIHEPmaU3vPdKPL0UdvUCXs5SS4JAwsBxysK5sFMW8ocFiVjQ==",
+    "kleur": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
+      "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==",
       "dev": true
     },
-    "ignore-walk": {
-      "version": "3.0.4",
-      "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.4.tgz",
-      "integrity": "sha512-PY6Ii8o1jMRA1z4F2hRkH/xN59ox43DavKvD3oDpfurRlOJyAHpifIwpbdv1n4jt4ov0jSpw3kQ4GhJnpBL6WQ==",
+    "klona": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.5.tgz",
+      "integrity": "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==",
+      "dev": true
+    },
+    "lazy-universal-dotenv": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/lazy-universal-dotenv/-/lazy-universal-dotenv-3.0.1.tgz",
+      "integrity": "sha512-prXSYk799h3GY3iOWnC6ZigYzMPjxN2svgjJ9shk7oMadSNX3wXy0B6F32PMJv7qtMnrIbUxoEHzbutvxR2LBQ==",
       "dev": true,
       "requires": {
-        "minimatch": "^3.0.4"
+        "@babel/runtime": "^7.5.0",
+        "app-root-dir": "^1.0.2",
+        "core-js": "^3.0.4",
+        "dotenv": "^8.0.0",
+        "dotenv-expand": "^5.1.0"
       }
     },
-    "image-size": {
-      "version": "0.5.5",
-      "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz",
-      "integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=",
+    "less": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/less/-/less-4.1.1.tgz",
+      "integrity": "sha512-w09o8tZFPThBscl5d0Ggp3RcrKIouBoQscnOMgFH3n5V3kN/CXGHNfCkRPtxJk6nKryDXaV9aHLK55RXuH4sAw==",
       "dev": true,
-      "optional": true
+      "requires": {
+        "copy-anything": "^2.0.1",
+        "errno": "^0.1.1",
+        "graceful-fs": "^4.1.2",
+        "image-size": "~0.5.0",
+        "make-dir": "^2.1.0",
+        "mime": "^1.4.1",
+        "needle": "^2.5.2",
+        "parse-node-version": "^1.0.1",
+        "source-map": "~0.6.0",
+        "tslib": "^1.10.0"
+      },
+      "dependencies": {
+        "make-dir": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
+          "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "pify": "^4.0.1",
+            "semver": "^5.6.0"
+          }
+        },
+        "semver": {
+          "version": "5.7.1",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+          "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+          "dev": true,
+          "optional": true
+        },
+        "source-map": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+          "dev": true,
+          "optional": true
+        },
+        "tslib": {
+          "version": "1.14.1",
+          "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+          "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+          "dev": true
+        }
+      }
     },
-    "immediate": {
-      "version": "3.0.6",
-      "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
-      "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps="
+    "less-loader": {
+      "version": "10.0.1",
+      "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-10.0.1.tgz",
+      "integrity": "sha512-Crln//HpW9M5CbtdfWm3IO66Cvx1WhZQvNybXgfB2dD/6Sav9ppw+IWqs/FQKPBFO4B6X0X28Z0WNznshgwUzA==",
+      "dev": true,
+      "requires": {
+        "klona": "^2.0.4"
+      }
+    },
+    "levn": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
+      "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
+      "dev": true,
+      "requires": {
+        "prelude-ls": "~1.1.2",
+        "type-check": "~0.3.2"
+      }
+    },
+    "license-webpack-plugin": {
+      "version": "2.3.20",
+      "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-2.3.20.tgz",
+      "integrity": "sha512-AHVueg9clOKACSHkhmEI+PCC9x8+qsQVuKECZD3ETxETK5h/PCv5/MUzyG1gm8OMcip/s1tcNxqo9Qb7WhjGsg==",
+      "dev": true,
+      "requires": {
+        "@types/webpack-sources": "^0.1.5",
+        "webpack-sources": "^1.2.0"
+      }
     },
-    "import-fresh": {
+    "lie": {
       "version": "3.3.0",
-      "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
-      "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
-      "dev": true,
+      "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
+      "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
       "requires": {
-        "parent-module": "^1.0.0",
-        "resolve-from": "^4.0.0"
+        "immediate": "~3.0.5"
       }
     },
-    "import-local": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz",
-      "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==",
+    "lilconfig": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.5.tgz",
+      "integrity": "sha512-xaYmXZtTHPAw5m+xLN8ab9C+3a8YmV3asNSPOATITbtwrfbwaLJj8h66H1WMIpALCkqsIzK3h7oQ+PdX+LQ9Eg==",
+      "dev": true
+    },
+    "lines-and-columns": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
+      "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
+      "dev": true
+    },
+    "load-json-file": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
+      "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=",
       "dev": true,
+      "optional": true,
       "requires": {
-        "pkg-dir": "^3.0.0",
-        "resolve-cwd": "^2.0.0"
+        "graceful-fs": "^4.1.2",
+        "parse-json": "^2.2.0",
+        "pify": "^2.0.0",
+        "pinkie-promise": "^2.0.0",
+        "strip-bom": "^2.0.0"
       },
       "dependencies": {
-        "pkg-dir": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz",
-          "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==",
+        "parse-json": {
+          "version": "2.2.0",
+          "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
+          "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
           "dev": true,
+          "optional": true,
           "requires": {
-            "find-up": "^3.0.0"
+            "error-ex": "^1.2.0"
           }
+        },
+        "pify": {
+          "version": "2.3.0",
+          "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+          "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+          "dev": true,
+          "optional": true
         }
       }
     },
-    "imurmurhash": {
-      "version": "0.1.4",
-      "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
-      "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
+    "loader-runner": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz",
+      "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==",
       "dev": true
     },
-    "indent-string": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
-      "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
+    "loader-utils": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz",
+      "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==",
+      "requires": {
+        "big.js": "^5.2.2",
+        "emojis-list": "^3.0.0",
+        "json5": "^2.1.2"
+      }
+    },
+    "locate-path": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
+      "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+      "dev": true,
+      "requires": {
+        "p-locate": "^4.1.0"
+      }
+    },
+    "lodash": {
+      "version": "4.17.21",
+      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+      "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
       "dev": true
     },
-    "indexes-of": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz",
-      "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=",
+    "lodash.debounce": {
+      "version": "4.0.8",
+      "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
+      "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=",
       "dev": true
     },
-    "infer-owner": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz",
-      "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==",
+    "lodash.memoize": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
+      "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=",
       "dev": true
     },
-    "inflight": {
-      "version": "1.0.6",
-      "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
-      "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
-      "dev": true,
-      "requires": {
-        "once": "^1.3.0",
-        "wrappy": "1"
-      }
+    "lodash.merge": {
+      "version": "4.6.2",
+      "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+      "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+      "dev": true
     },
-    "inherits": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
-      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+    "lodash.sortby": {
+      "version": "4.7.0",
+      "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
+      "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=",
+      "dev": true
     },
-    "ini": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz",
-      "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==",
+    "lodash.truncate": {
+      "version": "4.4.2",
+      "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz",
+      "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=",
       "dev": true
     },
-    "inquirer": {
-      "version": "8.1.2",
-      "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.1.2.tgz",
-      "integrity": "sha512-DHLKJwLPNgkfwNmsuEUKSejJFbkv0FMO9SMiQbjI3n5NQuCrSIBqP66ggqyz2a6t2qEolKrMjhQ3+W/xXgUQ+Q==",
+    "lodash.uniq": {
+      "version": "4.5.0",
+      "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
+      "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=",
+      "dev": true
+    },
+    "log-symbols": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
+      "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
       "dev": true,
       "requires": {
-        "ansi-escapes": "^4.2.1",
-        "chalk": "^4.1.1",
-        "cli-cursor": "^3.1.0",
-        "cli-width": "^3.0.0",
-        "external-editor": "^3.0.3",
-        "figures": "^3.0.0",
-        "lodash": "^4.17.21",
-        "mute-stream": "0.0.8",
-        "ora": "^5.3.0",
-        "run-async": "^2.4.0",
-        "rxjs": "^7.2.0",
-        "string-width": "^4.1.0",
-        "strip-ansi": "^6.0.0",
-        "through": "^2.3.6"
+        "chalk": "^4.1.0",
+        "is-unicode-supported": "^0.1.0"
       },
       "dependencies": {
-        "ansi-regex": {
-          "version": "5.0.1",
-          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
-          "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
-          "dev": true
-        },
         "ansi-styles": {
           "version": "4.3.0",
           "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
@@ -6275,454 +17543,451 @@
         "color-convert": {
           "version": "2.0.1",
           "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
-          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
-          "dev": true,
-          "requires": {
-            "color-name": "~1.1.4"
-          }
-        },
-        "color-name": {
-          "version": "1.1.4",
-          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
-          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
-          "dev": true
-        },
-        "emoji-regex": {
-          "version": "8.0.0",
-          "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
-          "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
-          "dev": true
-        },
-        "has-flag": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
-          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
-          "dev": true
-        },
-        "is-fullwidth-code-point": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
-          "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
-          "dev": true
-        },
-        "rxjs": {
-          "version": "7.4.0",
-          "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.4.0.tgz",
-          "integrity": "sha512-7SQDi7xeTMCJpqViXh8gL/lebcwlp3d831F05+9B44A4B0WfsEwUQHR64gsH1kvJ+Ep/J9K2+n1hVl1CsGN23w==",
-          "dev": true,
-          "requires": {
-            "tslib": "~2.1.0"
-          }
-        },
-        "string-width": {
-          "version": "4.2.3",
-          "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
-          "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
-          "dev": true,
-          "requires": {
-            "emoji-regex": "^8.0.0",
-            "is-fullwidth-code-point": "^3.0.0",
-            "strip-ansi": "^6.0.1"
-          }
-        },
-        "strip-ansi": {
-          "version": "6.0.1",
-          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
-          "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
-          "dev": true,
-          "requires": {
-            "ansi-regex": "^5.0.1"
-          }
-        },
-        "supports-color": {
-          "version": "7.2.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
-          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
-          "dev": true,
-          "requires": {
-            "has-flag": "^4.0.0"
-          }
-        },
-        "tslib": {
-          "version": "2.1.0",
-          "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz",
-          "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==",
-          "dev": true
-        }
-      }
-    },
-    "internal-ip": {
-      "version": "4.3.0",
-      "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz",
-      "integrity": "sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg==",
-      "dev": true,
-      "requires": {
-        "default-gateway": "^4.2.0",
-        "ipaddr.js": "^1.9.0"
-      }
-    },
-    "ip": {
-      "version": "1.1.5",
-      "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
-      "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=",
-      "dev": true
-    },
-    "ip-regex": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz",
-      "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=",
-      "dev": true
-    },
-    "ipaddr.js": {
-      "version": "1.9.1",
-      "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
-      "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
-      "dev": true
-    },
-    "is-absolute-url": {
-      "version": "3.0.3",
-      "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz",
-      "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==",
-      "dev": true
-    },
-    "is-accessor-descriptor": {
-      "version": "0.1.6",
-      "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
-      "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
-      "dev": true,
-      "requires": {
-        "kind-of": "^3.0.2"
-      },
-      "dependencies": {
-        "kind-of": {
-          "version": "3.2.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
           "dev": true,
           "requires": {
-            "is-buffer": "^1.1.5"
+            "color-name": "~1.1.4"
+          }
+        },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
           }
         }
       }
     },
-    "is-arguments": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz",
-      "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==",
+    "log4js": {
+      "version": "6.5.1",
+      "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.5.1.tgz",
+      "integrity": "sha512-z1hRRe5DDPzsP73PgN/GYmeSbIAl/g9kX3GLjABCpcU1ojns8S4cyjpJ21jU1P7z1wWkm69PjyMcEGqYYdDqaA==",
       "dev": true,
       "requires": {
-        "call-bind": "^1.0.2",
-        "has-tostringtag": "^1.0.0"
+        "date-format": "^4.0.10",
+        "debug": "^4.3.4",
+        "flatted": "^3.2.5",
+        "rfdc": "^1.3.0",
+        "streamroller": "^3.1.1"
       }
     },
-    "is-arrayish": {
-      "version": "0.2.1",
-      "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
-      "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
+    "loglevel": {
+      "version": "1.8.0",
+      "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.8.0.tgz",
+      "integrity": "sha512-G6A/nJLRgWOuuwdNuA6koovfEV1YpqqAG4pRUlFaz3jj2QNZ8M4vBqnVA+HBTmU/AMNUtlOsMmSpF6NyOjztbA==",
       "dev": true
     },
-    "is-binary-path": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
-      "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+    "loglevel-plugin-prefix": {
+      "version": "0.8.4",
+      "resolved": "https://registry.npmjs.org/loglevel-plugin-prefix/-/loglevel-plugin-prefix-0.8.4.tgz",
+      "integrity": "sha512-WpG9CcFAOjz/FtNht+QJeGpvVl/cdR6P0z6OcXSkr8wFJOsV2GRj2j10JLfjuA4aYkcKCNIEqRGCyTife9R8/g==",
+      "dev": true
+    },
+    "loose-envify": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+      "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
       "dev": true,
       "requires": {
-        "binary-extensions": "^2.0.0"
+        "js-tokens": "^3.0.0 || ^4.0.0"
       }
     },
-    "is-buffer": {
-      "version": "1.1.6",
-      "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
-      "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
-      "dev": true
-    },
-    "is-core-module": {
-      "version": "2.8.0",
-      "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz",
-      "integrity": "sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==",
+    "loud-rejection": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz",
+      "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=",
       "dev": true,
+      "optional": true,
       "requires": {
-        "has": "^1.0.3"
+        "currently-unhandled": "^0.4.1",
+        "signal-exit": "^3.0.0"
       }
     },
-    "is-data-descriptor": {
-      "version": "0.1.4",
-      "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
-      "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
+    "lower-case": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz",
+      "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==",
       "dev": true,
       "requires": {
-        "kind-of": "^3.0.2"
-      },
-      "dependencies": {
-        "kind-of": {
-          "version": "3.2.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-          "dev": true,
-          "requires": {
-            "is-buffer": "^1.1.5"
-          }
-        }
+        "tslib": "^2.0.3"
       }
     },
-    "is-date-object": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz",
-      "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==",
+    "lowlight": {
+      "version": "1.20.0",
+      "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-1.20.0.tgz",
+      "integrity": "sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==",
       "dev": true,
       "requires": {
-        "has-tostringtag": "^1.0.0"
+        "fault": "^1.0.0",
+        "highlight.js": "~10.7.0"
       }
     },
-    "is-descriptor": {
-      "version": "0.1.6",
-      "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
-      "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+    "lru-cache": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+      "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
       "dev": true,
       "requires": {
-        "is-accessor-descriptor": "^0.1.6",
-        "is-data-descriptor": "^0.1.4",
-        "kind-of": "^5.0.0"
-      },
-      "dependencies": {
-        "kind-of": {
-          "version": "5.1.0",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
-          "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
-          "dev": true
-        }
+        "yallist": "^4.0.0"
       }
     },
-    "is-docker": {
-      "version": "2.2.1",
-      "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
-      "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==",
-      "dev": true
-    },
-    "is-extendable": {
-      "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
-      "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=",
+    "lunr": {
+      "version": "2.3.9",
+      "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz",
+      "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==",
       "dev": true
     },
-    "is-extglob": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
-      "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+    "lz-string": {
+      "version": "1.4.4",
+      "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz",
+      "integrity": "sha1-wNjq82BZ9wV5bh40SBHPTEmNOiY=",
       "dev": true
     },
-    "is-fullwidth-code-point": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
-      "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+    "macos-release": {
+      "version": "2.5.0",
+      "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.5.0.tgz",
+      "integrity": "sha512-EIgv+QZ9r+814gjJj0Bt5vSLJLzswGmSUbUpbi9AIr/fsN2IWFBl2NucV9PAiek+U1STK468tEkxmVYUtuAN3g==",
       "dev": true
     },
-    "is-glob": {
-      "version": "4.0.3",
-      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
-      "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+    "magic-string": {
+      "version": "0.25.7",
+      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz",
+      "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==",
       "dev": true,
       "requires": {
-        "is-extglob": "^2.1.1"
+        "sourcemap-codec": "^1.4.4"
       }
     },
-    "is-interactive": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz",
-      "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==",
-      "dev": true
-    },
-    "is-lambda": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz",
-      "integrity": "sha1-PZh3iZ5qU+/AFgUEzeFfgubwYdU=",
-      "dev": true
-    },
-    "is-number": {
-      "version": "7.0.0",
-      "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
-      "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
-      "dev": true
-    },
-    "is-path-cwd": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz",
-      "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==",
-      "dev": true
-    },
-    "is-path-in-cwd": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz",
-      "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==",
+    "make-dir": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
+      "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
       "dev": true,
       "requires": {
-        "is-path-inside": "^2.1.0"
+        "semver": "^6.0.0"
+      },
+      "dependencies": {
+        "semver": {
+          "version": "6.3.0",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+          "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+          "dev": true
+        }
       }
     },
-    "is-path-inside": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz",
-      "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==",
+    "make-fetch-happen": {
+      "version": "9.1.0",
+      "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz",
+      "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==",
       "dev": true,
       "requires": {
-        "path-is-inside": "^1.0.2"
+        "agentkeepalive": "^4.1.3",
+        "cacache": "^15.2.0",
+        "http-cache-semantics": "^4.1.0",
+        "http-proxy-agent": "^4.0.1",
+        "https-proxy-agent": "^5.0.0",
+        "is-lambda": "^1.0.1",
+        "lru-cache": "^6.0.0",
+        "minipass": "^3.1.3",
+        "minipass-collect": "^1.0.2",
+        "minipass-fetch": "^1.3.2",
+        "minipass-flush": "^1.0.5",
+        "minipass-pipeline": "^1.2.4",
+        "negotiator": "^0.6.2",
+        "promise-retry": "^2.0.1",
+        "socks-proxy-agent": "^6.0.0",
+        "ssri": "^8.0.0"
       }
     },
-    "is-plain-object": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
-      "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+    "makeerror": {
+      "version": "1.0.12",
+      "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz",
+      "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==",
       "dev": true,
       "requires": {
-        "isobject": "^3.0.1"
+        "tmpl": "1.0.5"
       }
     },
-    "is-regex": {
-      "version": "1.1.4",
-      "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz",
-      "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==",
+    "map-age-cleaner": {
+      "version": "0.1.3",
+      "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz",
+      "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==",
       "dev": true,
       "requires": {
-        "call-bind": "^1.0.2",
-        "has-tostringtag": "^1.0.0"
+        "p-defer": "^1.0.0"
       }
     },
-    "is-resolvable": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz",
-      "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==",
+    "map-cache": {
+      "version": "0.2.2",
+      "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
+      "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=",
       "dev": true
     },
-    "is-stream": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
-      "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=",
-      "dev": true
+    "map-obj": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz",
+      "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=",
+      "dev": true,
+      "optional": true
     },
-    "is-typedarray": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
-      "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
+    "map-or-similar": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/map-or-similar/-/map-or-similar-1.5.0.tgz",
+      "integrity": "sha1-beJlMXSt+12e3DPGnT6Sobdvrwg=",
       "dev": true
     },
-    "is-unicode-supported": {
-      "version": "0.1.0",
-      "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
-      "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==",
+    "map-stream": {
+      "version": "0.0.7",
+      "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz",
+      "integrity": "sha1-ih8HiW2CsQkmvTdEokIACfiJdKg=",
       "dev": true
     },
-    "is-what": {
-      "version": "3.14.1",
-      "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz",
-      "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==",
-      "dev": true
+    "map-visit": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz",
+      "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=",
+      "dev": true,
+      "requires": {
+        "object-visit": "^1.0.0"
+      }
     },
-    "is-windows": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
-      "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==",
+    "markdown-escapes": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/markdown-escapes/-/markdown-escapes-1.0.4.tgz",
+      "integrity": "sha512-8z4efJYk43E0upd0NbVXwgSTQs6cT3T06etieCMEg7dRbzCbxUCK/GHlX8mhHRDcp+OLlHkPKsvqQTCvsRl2cg==",
+      "dev": true
+    },
+    "marked": {
+      "version": "4.0.16",
+      "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.16.tgz",
+      "integrity": "sha512-wahonIQ5Jnyatt2fn8KqF/nIqZM8mh3oRu2+l5EANGMhu6RFjiSG52QNE2eWzFMI94HqYSgN184NurgNG6CztA==",
       "dev": true
     },
-    "is-wsl": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
-      "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
+    "md5.js": {
+      "version": "1.3.5",
+      "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
+      "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==",
       "dev": true,
       "requires": {
-        "is-docker": "^2.0.0"
+        "hash-base": "^3.0.0",
+        "inherits": "^2.0.1",
+        "safe-buffer": "^5.1.2"
       }
     },
-    "isarray": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
-      "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
+    "mdast-squeeze-paragraphs": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/mdast-squeeze-paragraphs/-/mdast-squeeze-paragraphs-4.0.0.tgz",
+      "integrity": "sha512-zxdPn69hkQ1rm4J+2Cs2j6wDEv7O17TfXTJ33tl/+JPIoEmtV9t2ZzBM5LPHE8QlHsmVD8t3vPKCyY3oH+H8MQ==",
+      "dev": true,
+      "requires": {
+        "unist-util-remove": "^2.0.0"
+      }
     },
-    "isbinaryfile": {
-      "version": "4.0.8",
-      "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.8.tgz",
-      "integrity": "sha512-53h6XFniq77YdW+spoRrebh0mnmTxRPTlcuIArO57lmMdq4uBKFKaeTjnb92oYWrSn/LVL+LT+Hap2tFQj8V+w==",
-      "dev": true
+    "mdast-util-definitions": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-4.0.0.tgz",
+      "integrity": "sha512-k8AJ6aNnUkB7IE+5azR9h81O5EQ/cTDXtWdMq9Kk5KcEW/8ritU5CeLg/9HhOC++nALHBlaogJ5jz0Ybk3kPMQ==",
+      "dev": true,
+      "requires": {
+        "unist-util-visit": "^2.0.0"
+      }
     },
-    "isexe": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
-      "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+    "mdast-util-to-hast": {
+      "version": "10.0.1",
+      "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-10.0.1.tgz",
+      "integrity": "sha512-BW3LM9SEMnjf4HXXVApZMt8gLQWVNXc3jryK0nJu/rOXPOnlkUjmdkDlmxMirpbU9ILncGFIwLH/ubnWBbcdgA==",
+      "dev": true,
+      "requires": {
+        "@types/mdast": "^3.0.0",
+        "@types/unist": "^2.0.0",
+        "mdast-util-definitions": "^4.0.0",
+        "mdurl": "^1.0.0",
+        "unist-builder": "^2.0.0",
+        "unist-util-generated": "^1.0.0",
+        "unist-util-position": "^3.0.0",
+        "unist-util-visit": "^2.0.0"
+      }
+    },
+    "mdast-util-to-string": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-1.1.0.tgz",
+      "integrity": "sha512-jVU0Nr2B9X3MU4tSK7JP1CMkSvOj7X5l/GboG1tKRw52lLF1x2Ju92Ms9tNetCcbfX3hzlM73zYo2NKkWSfF/A==",
       "dev": true
     },
-    "isobject": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
-      "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
+    "mdn-data": {
+      "version": "2.0.14",
+      "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz",
+      "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==",
       "dev": true
     },
-    "isstream": {
-      "version": "0.1.2",
-      "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
-      "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=",
+    "mdurl": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz",
+      "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=",
       "dev": true
     },
-    "istanbul-lib-coverage": {
-      "version": "3.2.0",
-      "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz",
-      "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==",
+    "media-typer": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+      "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=",
       "dev": true
     },
-    "istanbul-lib-instrument": {
-      "version": "4.0.3",
-      "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz",
-      "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==",
+    "mem": {
+      "version": "8.1.1",
+      "resolved": "https://registry.npmjs.org/mem/-/mem-8.1.1.tgz",
+      "integrity": "sha512-qFCFUDs7U3b8mBDPyz5EToEKoAkgCzqquIgi9nkkR9bixxOVOre+09lbuH7+9Kn2NFpm56M3GUWVbU2hQgdACA==",
       "dev": true,
       "requires": {
-        "@babel/core": "^7.7.5",
-        "@istanbuljs/schema": "^0.1.2",
-        "istanbul-lib-coverage": "^3.0.0",
-        "semver": "^6.3.0"
+        "map-age-cleaner": "^0.1.3",
+        "mimic-fn": "^3.1.0"
       },
       "dependencies": {
-        "semver": {
-          "version": "6.3.0",
-          "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
-          "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+        "mimic-fn": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-3.1.0.tgz",
+          "integrity": "sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==",
           "dev": true
         }
       }
     },
-    "istanbul-lib-report": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz",
-      "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==",
+    "memfs": {
+      "version": "3.4.3",
+      "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.3.tgz",
+      "integrity": "sha512-eivjfi7Ahr6eQTn44nvTnR60e4a1Fs1Via2kCR5lHo/kyNoiMWaXCNJ/GpSd0ilXas2JSOl9B5FTIhflXu0hlg==",
       "dev": true,
       "requires": {
-        "istanbul-lib-coverage": "^3.0.0",
-        "make-dir": "^3.0.0",
-        "supports-color": "^7.1.0"
+        "fs-monkey": "1.0.3"
+      }
+    },
+    "memoizerific": {
+      "version": "1.11.3",
+      "resolved": "https://registry.npmjs.org/memoizerific/-/memoizerific-1.11.3.tgz",
+      "integrity": "sha1-fIekZGREwy11Q4VwkF8tvRsagFo=",
+      "dev": true,
+      "requires": {
+        "map-or-similar": "^1.5.0"
+      }
+    },
+    "memory-fs": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz",
+      "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=",
+      "dev": true,
+      "requires": {
+        "errno": "^0.1.3",
+        "readable-stream": "^2.0.1"
+      }
+    },
+    "meow": {
+      "version": "3.7.0",
+      "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz",
+      "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=",
+      "dev": true,
+      "optional": true,
+      "requires": {
+        "camelcase-keys": "^2.0.0",
+        "decamelize": "^1.1.2",
+        "loud-rejection": "^1.0.0",
+        "map-obj": "^1.0.1",
+        "minimist": "^1.1.3",
+        "normalize-package-data": "^2.3.4",
+        "object-assign": "^4.0.1",
+        "read-pkg-up": "^1.0.1",
+        "redent": "^1.0.0",
+        "trim-newlines": "^1.0.0"
       },
       "dependencies": {
-        "has-flag": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
-          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
-          "dev": true
+        "find-up": {
+          "version": "1.1.2",
+          "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
+          "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "path-exists": "^2.0.0",
+            "pinkie-promise": "^2.0.0"
+          }
         },
-        "supports-color": {
-          "version": "7.2.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
-          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+        "path-exists": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
+          "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=",
           "dev": true,
+          "optional": true,
           "requires": {
-            "has-flag": "^4.0.0"
+            "pinkie-promise": "^2.0.0"
+          }
+        },
+        "path-type": {
+          "version": "1.1.0",
+          "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz",
+          "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "graceful-fs": "^4.1.2",
+            "pify": "^2.0.0",
+            "pinkie-promise": "^2.0.0"
+          }
+        },
+        "pify": {
+          "version": "2.3.0",
+          "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+          "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+          "dev": true,
+          "optional": true
+        },
+        "read-pkg": {
+          "version": "1.1.0",
+          "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
+          "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "load-json-file": "^1.0.0",
+            "normalize-package-data": "^2.3.2",
+            "path-type": "^1.0.0"
+          }
+        },
+        "read-pkg-up": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz",
+          "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "find-up": "^1.0.0",
+            "read-pkg": "^1.0.0"
           }
         }
       }
     },
-    "istanbul-lib-source-maps": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz",
-      "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==",
+    "merge-descriptors": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
+      "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=",
+      "dev": true
+    },
+    "merge-source-map": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz",
+      "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==",
       "dev": true,
       "requires": {
-        "debug": "^4.1.1",
-        "istanbul-lib-coverage": "^3.0.0",
         "source-map": "^0.6.1"
       },
       "dependencies": {
@@ -6734,1940 +17999,2128 @@
         }
       }
     },
-    "istanbul-reports": {
-      "version": "3.0.5",
-      "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.5.tgz",
-      "integrity": "sha512-5+19PlhnGabNWB7kOFnuxT8H3T/iIyQzIbQMxXsURmmvKg86P2sbkrGOT77VnHw0Qr0gc2XzRaRfMZYYbSQCJQ==",
-      "dev": true,
-      "requires": {
-        "html-escaper": "^2.0.0",
-        "istanbul-lib-report": "^3.0.0"
-      }
+    "merge-stream": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
+      "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
+      "dev": true
     },
-    "jasmine-core": {
-      "version": "3.8.0",
-      "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.8.0.tgz",
-      "integrity": "sha512-zl0nZWDrmbCiKns0NcjkFGYkVTGCPUgoHypTaj+G2AzaWus7QGoXARSlYsSle2VRpSdfJmM+hzmFKzQNhF2kHg==",
+    "merge2": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+      "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
       "dev": true
     },
-    "jasmine-marbles": {
-      "version": "0.8.4",
-      "resolved": "https://registry.npmjs.org/jasmine-marbles/-/jasmine-marbles-0.8.4.tgz",
-      "integrity": "sha512-zbtuXABpSWrSswYPiZ5m6EQhluNmKcRQs+82AqJHSN+PMx3ASpDmTvRfqe9Pk2hPh9Ge5zrzOsorIlw3kdwTXQ==",
+    "methods": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
+      "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=",
+      "dev": true
+    },
+    "microevent.ts": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/microevent.ts/-/microevent.ts-0.1.1.tgz",
+      "integrity": "sha512-jo1OfR4TaEwd5HOrt5+tAZ9mqT4jmpNAusXtyfNzqVm9uiSYFZlKM1wYL4oU7azZW/PxQW53wM0S6OR1JHNa2g==",
+      "dev": true
+    },
+    "micromatch": {
+      "version": "4.0.5",
+      "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
+      "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
       "dev": true,
       "requires": {
-        "lodash": "^4.17.20"
+        "braces": "^3.0.2",
+        "picomatch": "^2.3.1"
       }
     },
-    "jest-worker": {
-      "version": "27.4.1",
-      "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.4.1.tgz",
-      "integrity": "sha512-cWYUNkfST1i17513Ll3GM5h/lJtYR1hRZsfPL4OQkIBWgKj4kbJREgHhxJxZmOmjKyeAg02HRoMWXr+B/JtFgg==",
+    "miller-rabin": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz",
+      "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==",
       "dev": true,
       "requires": {
-        "@types/node": "*",
-        "merge-stream": "^2.0.0",
-        "supports-color": "^8.0.0"
+        "bn.js": "^4.0.0",
+        "brorand": "^1.0.1"
       },
       "dependencies": {
-        "has-flag": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
-          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+        "bn.js": {
+          "version": "4.12.0",
+          "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
+          "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==",
           "dev": true
-        },
-        "supports-color": {
-          "version": "8.1.1",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
-          "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
-          "dev": true,
-          "requires": {
-            "has-flag": "^4.0.0"
-          }
         }
       }
     },
-    "js-tokens": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
-      "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+    "mime": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+      "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
       "dev": true
     },
-    "js-yaml": {
-      "version": "3.14.1",
-      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
-      "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
+    "mime-db": {
+      "version": "1.52.0",
+      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+      "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+      "dev": true
+    },
+    "mime-types": {
+      "version": "2.1.35",
+      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+      "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
       "dev": true,
       "requires": {
-        "argparse": "^1.0.7",
-        "esprima": "^4.0.0"
+        "mime-db": "1.52.0"
       }
     },
-    "jsbn": {
-      "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
-      "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
-      "dev": true
-    },
-    "jsesc": {
-      "version": "2.5.2",
-      "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
-      "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
-      "dev": true
-    },
-    "json-parse-better-errors": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
-      "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
-      "dev": true
-    },
-    "json-parse-even-better-errors": {
-      "version": "2.3.1",
-      "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
-      "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
+    "mimic-fn": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
+      "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
       "dev": true
     },
-    "json-schema": {
-      "version": "0.4.0",
-      "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz",
-      "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==",
-      "dev": true
+    "min-document": {
+      "version": "2.19.0",
+      "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz",
+      "integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=",
+      "dev": true,
+      "requires": {
+        "dom-walk": "^0.1.0"
+      }
     },
-    "json-schema-traverse": {
-      "version": "0.4.1",
-      "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
-      "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
+    "mini-css-extract-plugin": {
+      "version": "2.4.2",
+      "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.4.2.tgz",
+      "integrity": "sha512-ZmqShkn79D36uerdED+9qdo1ZYG8C1YsWvXu0UMJxurZnSdgz7gQKO2EGv8T55MhDqG3DYmGtizZNpM/UbTlcA==",
+      "dev": true,
+      "requires": {
+        "schema-utils": "^3.1.0"
+      }
     },
-    "json-stable-stringify-without-jsonify": {
+    "minimalistic-assert": {
       "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
-      "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
+      "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
+      "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==",
       "dev": true
     },
-    "json-stringify-safe": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
-      "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=",
+    "minimalistic-crypto-utils": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz",
+      "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=",
       "dev": true
     },
-    "json3": {
-      "version": "3.3.3",
-      "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.3.tgz",
-      "integrity": "sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA==",
+    "minimatch": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+      "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+      "dev": true,
+      "requires": {
+        "brace-expansion": "^1.1.7"
+      }
+    },
+    "minimist": {
+      "version": "1.2.6",
+      "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
+      "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==",
       "dev": true
     },
-    "json5": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz",
-      "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==",
+    "minipass": {
+      "version": "3.1.6",
+      "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.6.tgz",
+      "integrity": "sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==",
+      "dev": true,
       "requires": {
-        "minimist": "^1.2.5"
+        "yallist": "^4.0.0"
       }
     },
-    "jsonc-parser": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz",
-      "integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==",
-      "dev": true
+    "minipass-collect": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz",
+      "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==",
+      "dev": true,
+      "requires": {
+        "minipass": "^3.0.0"
+      }
     },
-    "jsonfile": {
-      "version": "6.1.0",
-      "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
-      "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
+    "minipass-fetch": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz",
+      "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==",
       "dev": true,
       "requires": {
-        "graceful-fs": "^4.1.6",
-        "universalify": "^2.0.0"
+        "encoding": "^0.1.12",
+        "minipass": "^3.1.0",
+        "minipass-sized": "^1.0.3",
+        "minizlib": "^2.0.0"
       }
     },
-    "jsonparse": {
-      "version": "1.3.1",
-      "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz",
-      "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=",
-      "dev": true
+    "minipass-flush": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz",
+      "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==",
+      "dev": true,
+      "requires": {
+        "minipass": "^3.0.0"
+      }
     },
-    "jsprim": {
-      "version": "1.4.2",
-      "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz",
-      "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==",
+    "minipass-json-stream": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/minipass-json-stream/-/minipass-json-stream-1.0.1.tgz",
+      "integrity": "sha512-ODqY18UZt/I8k+b7rl2AENgbWE8IDYam+undIJONvigAz8KR5GWblsFTEfQs0WODsjbSXWlm+JHEv8Gr6Tfdbg==",
       "dev": true,
       "requires": {
-        "assert-plus": "1.0.0",
-        "extsprintf": "1.3.0",
-        "json-schema": "0.4.0",
-        "verror": "1.10.0"
+        "jsonparse": "^1.3.1",
+        "minipass": "^3.0.0"
       }
     },
-    "jszip": {
-      "version": "3.7.1",
-      "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.7.1.tgz",
-      "integrity": "sha512-ghL0tz1XG9ZEmRMcEN2vt7xabrDdqHHeykgARpmZ0BiIctWxM47Vt63ZO2dnp4QYt/xJVLLy5Zv1l/xRdh2byg==",
+    "minipass-pipeline": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz",
+      "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==",
+      "dev": true,
       "requires": {
-        "lie": "~3.3.0",
-        "pako": "~1.0.2",
-        "readable-stream": "~2.3.6",
-        "set-immediate-shim": "~1.0.1"
+        "minipass": "^3.0.0"
       }
     },
-    "karma": {
-      "version": "6.3.16",
-      "resolved": "https://registry.npmjs.org/karma/-/karma-6.3.16.tgz",
-      "integrity": "sha512-nEU50jLvDe5yvXqkEJRf8IuvddUkOY2x5Xc4WXHz6dxINgGDrgD2uqQWeVrJs4hbfNaotn+HQ1LZJ4yOXrL7xQ==",
+    "minipass-sized": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz",
+      "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==",
       "dev": true,
       "requires": {
-        "body-parser": "^1.19.0",
-        "braces": "^3.0.2",
-        "chokidar": "^3.5.1",
-        "colors": "1.4.0",
-        "connect": "^3.7.0",
-        "di": "^0.0.1",
-        "dom-serialize": "^2.2.1",
-        "glob": "^7.1.7",
-        "graceful-fs": "^4.2.6",
-        "http-proxy": "^1.18.1",
-        "isbinaryfile": "^4.0.8",
-        "lodash": "^4.17.21",
-        "log4js": "^6.4.1",
-        "mime": "^2.5.2",
-        "minimatch": "^3.0.4",
-        "mkdirp": "^0.5.5",
-        "qjobs": "^1.2.0",
-        "range-parser": "^1.2.1",
-        "rimraf": "^3.0.2",
-        "socket.io": "^4.2.0",
-        "source-map": "^0.6.1",
-        "tmp": "^0.2.1",
-        "ua-parser-js": "^0.7.30",
-        "yargs": "^16.1.1"
+        "minipass": "^3.0.0"
+      }
+    },
+    "minizlib": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
+      "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
+      "dev": true,
+      "requires": {
+        "minipass": "^3.0.0",
+        "yallist": "^4.0.0"
+      }
+    },
+    "mississippi": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz",
+      "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==",
+      "dev": true,
+      "requires": {
+        "concat-stream": "^1.5.0",
+        "duplexify": "^3.4.2",
+        "end-of-stream": "^1.1.0",
+        "flush-write-stream": "^1.0.0",
+        "from2": "^2.1.0",
+        "parallel-transform": "^1.1.0",
+        "pump": "^3.0.0",
+        "pumpify": "^1.3.3",
+        "stream-each": "^1.1.0",
+        "through2": "^2.0.0"
+      }
+    },
+    "mixin-deep": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz",
+      "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==",
+      "dev": true,
+      "requires": {
+        "for-in": "^1.0.2",
+        "is-extendable": "^1.0.1"
       },
       "dependencies": {
-        "ansi-regex": {
-          "version": "5.0.1",
-          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
-          "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
-          "dev": true
-        },
-        "ansi-styles": {
-          "version": "4.3.0",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
-          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
-          "dev": true,
-          "requires": {
-            "color-convert": "^2.0.1"
-          }
-        },
-        "cliui": {
-          "version": "7.0.4",
-          "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
-          "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
+        "is-extendable": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+          "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
           "dev": true,
           "requires": {
-            "string-width": "^4.2.0",
-            "strip-ansi": "^6.0.0",
-            "wrap-ansi": "^7.0.0"
+            "is-plain-object": "^2.0.4"
           }
-        },
-        "color-convert": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
-          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+        }
+      }
+    },
+    "mkdirp": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
+      "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
+      "dev": true
+    },
+    "morgan": {
+      "version": "1.10.0",
+      "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz",
+      "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==",
+      "dev": true,
+      "requires": {
+        "basic-auth": "~2.0.1",
+        "debug": "2.6.9",
+        "depd": "~2.0.0",
+        "on-finished": "~2.3.0",
+        "on-headers": "~1.0.2"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "2.6.9",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
           "dev": true,
           "requires": {
-            "color-name": "~1.1.4"
+            "ms": "2.0.0"
           }
         },
-        "color-name": {
-          "version": "1.1.4",
-          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
-          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
-          "dev": true
-        },
-        "mime": {
-          "version": "2.6.0",
-          "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz",
-          "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==",
+        "ms": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
           "dev": true
         },
-        "mkdirp": {
-          "version": "0.5.5",
-          "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
-          "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
+        "on-finished": {
+          "version": "2.3.0",
+          "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
+          "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
           "dev": true,
           "requires": {
-            "minimist": "^1.2.5"
+            "ee-first": "1.1.1"
           }
-        },
-        "source-map": {
-          "version": "0.6.1",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+        }
+      }
+    },
+    "move-concurrently": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",
+      "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=",
+      "dev": true,
+      "requires": {
+        "aproba": "^1.1.1",
+        "copy-concurrently": "^1.0.0",
+        "fs-write-stream-atomic": "^1.0.8",
+        "mkdirp": "^0.5.1",
+        "rimraf": "^2.5.4",
+        "run-queue": "^1.0.3"
+      },
+      "dependencies": {
+        "aproba": {
+          "version": "1.2.0",
+          "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
+          "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
           "dev": true
         },
-        "strip-ansi": {
-          "version": "6.0.1",
-          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
-          "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
-          "dev": true,
-          "requires": {
-            "ansi-regex": "^5.0.1"
-          }
-        },
-        "tmp": {
-          "version": "0.2.1",
-          "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz",
-          "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==",
-          "dev": true,
-          "requires": {
-            "rimraf": "^3.0.0"
-          }
-        },
-        "wrap-ansi": {
-          "version": "7.0.0",
-          "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
-          "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+        "mkdirp": {
+          "version": "0.5.6",
+          "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
+          "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
           "dev": true,
           "requires": {
-            "ansi-styles": "^4.0.0",
-            "string-width": "^4.1.0",
-            "strip-ansi": "^6.0.0"
+            "minimist": "^1.2.6"
           }
         },
-        "y18n": {
-          "version": "5.0.8",
-          "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
-          "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
-          "dev": true
-        },
-        "yargs": {
-          "version": "16.2.0",
-          "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
-          "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
+        "rimraf": {
+          "version": "2.7.1",
+          "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+          "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
           "dev": true,
           "requires": {
-            "cliui": "^7.0.2",
-            "escalade": "^3.1.1",
-            "get-caller-file": "^2.0.5",
-            "require-directory": "^2.1.1",
-            "string-width": "^4.2.0",
-            "y18n": "^5.0.5",
-            "yargs-parser": "^20.2.2"
+            "glob": "^7.1.3"
           }
-        },
-        "yargs-parser": {
-          "version": "20.2.9",
-          "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
-          "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
-          "dev": true
         }
       }
     },
-    "karma-chrome-launcher": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-3.1.0.tgz",
-      "integrity": "sha512-3dPs/n7vgz1rxxtynpzZTvb9y/GIaW8xjAwcIGttLbycqoFtI7yo1NGnQi6oFTherRE+GIhCAHZC4vEqWGhNvg==",
+    "ms": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+      "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+      "dev": true
+    },
+    "multicast-dns": {
+      "version": "6.2.3",
+      "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz",
+      "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==",
       "dev": true,
       "requires": {
-        "which": "^1.2.1"
+        "dns-packet": "^1.3.1",
+        "thunky": "^1.0.2"
       }
     },
-    "karma-coverage": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmjs.org/karma-coverage/-/karma-coverage-2.0.3.tgz",
-      "integrity": "sha512-atDvLQqvPcLxhED0cmXYdsPMCQuh6Asa9FMZW1bhNqlVEhJoB9qyZ2BY1gu7D/rr5GLGb5QzYO4siQskxaWP/g==",
+    "multicast-dns-service-types": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz",
+      "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=",
+      "dev": true
+    },
+    "mute-stream": {
+      "version": "0.0.8",
+      "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
+      "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==",
+      "dev": true
+    },
+    "nanoid": {
+      "version": "3.3.4",
+      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
+      "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw=="
+    },
+    "nanomatch": {
+      "version": "1.2.13",
+      "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
+      "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==",
       "dev": true,
       "requires": {
-        "istanbul-lib-coverage": "^3.0.0",
-        "istanbul-lib-instrument": "^4.0.1",
-        "istanbul-lib-report": "^3.0.0",
-        "istanbul-lib-source-maps": "^4.0.0",
-        "istanbul-reports": "^3.0.0",
-        "minimatch": "^3.0.4"
+        "arr-diff": "^4.0.0",
+        "array-unique": "^0.3.2",
+        "define-property": "^2.0.2",
+        "extend-shallow": "^3.0.2",
+        "fragment-cache": "^0.2.1",
+        "is-windows": "^1.0.2",
+        "kind-of": "^6.0.2",
+        "object.pick": "^1.3.0",
+        "regex-not": "^1.0.0",
+        "snapdragon": "^0.8.1",
+        "to-regex": "^3.0.1"
       }
     },
-    "karma-jasmine": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-4.0.1.tgz",
-      "integrity": "sha512-h8XDAhTiZjJKzfkoO1laMH+zfNlra+dEQHUAjpn5JV1zCPtOIVWGQjLBrqhnzQa/hrU2XrZwSyBa6XjEBzfXzw==",
+    "natural-compare": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+      "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
+      "dev": true
+    },
+    "needle": {
+      "version": "2.9.1",
+      "resolved": "https://registry.npmjs.org/needle/-/needle-2.9.1.tgz",
+      "integrity": "sha512-6R9fqJ5Zcmf+uYaFgdIHmLwNldn5HbK8L5ybn7Uz+ylX/rnOsSp1AHcvQSrCaFN+qNM1wpymHqD7mVasEOlHGQ==",
       "dev": true,
+      "optional": true,
       "requires": {
-        "jasmine-core": "^3.6.0"
+        "debug": "^3.2.6",
+        "iconv-lite": "^0.4.4",
+        "sax": "^1.2.4"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "3.2.7",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+          "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "ms": "^2.1.1"
+          }
+        }
       }
     },
-    "karma-jasmine-html-reporter": {
-      "version": "1.7.0",
-      "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-1.7.0.tgz",
-      "integrity": "sha512-pzum1TL7j90DTE86eFt48/s12hqwQuiD+e5aXx2Dc9wDEn2LfGq6RoAxEZZjFiN0RDSCOnosEKRZWxbQ+iMpQQ==",
+    "negotiator": {
+      "version": "0.6.3",
+      "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
+      "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
       "dev": true
     },
-    "karma-source-map-support": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/karma-source-map-support/-/karma-source-map-support-1.4.0.tgz",
-      "integrity": "sha512-RsBECncGO17KAoJCYXjv+ckIz+Ii9NCi+9enk+rq6XC81ezYkb4/RHE6CTXdA7IOJqoF3wcaLfVG0CPmE5ca6A==",
+    "neo-async": {
+      "version": "2.6.2",
+      "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
+      "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
+      "dev": true
+    },
+    "nested-error-stacks": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-2.1.1.tgz",
+      "integrity": "sha512-9iN1ka/9zmX1ZvLV9ewJYEk9h7RyRRtqdK0woXcqohu8EWIerfPUjYJPg0ULy0UqP7cslmdGc8xKDJcojlKiaw==",
+      "dev": true
+    },
+    "next-tick": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz",
+      "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==",
+      "dev": true
+    },
+    "nice-napi": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz",
+      "integrity": "sha512-px/KnJAJZf5RuBGcfD+Sp2pAKq0ytz8j+1NehvgIGFkvtvFrDM3T8E4x/JJODXK9WZow8RRGrbA9QQ3hs+pDhA==",
       "dev": true,
+      "optional": true,
       "requires": {
-        "source-map-support": "^0.5.5"
+        "node-addon-api": "^3.0.0",
+        "node-gyp-build": "^4.2.2"
       }
     },
-    "killable": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz",
-      "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==",
+    "nice-try": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
+      "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
       "dev": true
     },
-    "kind-of": {
-      "version": "6.0.3",
-      "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
-      "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
-      "dev": true
+    "no-case": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz",
+      "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==",
+      "dev": true,
+      "requires": {
+        "lower-case": "^2.0.2",
+        "tslib": "^2.0.3"
+      }
     },
-    "klona": {
-      "version": "2.0.5",
-      "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.5.tgz",
-      "integrity": "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==",
+    "node-addon-api": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz",
+      "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==",
+      "dev": true,
+      "optional": true
+    },
+    "node-fetch": {
+      "version": "2.6.7",
+      "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
+      "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
+      "dev": true,
+      "requires": {
+        "whatwg-url": "^5.0.0"
+      }
+    },
+    "node-forge": {
+      "version": "0.10.0",
+      "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz",
+      "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==",
       "dev": true
     },
-    "less": {
-      "version": "4.1.1",
-      "resolved": "https://registry.npmjs.org/less/-/less-4.1.1.tgz",
-      "integrity": "sha512-w09o8tZFPThBscl5d0Ggp3RcrKIouBoQscnOMgFH3n5V3kN/CXGHNfCkRPtxJk6nKryDXaV9aHLK55RXuH4sAw==",
+    "node-gyp": {
+      "version": "8.4.1",
+      "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz",
+      "integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==",
       "dev": true,
       "requires": {
-        "copy-anything": "^2.0.1",
-        "errno": "^0.1.1",
-        "graceful-fs": "^4.1.2",
-        "image-size": "~0.5.0",
-        "make-dir": "^2.1.0",
-        "mime": "^1.4.1",
-        "needle": "^2.5.2",
-        "parse-node-version": "^1.0.1",
-        "source-map": "~0.6.0",
-        "tslib": "^1.10.0"
+        "env-paths": "^2.2.0",
+        "glob": "^7.1.4",
+        "graceful-fs": "^4.2.6",
+        "make-fetch-happen": "^9.1.0",
+        "nopt": "^5.0.0",
+        "npmlog": "^6.0.0",
+        "rimraf": "^3.0.2",
+        "semver": "^7.3.5",
+        "tar": "^6.1.2",
+        "which": "^2.0.2"
       },
       "dependencies": {
-        "make-dir": {
-          "version": "2.1.0",
-          "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
-          "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
+        "which": {
+          "version": "2.0.2",
+          "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+          "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
           "dev": true,
-          "optional": true,
           "requires": {
-            "pify": "^4.0.1",
-            "semver": "^5.6.0"
+            "isexe": "^2.0.0"
+          }
+        }
+      }
+    },
+    "node-gyp-build": {
+      "version": "4.4.0",
+      "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.4.0.tgz",
+      "integrity": "sha512-amJnQCcgtRVw9SvoebO3BKGESClrfXGCUTX9hSn1OuGQTQBOZmVd0Z0OlecpuRksKvbsUqALE8jls/ErClAPuQ==",
+      "dev": true,
+      "optional": true
+    },
+    "node-int64": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz",
+      "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=",
+      "dev": true
+    },
+    "node-libs-browser": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz",
+      "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==",
+      "dev": true,
+      "requires": {
+        "assert": "^1.1.1",
+        "browserify-zlib": "^0.2.0",
+        "buffer": "^4.3.0",
+        "console-browserify": "^1.1.0",
+        "constants-browserify": "^1.0.0",
+        "crypto-browserify": "^3.11.0",
+        "domain-browser": "^1.1.1",
+        "events": "^3.0.0",
+        "https-browserify": "^1.0.0",
+        "os-browserify": "^0.3.0",
+        "path-browserify": "0.0.1",
+        "process": "^0.11.10",
+        "punycode": "^1.2.4",
+        "querystring-es3": "^0.2.0",
+        "readable-stream": "^2.3.3",
+        "stream-browserify": "^2.0.1",
+        "stream-http": "^2.7.2",
+        "string_decoder": "^1.0.0",
+        "timers-browserify": "^2.0.4",
+        "tty-browserify": "0.0.0",
+        "url": "^0.11.0",
+        "util": "^0.11.0",
+        "vm-browserify": "^1.0.1"
+      },
+      "dependencies": {
+        "buffer": {
+          "version": "4.9.2",
+          "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz",
+          "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==",
+          "dev": true,
+          "requires": {
+            "base64-js": "^1.0.2",
+            "ieee754": "^1.1.4",
+            "isarray": "^1.0.0"
           }
         },
+        "path-browserify": {
+          "version": "0.0.1",
+          "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz",
+          "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==",
+          "dev": true
+        },
+        "punycode": {
+          "version": "1.4.1",
+          "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
+          "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
+          "dev": true
+        }
+      }
+    },
+    "node-releases": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.4.tgz",
+      "integrity": "sha512-gbMzqQtTtDz/00jQzZ21PQzdI9PyLYqUSvD0p3naOhX4odFji0ZxYdnVwPTxmSwkmxhcFImpozceidSG+AgoPQ==",
+      "dev": true
+    },
+    "nopt": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz",
+      "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==",
+      "dev": true,
+      "requires": {
+        "abbrev": "1"
+      }
+    },
+    "normalize-package-data": {
+      "version": "2.5.0",
+      "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
+      "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
+      "dev": true,
+      "requires": {
+        "hosted-git-info": "^2.1.4",
+        "resolve": "^1.10.0",
+        "semver": "2 || 3 || 4 || 5",
+        "validate-npm-package-license": "^3.0.1"
+      },
+      "dependencies": {
+        "hosted-git-info": {
+          "version": "2.8.9",
+          "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
+          "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
+          "dev": true
+        },
         "semver": {
           "version": "5.7.1",
           "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
           "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
-          "dev": true,
-          "optional": true
-        },
-        "source-map": {
-          "version": "0.6.1",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
-          "dev": true,
-          "optional": true
-        },
-        "tslib": {
-          "version": "1.14.1",
-          "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
-          "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
           "dev": true
         }
       }
     },
-    "less-loader": {
-      "version": "10.0.1",
-      "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-10.0.1.tgz",
-      "integrity": "sha512-Crln//HpW9M5CbtdfWm3IO66Cvx1WhZQvNybXgfB2dD/6Sav9ppw+IWqs/FQKPBFO4B6X0X28Z0WNznshgwUzA==",
+    "normalize-path": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+      "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+      "dev": true
+    },
+    "normalize-range": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz",
+      "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=",
+      "dev": true
+    },
+    "normalize-url": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz",
+      "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==",
+      "dev": true
+    },
+    "npm-bundled": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.2.tgz",
+      "integrity": "sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ==",
       "dev": true,
       "requires": {
-        "klona": "^2.0.4"
+        "npm-normalize-package-bin": "^1.0.1"
       }
     },
-    "levn": {
-      "version": "0.4.1",
-      "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
-      "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+    "npm-install-checks": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-4.0.0.tgz",
+      "integrity": "sha512-09OmyDkNLYwqKPOnbI8exiOZU2GVVmQp7tgez2BPi5OZC8M82elDAps7sxC4l//uSUtotWqoEIDwjRvWH4qz8w==",
       "dev": true,
       "requires": {
-        "prelude-ls": "^1.2.1",
-        "type-check": "~0.4.0"
+        "semver": "^7.1.1"
       }
     },
-    "license-webpack-plugin": {
-      "version": "2.3.20",
-      "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-2.3.20.tgz",
-      "integrity": "sha512-AHVueg9clOKACSHkhmEI+PCC9x8+qsQVuKECZD3ETxETK5h/PCv5/MUzyG1gm8OMcip/s1tcNxqo9Qb7WhjGsg==",
+    "npm-normalize-package-bin": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz",
+      "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==",
+      "dev": true
+    },
+    "npm-package-arg": {
+      "version": "8.1.5",
+      "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-8.1.5.tgz",
+      "integrity": "sha512-LhgZrg0n0VgvzVdSm1oiZworPbTxYHUJCgtsJW8mGvlDpxTM1vSJc3m5QZeUkhAHIzbz3VCHd/R4osi1L1Tg/Q==",
       "dev": true,
       "requires": {
-        "@types/webpack-sources": "^0.1.5",
-        "webpack-sources": "^1.2.0"
+        "hosted-git-info": "^4.0.1",
+        "semver": "^7.3.4",
+        "validate-npm-package-name": "^3.0.0"
       }
     },
-    "lie": {
-      "version": "3.3.0",
-      "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
-      "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
+    "npm-packlist": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-3.0.0.tgz",
+      "integrity": "sha512-L/cbzmutAwII5glUcf2DBRNY/d0TFd4e/FnaZigJV6JD85RHZXJFGwCndjMWiiViiWSsWt3tiOLpI3ByTnIdFQ==",
+      "dev": true,
       "requires": {
-        "immediate": "~3.0.5"
+        "glob": "^7.1.6",
+        "ignore-walk": "^4.0.1",
+        "npm-bundled": "^1.1.1",
+        "npm-normalize-package-bin": "^1.0.1"
       }
     },
-    "lilconfig": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.4.tgz",
-      "integrity": "sha512-bfTIN7lEsiooCocSISTWXkiWJkRqtL9wYtYy+8EK3Y41qh3mpwPU0ycTOgjdY9ErwXCc8QyrQp82bdL0Xkm9yA==",
-      "dev": true
-    },
-    "lines-and-columns": {
-      "version": "1.2.4",
-      "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
-      "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
-      "dev": true
+    "npm-pick-manifest": {
+      "version": "6.1.1",
+      "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-6.1.1.tgz",
+      "integrity": "sha512-dBsdBtORT84S8V8UTad1WlUyKIY9iMsAmqxHbLdeEeBNMLQDlDWWra3wYUx9EBEIiG/YwAy0XyNHDd2goAsfuA==",
+      "dev": true,
+      "requires": {
+        "npm-install-checks": "^4.0.0",
+        "npm-normalize-package-bin": "^1.0.1",
+        "npm-package-arg": "^8.1.2",
+        "semver": "^7.3.4"
+      }
     },
-    "loader-runner": {
-      "version": "4.2.0",
-      "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz",
-      "integrity": "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==",
-      "dev": true
+    "npm-registry-fetch": {
+      "version": "11.0.0",
+      "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-11.0.0.tgz",
+      "integrity": "sha512-jmlgSxoDNuhAtxUIG6pVwwtz840i994dL14FoNVZisrmZW5kWd63IUTNv1m/hyRSGSqWjCUp/YZlS1BJyNp9XA==",
+      "dev": true,
+      "requires": {
+        "make-fetch-happen": "^9.0.1",
+        "minipass": "^3.1.3",
+        "minipass-fetch": "^1.3.0",
+        "minipass-json-stream": "^1.0.1",
+        "minizlib": "^2.0.0",
+        "npm-package-arg": "^8.0.0"
+      }
     },
-    "loader-utils": {
+    "npm-run-path": {
       "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz",
-      "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==",
+      "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
+      "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=",
+      "dev": true,
       "requires": {
-        "big.js": "^5.2.2",
-        "emojis-list": "^3.0.0",
-        "json5": "^2.1.2"
+        "path-key": "^2.0.0"
       }
     },
-    "locate-path": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
-      "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+    "npmlog": {
+      "version": "6.0.2",
+      "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz",
+      "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==",
       "dev": true,
       "requires": {
-        "p-locate": "^3.0.0",
-        "path-exists": "^3.0.0"
+        "are-we-there-yet": "^3.0.0",
+        "console-control-strings": "^1.1.0",
+        "gauge": "^4.0.3",
+        "set-blocking": "^2.0.0"
       }
     },
-    "lodash": {
-      "version": "4.17.21",
-      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
-      "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
-      "dev": true
-    },
-    "lodash.debounce": {
-      "version": "4.0.8",
-      "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
-      "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=",
-      "dev": true
-    },
-    "lodash.memoize": {
-      "version": "4.1.2",
-      "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
-      "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=",
-      "dev": true
-    },
-    "lodash.merge": {
-      "version": "4.6.2",
-      "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
-      "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
-      "dev": true
+    "nth-check": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
+      "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
+      "dev": true,
+      "requires": {
+        "boolbase": "^1.0.0"
+      }
     },
-    "lodash.truncate": {
-      "version": "4.4.2",
-      "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz",
-      "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=",
+    "num2fraction": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz",
+      "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=",
       "dev": true
     },
-    "lodash.uniq": {
-      "version": "4.5.0",
-      "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
-      "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=",
+    "object-assign": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+      "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
       "dev": true
     },
-    "log-symbols": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
-      "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
+    "object-copy": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz",
+      "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=",
       "dev": true,
       "requires": {
-        "chalk": "^4.1.0",
-        "is-unicode-supported": "^0.1.0"
+        "copy-descriptor": "^0.1.0",
+        "define-property": "^0.2.5",
+        "kind-of": "^3.0.3"
       },
       "dependencies": {
-        "ansi-styles": {
-          "version": "4.3.0",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
-          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
-          "dev": true,
-          "requires": {
-            "color-convert": "^2.0.1"
-          }
-        },
-        "chalk": {
-          "version": "4.1.2",
-          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
-          "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
-          "dev": true,
-          "requires": {
-            "ansi-styles": "^4.1.0",
-            "supports-color": "^7.1.0"
-          }
-        },
-        "color-convert": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
-          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+        "define-property": {
+          "version": "0.2.5",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
           "dev": true,
           "requires": {
-            "color-name": "~1.1.4"
+            "is-descriptor": "^0.1.0"
           }
         },
-        "color-name": {
-          "version": "1.1.4",
-          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
-          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
-          "dev": true
-        },
-        "has-flag": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
-          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
-          "dev": true
-        },
-        "supports-color": {
-          "version": "7.2.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
-          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
           "dev": true,
           "requires": {
-            "has-flag": "^4.0.0"
+            "is-buffer": "^1.1.5"
           }
         }
       }
     },
-    "log4js": {
-      "version": "6.4.1",
-      "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.4.1.tgz",
-      "integrity": "sha512-iUiYnXqAmNKiIZ1XSAitQ4TmNs8CdZYTAWINARF3LjnsLN8tY5m0vRwd6uuWj/yNY0YHxeZodnbmxKFUOM2rMg==",
+    "object-inspect": {
+      "version": "1.12.1",
+      "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.1.tgz",
+      "integrity": "sha512-Y/jF6vnvEtOPGiKD1+q+X0CiUYRQtEHp89MLLUJ7TUivtH8Ugn2+3A7Rynqk7BRsAoqeOQWnFnjpDrKSxDgIGA==",
+      "dev": true
+    },
+    "object-is": {
+      "version": "1.1.5",
+      "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz",
+      "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==",
+      "dev": true,
+      "requires": {
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.3"
+      }
+    },
+    "object-keys": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+      "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+      "dev": true
+    },
+    "object-visit": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz",
+      "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=",
       "dev": true,
       "requires": {
-        "date-format": "^4.0.3",
-        "debug": "^4.3.3",
-        "flatted": "^3.2.4",
-        "rfdc": "^1.3.0",
-        "streamroller": "^3.0.2"
+        "isobject": "^3.0.0"
       }
     },
-    "loglevel": {
-      "version": "1.8.0",
-      "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.8.0.tgz",
-      "integrity": "sha512-G6A/nJLRgWOuuwdNuA6koovfEV1YpqqAG4pRUlFaz3jj2QNZ8M4vBqnVA+HBTmU/AMNUtlOsMmSpF6NyOjztbA==",
-      "dev": true
+    "object.assign": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz",
+      "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==",
+      "dev": true,
+      "requires": {
+        "call-bind": "^1.0.0",
+        "define-properties": "^1.1.3",
+        "has-symbols": "^1.0.1",
+        "object-keys": "^1.1.1"
+      }
     },
-    "lru-cache": {
-      "version": "6.0.0",
-      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
-      "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+    "object.entries": {
+      "version": "1.1.5",
+      "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.5.tgz",
+      "integrity": "sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g==",
       "dev": true,
       "requires": {
-        "yallist": "^4.0.0"
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.3",
+        "es-abstract": "^1.19.1"
       }
     },
-    "magic-string": {
-      "version": "0.25.7",
-      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz",
-      "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==",
+    "object.fromentries": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.5.tgz",
+      "integrity": "sha512-CAyG5mWQRRiBU57Re4FKoTBjXfDoNwdFVH2Y1tS9PqCsfUTymAohOkEMSG3aRNKmv4lV3O7p1et7c187q6bynw==",
       "dev": true,
       "requires": {
-        "sourcemap-codec": "^1.4.4"
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.3",
+        "es-abstract": "^1.19.1"
       }
     },
-    "make-dir": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
-      "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
+    "object.getownpropertydescriptors": {
+      "version": "2.1.4",
+      "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.4.tgz",
+      "integrity": "sha512-sccv3L/pMModT6dJAYF3fzGMVcb38ysQ0tEE6ixv2yXJDtEIPph268OlAdJj5/qZMZDq2g/jqvwppt36uS/uQQ==",
       "dev": true,
       "requires": {
-        "semver": "^6.0.0"
-      },
-      "dependencies": {
-        "semver": {
-          "version": "6.3.0",
-          "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
-          "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
-          "dev": true
-        }
+        "array.prototype.reduce": "^1.0.4",
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.4",
+        "es-abstract": "^1.20.1"
       }
     },
-    "make-fetch-happen": {
-      "version": "9.1.0",
-      "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz",
-      "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==",
+    "object.pick": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz",
+      "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=",
       "dev": true,
       "requires": {
-        "agentkeepalive": "^4.1.3",
-        "cacache": "^15.2.0",
-        "http-cache-semantics": "^4.1.0",
-        "http-proxy-agent": "^4.0.1",
-        "https-proxy-agent": "^5.0.0",
-        "is-lambda": "^1.0.1",
-        "lru-cache": "^6.0.0",
-        "minipass": "^3.1.3",
-        "minipass-collect": "^1.0.2",
-        "minipass-fetch": "^1.3.2",
-        "minipass-flush": "^1.0.5",
-        "minipass-pipeline": "^1.2.4",
-        "negotiator": "^0.6.2",
-        "promise-retry": "^2.0.1",
-        "socks-proxy-agent": "^6.0.0",
-        "ssri": "^8.0.0"
+        "isobject": "^3.0.1"
       }
     },
-    "map-age-cleaner": {
-      "version": "0.1.3",
-      "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz",
-      "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==",
+    "object.values": {
+      "version": "1.1.5",
+      "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz",
+      "integrity": "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==",
       "dev": true,
       "requires": {
-        "p-defer": "^1.0.0"
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.3",
+        "es-abstract": "^1.19.1"
       }
     },
-    "map-cache": {
-      "version": "0.2.2",
-      "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
-      "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=",
+    "obuf": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz",
+      "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==",
       "dev": true
     },
-    "map-visit": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz",
-      "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=",
+    "on-finished": {
+      "version": "2.4.1",
+      "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
+      "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
       "dev": true,
       "requires": {
-        "object-visit": "^1.0.0"
+        "ee-first": "1.1.1"
       }
     },
-    "mdn-data": {
-      "version": "2.0.14",
-      "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz",
-      "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==",
-      "dev": true
-    },
-    "media-typer": {
-      "version": "0.3.0",
-      "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
-      "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=",
+    "on-headers": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
+      "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==",
       "dev": true
     },
-    "mem": {
-      "version": "8.1.1",
-      "resolved": "https://registry.npmjs.org/mem/-/mem-8.1.1.tgz",
-      "integrity": "sha512-qFCFUDs7U3b8mBDPyz5EToEKoAkgCzqquIgi9nkkR9bixxOVOre+09lbuH7+9Kn2NFpm56M3GUWVbU2hQgdACA==",
+    "once": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+      "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
       "dev": true,
       "requires": {
-        "map-age-cleaner": "^0.1.3",
-        "mimic-fn": "^3.1.0"
-      },
-      "dependencies": {
-        "mimic-fn": {
-          "version": "3.1.0",
-          "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-3.1.0.tgz",
-          "integrity": "sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==",
-          "dev": true
-        }
+        "wrappy": "1"
       }
     },
-    "memfs": {
-      "version": "3.4.0",
-      "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.0.tgz",
-      "integrity": "sha512-o/RfP0J1d03YwsAxyHxAYs2kyJp55AFkMazlFAZFR2I2IXkxiUTXRabJ6RmNNCQ83LAD2jy52Khj0m3OffpNdA==",
+    "onetime": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
+      "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
       "dev": true,
       "requires": {
-        "fs-monkey": "1.0.3"
+        "mimic-fn": "^2.1.0"
       }
     },
-    "memory-fs": {
-      "version": "0.4.1",
-      "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz",
-      "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=",
+    "open": {
+      "version": "8.2.1",
+      "resolved": "https://registry.npmjs.org/open/-/open-8.2.1.tgz",
+      "integrity": "sha512-rXILpcQlkF/QuFez2BJDf3GsqpjGKbkUUToAIGo9A0Q6ZkoSGogZJulrUdwRkrAsoQvoZsrjCYt8+zblOk7JQQ==",
       "dev": true,
       "requires": {
-        "errno": "^0.1.3",
-        "readable-stream": "^2.0.1"
+        "define-lazy-prop": "^2.0.0",
+        "is-docker": "^2.1.1",
+        "is-wsl": "^2.2.0"
       }
     },
-    "merge-descriptors": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
-      "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=",
+    "opencollective-postinstall": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz",
+      "integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==",
       "dev": true
     },
-    "merge-source-map": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz",
-      "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==",
+    "opn": {
+      "version": "5.5.0",
+      "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz",
+      "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==",
       "dev": true,
       "requires": {
-        "source-map": "^0.6.1"
+        "is-wsl": "^1.1.0"
       },
       "dependencies": {
-        "source-map": {
-          "version": "0.6.1",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+        "is-wsl": {
+          "version": "1.1.0",
+          "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz",
+          "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=",
           "dev": true
         }
       }
     },
-    "merge-stream": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
-      "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
-      "dev": true
-    },
-    "merge2": {
-      "version": "1.4.1",
-      "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
-      "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
-      "dev": true
+    "optionator": {
+      "version": "0.8.3",
+      "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz",
+      "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==",
+      "dev": true,
+      "requires": {
+        "deep-is": "~0.1.3",
+        "fast-levenshtein": "~2.0.6",
+        "levn": "~0.3.0",
+        "prelude-ls": "~1.1.2",
+        "type-check": "~0.3.2",
+        "word-wrap": "~1.2.3"
+      }
     },
-    "methods": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
-      "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=",
-      "dev": true
+    "ora": {
+      "version": "5.4.1",
+      "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz",
+      "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==",
+      "dev": true,
+      "requires": {
+        "bl": "^4.1.0",
+        "chalk": "^4.1.0",
+        "cli-cursor": "^3.1.0",
+        "cli-spinners": "^2.5.0",
+        "is-interactive": "^1.0.0",
+        "is-unicode-supported": "^0.1.0",
+        "log-symbols": "^4.1.0",
+        "strip-ansi": "^6.0.0",
+        "wcwidth": "^1.0.1"
+      },
+      "dependencies": {
+        "ansi-styles": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+          "dev": true,
+          "requires": {
+            "color-convert": "^2.0.1"
+          }
+        },
+        "chalk": {
+          "version": "4.1.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+          "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "dev": true,
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
+        }
+      }
     },
-    "micromatch": {
-      "version": "4.0.4",
-      "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz",
-      "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==",
+    "original": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz",
+      "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==",
       "dev": true,
       "requires": {
-        "braces": "^3.0.1",
-        "picomatch": "^2.2.3"
+        "url-parse": "^1.4.3"
       }
     },
-    "mime": {
-      "version": "1.6.0",
-      "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
-      "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
+    "os-browserify": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz",
+      "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=",
       "dev": true
     },
-    "mime-db": {
-      "version": "1.51.0",
-      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz",
-      "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==",
-      "dev": true
+    "os-homedir": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
+      "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
+      "dev": true,
+      "optional": true
     },
-    "mime-types": {
-      "version": "2.1.34",
-      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz",
-      "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==",
+    "os-name": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/os-name/-/os-name-4.0.1.tgz",
+      "integrity": "sha512-xl9MAoU97MH1Xt5K9ERft2YfCAoaO6msy1OBA0ozxEC0x0TmIoE6K3QvgJMMZA9yKGLmHXNY/YZoDbiGDj4zYw==",
       "dev": true,
       "requires": {
-        "mime-db": "1.51.0"
+        "macos-release": "^2.5.0",
+        "windows-release": "^4.0.0"
       }
     },
-    "mimic-fn": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
-      "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+    "os-tmpdir": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
+      "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
       "dev": true
     },
-    "mini-css-extract-plugin": {
-      "version": "2.4.2",
-      "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.4.2.tgz",
-      "integrity": "sha512-ZmqShkn79D36uerdED+9qdo1ZYG8C1YsWvXu0UMJxurZnSdgz7gQKO2EGv8T55MhDqG3DYmGtizZNpM/UbTlcA==",
+    "p-all": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/p-all/-/p-all-2.1.0.tgz",
+      "integrity": "sha512-HbZxz5FONzz/z2gJfk6bFca0BCiSRF8jU3yCsWOen/vR6lZjfPOu/e7L3uFzTW1i0H8TlC3vqQstEJPQL4/uLA==",
       "dev": true,
       "requires": {
-        "schema-utils": "^3.1.0"
+        "p-map": "^2.0.0"
+      },
+      "dependencies": {
+        "p-map": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz",
+          "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==",
+          "dev": true
+        }
       }
     },
-    "minimalistic-assert": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
-      "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==",
+    "p-defer": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz",
+      "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=",
       "dev": true
     },
-    "minimatch": {
-      "version": "3.0.4",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
-      "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+    "p-event": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/p-event/-/p-event-4.2.0.tgz",
+      "integrity": "sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ==",
       "dev": true,
       "requires": {
-        "brace-expansion": "^1.1.7"
+        "p-timeout": "^3.1.0"
       }
     },
-    "minimist": {
-      "version": "1.2.5",
-      "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
-      "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
-    },
-    "minipass": {
-      "version": "3.1.5",
-      "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.5.tgz",
-      "integrity": "sha512-+8NzxD82XQoNKNrl1d/FSi+X8wAEWR+sbYAfIvub4Nz0d22plFG72CEVVaufV8PNf4qSslFTD8VMOxNVhHCjTw==",
+    "p-filter": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/p-filter/-/p-filter-2.1.0.tgz",
+      "integrity": "sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==",
       "dev": true,
       "requires": {
-        "yallist": "^4.0.0"
+        "p-map": "^2.0.0"
+      },
+      "dependencies": {
+        "p-map": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz",
+          "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==",
+          "dev": true
+        }
       }
     },
-    "minipass-collect": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz",
-      "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==",
-      "dev": true,
-      "requires": {
-        "minipass": "^3.0.0"
-      }
+    "p-finally": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
+      "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=",
+      "dev": true
     },
-    "minipass-fetch": {
-      "version": "1.4.1",
-      "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz",
-      "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==",
+    "p-limit": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+      "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
       "dev": true,
       "requires": {
-        "encoding": "^0.1.12",
-        "minipass": "^3.1.0",
-        "minipass-sized": "^1.0.3",
-        "minizlib": "^2.0.0"
+        "p-try": "^2.0.0"
       }
     },
-    "minipass-flush": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz",
-      "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==",
+    "p-locate": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
+      "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
       "dev": true,
       "requires": {
-        "minipass": "^3.0.0"
+        "p-limit": "^2.2.0"
       }
     },
-    "minipass-json-stream": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/minipass-json-stream/-/minipass-json-stream-1.0.1.tgz",
-      "integrity": "sha512-ODqY18UZt/I8k+b7rl2AENgbWE8IDYam+undIJONvigAz8KR5GWblsFTEfQs0WODsjbSXWlm+JHEv8Gr6Tfdbg==",
+    "p-map": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz",
+      "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==",
       "dev": true,
       "requires": {
-        "jsonparse": "^1.3.1",
-        "minipass": "^3.0.0"
+        "aggregate-error": "^3.0.0"
       }
     },
-    "minipass-pipeline": {
-      "version": "1.2.4",
-      "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz",
-      "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==",
+    "p-retry": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-3.0.1.tgz",
+      "integrity": "sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w==",
       "dev": true,
       "requires": {
-        "minipass": "^3.0.0"
+        "retry": "^0.12.0"
       }
     },
-    "minipass-sized": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz",
-      "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==",
+    "p-timeout": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz",
+      "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==",
       "dev": true,
       "requires": {
-        "minipass": "^3.0.0"
+        "p-finally": "^1.0.0"
       }
     },
-    "minizlib": {
-      "version": "2.1.2",
-      "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
-      "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
-      "dev": true,
-      "requires": {
-        "minipass": "^3.0.0",
-        "yallist": "^4.0.0"
-      }
+    "p-try": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+      "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+      "dev": true
     },
-    "mixin-deep": {
-      "version": "1.3.2",
-      "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz",
-      "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==",
+    "pacote": {
+      "version": "12.0.2",
+      "resolved": "https://registry.npmjs.org/pacote/-/pacote-12.0.2.tgz",
+      "integrity": "sha512-Ar3mhjcxhMzk+OVZ8pbnXdb0l8+pimvlsqBGRNkble2NVgyqOGE3yrCGi/lAYq7E7NRDMz89R1Wx5HIMCGgeYg==",
       "dev": true,
       "requires": {
-        "for-in": "^1.0.2",
-        "is-extendable": "^1.0.1"
-      },
-      "dependencies": {
-        "is-extendable": {
-          "version": "1.0.1",
-          "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
-          "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
-          "dev": true,
-          "requires": {
-            "is-plain-object": "^2.0.4"
-          }
-        }
+        "@npmcli/git": "^2.1.0",
+        "@npmcli/installed-package-contents": "^1.0.6",
+        "@npmcli/promise-spawn": "^1.2.0",
+        "@npmcli/run-script": "^2.0.0",
+        "cacache": "^15.0.5",
+        "chownr": "^2.0.0",
+        "fs-minipass": "^2.1.0",
+        "infer-owner": "^1.0.4",
+        "minipass": "^3.1.3",
+        "mkdirp": "^1.0.3",
+        "npm-package-arg": "^8.0.1",
+        "npm-packlist": "^3.0.0",
+        "npm-pick-manifest": "^6.0.0",
+        "npm-registry-fetch": "^11.0.0",
+        "promise-retry": "^2.0.1",
+        "read-package-json-fast": "^2.0.1",
+        "rimraf": "^3.0.2",
+        "ssri": "^8.0.1",
+        "tar": "^6.1.0"
       }
     },
-    "mkdirp": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
-      "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
-      "dev": true
-    },
-    "ms": {
-      "version": "2.1.2",
-      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
-      "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
-      "dev": true
+    "pako": {
+      "version": "1.0.11",
+      "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
+      "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
     },
-    "multicast-dns": {
-      "version": "6.2.3",
-      "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz",
-      "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==",
+    "parallel-transform": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz",
+      "integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==",
       "dev": true,
       "requires": {
-        "dns-packet": "^1.3.1",
-        "thunky": "^1.0.2"
+        "cyclist": "^1.0.1",
+        "inherits": "^2.0.3",
+        "readable-stream": "^2.1.5"
       }
     },
-    "multicast-dns-service-types": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz",
-      "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=",
-      "dev": true
-    },
-    "mute-stream": {
-      "version": "0.0.8",
-      "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
-      "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==",
-      "dev": true
-    },
-    "nanoid": {
-      "version": "3.3.1",
-      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz",
-      "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw=="
-    },
-    "nanomatch": {
-      "version": "1.2.13",
-      "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
-      "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==",
+    "param-case": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz",
+      "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==",
       "dev": true,
       "requires": {
-        "arr-diff": "^4.0.0",
-        "array-unique": "^0.3.2",
-        "define-property": "^2.0.2",
-        "extend-shallow": "^3.0.2",
-        "fragment-cache": "^0.2.1",
-        "is-windows": "^1.0.2",
-        "kind-of": "^6.0.2",
-        "object.pick": "^1.3.0",
-        "regex-not": "^1.0.0",
-        "snapdragon": "^0.8.1",
-        "to-regex": "^3.0.1"
-      }
-    },
-    "natural-compare": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
-      "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
-      "dev": true
+        "dot-case": "^3.0.4",
+        "tslib": "^2.0.3"
+      }
     },
-    "needle": {
-      "version": "2.9.1",
-      "resolved": "https://registry.npmjs.org/needle/-/needle-2.9.1.tgz",
-      "integrity": "sha512-6R9fqJ5Zcmf+uYaFgdIHmLwNldn5HbK8L5ybn7Uz+ylX/rnOsSp1AHcvQSrCaFN+qNM1wpymHqD7mVasEOlHGQ==",
+    "parent-module": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+      "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
       "dev": true,
-      "optional": true,
       "requires": {
-        "debug": "^3.2.6",
-        "iconv-lite": "^0.4.4",
-        "sax": "^1.2.4"
-      },
-      "dependencies": {
-        "debug": {
-          "version": "3.2.7",
-          "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
-          "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "ms": "^2.1.1"
-          }
-        }
+        "callsites": "^3.0.0"
       }
     },
-    "negotiator": {
-      "version": "0.6.2",
-      "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
-      "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==",
-      "dev": true
+    "parse-asn1": {
+      "version": "5.1.6",
+      "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz",
+      "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==",
+      "dev": true,
+      "requires": {
+        "asn1.js": "^5.2.0",
+        "browserify-aes": "^1.0.0",
+        "evp_bytestokey": "^1.0.0",
+        "pbkdf2": "^3.0.3",
+        "safe-buffer": "^5.1.1"
+      }
     },
-    "neo-async": {
-      "version": "2.6.2",
-      "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
-      "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
-      "dev": true
+    "parse-entities": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz",
+      "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==",
+      "dev": true,
+      "requires": {
+        "character-entities": "^1.0.0",
+        "character-entities-legacy": "^1.0.0",
+        "character-reference-invalid": "^1.0.0",
+        "is-alphanumerical": "^1.0.0",
+        "is-decimal": "^1.0.0",
+        "is-hexadecimal": "^1.0.0"
+      }
     },
-    "nice-napi": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz",
-      "integrity": "sha512-px/KnJAJZf5RuBGcfD+Sp2pAKq0ytz8j+1NehvgIGFkvtvFrDM3T8E4x/JJODXK9WZow8RRGrbA9QQ3hs+pDhA==",
+    "parse-json": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
+      "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
       "dev": true,
-      "optional": true,
       "requires": {
-        "node-addon-api": "^3.0.0",
-        "node-gyp-build": "^4.2.2"
+        "@babel/code-frame": "^7.0.0",
+        "error-ex": "^1.3.1",
+        "json-parse-even-better-errors": "^2.3.0",
+        "lines-and-columns": "^1.1.6"
       }
     },
-    "nice-try": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
-      "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
+    "parse-node-version": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz",
+      "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==",
       "dev": true
     },
-    "node-addon-api": {
-      "version": "3.2.1",
-      "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz",
-      "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==",
-      "dev": true,
+    "parse5": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz",
+      "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==",
       "optional": true
     },
-    "node-forge": {
-      "version": "0.10.0",
-      "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz",
-      "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==",
-      "dev": true
+    "parse5-html-rewriting-stream": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/parse5-html-rewriting-stream/-/parse5-html-rewriting-stream-6.0.1.tgz",
+      "integrity": "sha512-vwLQzynJVEfUlURxgnf51yAJDQTtVpNyGD8tKi2Za7m+akukNHxCcUQMAa/mUGLhCeicFdpy7Tlvj8ZNKadprg==",
+      "dev": true,
+      "requires": {
+        "parse5": "^6.0.1",
+        "parse5-sax-parser": "^6.0.1"
+      },
+      "dependencies": {
+        "parse5": {
+          "version": "6.0.1",
+          "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
+          "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==",
+          "dev": true
+        }
+      }
     },
-    "node-gyp": {
-      "version": "7.1.2",
-      "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-7.1.2.tgz",
-      "integrity": "sha512-CbpcIo7C3eMu3dL1c3d0xw449fHIGALIJsRP4DDPHpyiW8vcriNY7ubh9TE4zEKfSxscY7PjeFnshE7h75ynjQ==",
+    "parse5-htmlparser2-tree-adapter": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz",
+      "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==",
       "dev": true,
       "requires": {
-        "env-paths": "^2.2.0",
-        "glob": "^7.1.4",
-        "graceful-fs": "^4.2.3",
-        "nopt": "^5.0.0",
-        "npmlog": "^4.1.2",
-        "request": "^2.88.2",
-        "rimraf": "^3.0.2",
-        "semver": "^7.3.2",
-        "tar": "^6.0.2",
-        "which": "^2.0.2"
+        "parse5": "^6.0.1"
       },
       "dependencies": {
-        "which": {
-          "version": "2.0.2",
-          "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
-          "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
-          "dev": true,
-          "requires": {
-            "isexe": "^2.0.0"
-          }
+        "parse5": {
+          "version": "6.0.1",
+          "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
+          "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==",
+          "dev": true
         }
       }
     },
-    "node-gyp-build": {
-      "version": "4.3.0",
-      "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.3.0.tgz",
-      "integrity": "sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q==",
+    "parse5-sax-parser": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/parse5-sax-parser/-/parse5-sax-parser-6.0.1.tgz",
+      "integrity": "sha512-kXX+5S81lgESA0LsDuGjAlBybImAChYRMT+/uKCEXFBFOeEhS52qUCydGhU3qLRD8D9DVjaUo821WK7DM4iCeg==",
       "dev": true,
-      "optional": true
+      "requires": {
+        "parse5": "^6.0.1"
+      },
+      "dependencies": {
+        "parse5": {
+          "version": "6.0.1",
+          "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
+          "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==",
+          "dev": true
+        }
+      }
     },
-    "node-releases": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz",
-      "integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==",
+    "parseurl": {
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+      "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
       "dev": true
     },
-    "nopt": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz",
-      "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==",
+    "pascal-case": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz",
+      "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==",
       "dev": true,
       "requires": {
-        "abbrev": "1"
+        "no-case": "^3.0.4",
+        "tslib": "^2.0.3"
       }
     },
-    "normalize-path": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
-      "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+    "pascalcase": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
+      "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=",
       "dev": true
     },
-    "normalize-range": {
-      "version": "0.1.2",
-      "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz",
-      "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=",
+    "path-browserify": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz",
+      "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==",
       "dev": true
     },
-    "normalize-url": {
-      "version": "6.1.0",
-      "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz",
-      "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==",
+    "path-dirname": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz",
+      "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=",
       "dev": true
     },
-    "npm-bundled": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.2.tgz",
-      "integrity": "sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ==",
-      "dev": true,
-      "requires": {
-        "npm-normalize-package-bin": "^1.0.1"
-      }
-    },
-    "npm-install-checks": {
+    "path-exists": {
       "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-4.0.0.tgz",
-      "integrity": "sha512-09OmyDkNLYwqKPOnbI8exiOZU2GVVmQp7tgez2BPi5OZC8M82elDAps7sxC4l//uSUtotWqoEIDwjRvWH4qz8w==",
-      "dev": true,
-      "requires": {
-        "semver": "^7.1.1"
-      }
+      "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+      "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+      "dev": true
     },
-    "npm-normalize-package-bin": {
+    "path-is-absolute": {
       "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz",
-      "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==",
+      "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+      "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
       "dev": true
     },
-    "npm-package-arg": {
-      "version": "8.1.5",
-      "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-8.1.5.tgz",
-      "integrity": "sha512-LhgZrg0n0VgvzVdSm1oiZworPbTxYHUJCgtsJW8mGvlDpxTM1vSJc3m5QZeUkhAHIzbz3VCHd/R4osi1L1Tg/Q==",
-      "dev": true,
-      "requires": {
-        "hosted-git-info": "^4.0.1",
-        "semver": "^7.3.4",
-        "validate-npm-package-name": "^3.0.0"
-      }
+    "path-is-inside": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
+      "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=",
+      "dev": true
     },
-    "npm-packlist": {
-      "version": "2.2.2",
-      "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-2.2.2.tgz",
-      "integrity": "sha512-Jt01acDvJRhJGthnUJVF/w6gumWOZxO7IkpY/lsX9//zqQgnF7OJaxgQXcerd4uQOLu7W5bkb4mChL9mdfm+Zg==",
-      "dev": true,
-      "requires": {
-        "glob": "^7.1.6",
-        "ignore-walk": "^3.0.3",
-        "npm-bundled": "^1.1.1",
-        "npm-normalize-package-bin": "^1.0.1"
-      }
+    "path-key": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
+      "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
+      "dev": true
     },
-    "npm-pick-manifest": {
-      "version": "6.1.1",
-      "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-6.1.1.tgz",
-      "integrity": "sha512-dBsdBtORT84S8V8UTad1WlUyKIY9iMsAmqxHbLdeEeBNMLQDlDWWra3wYUx9EBEIiG/YwAy0XyNHDd2goAsfuA==",
-      "dev": true,
-      "requires": {
-        "npm-install-checks": "^4.0.0",
-        "npm-normalize-package-bin": "^1.0.1",
-        "npm-package-arg": "^8.1.2",
-        "semver": "^7.3.4"
-      }
+    "path-parse": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+      "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+      "dev": true
     },
-    "npm-registry-fetch": {
-      "version": "11.0.0",
-      "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-11.0.0.tgz",
-      "integrity": "sha512-jmlgSxoDNuhAtxUIG6pVwwtz840i994dL14FoNVZisrmZW5kWd63IUTNv1m/hyRSGSqWjCUp/YZlS1BJyNp9XA==",
-      "dev": true,
-      "requires": {
-        "make-fetch-happen": "^9.0.1",
-        "minipass": "^3.1.3",
-        "minipass-fetch": "^1.3.0",
-        "minipass-json-stream": "^1.0.1",
-        "minizlib": "^2.0.0",
-        "npm-package-arg": "^8.0.0"
-      }
+    "path-to-regexp": {
+      "version": "0.1.7",
+      "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
+      "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=",
+      "dev": true
     },
-    "npm-run-path": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
-      "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=",
+    "path-type": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
+      "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
+      "dev": true
+    },
+    "pause-stream": {
+      "version": "0.0.11",
+      "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz",
+      "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=",
       "dev": true,
       "requires": {
-        "path-key": "^2.0.0"
+        "through": "~2.3"
       }
     },
-    "npmlog": {
-      "version": "4.1.2",
-      "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
-      "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
+    "pbkdf2": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz",
+      "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==",
       "dev": true,
       "requires": {
-        "are-we-there-yet": "~1.1.2",
-        "console-control-strings": "~1.1.0",
-        "gauge": "~2.7.3",
-        "set-blocking": "~2.0.0"
+        "create-hash": "^1.1.2",
+        "create-hmac": "^1.1.4",
+        "ripemd160": "^2.0.1",
+        "safe-buffer": "^5.0.1",
+        "sha.js": "^2.4.8"
       }
     },
-    "nth-check": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz",
-      "integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==",
+    "pdfjs-dist": {
+      "version": "2.14.305",
+      "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-2.14.305.tgz",
+      "integrity": "sha512-5f7i25J1dKIBczhgfxEgNxfYNIxXEdxqo6Qb4ehY7Ja+p6AI4uUmk/OcVGXfRGm2ys5iaJJhJUwBFwv6Jl/Qww==",
       "dev": true,
       "requires": {
-        "boolbase": "^1.0.0"
+        "dommatrix": "^1.0.1",
+        "web-streams-polyfill": "^3.2.1"
       }
     },
-    "num2fraction": {
-      "version": "1.2.2",
-      "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz",
-      "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=",
-      "dev": true
-    },
-    "number-is-nan": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
-      "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
-      "dev": true
-    },
-    "oauth-sign": {
-      "version": "0.9.0",
-      "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
-      "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==",
-      "dev": true
-    },
-    "object-assign": {
-      "version": "4.1.1",
-      "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
-      "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
-      "dev": true
-    },
-    "object-copy": {
-      "version": "0.1.0",
-      "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz",
-      "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=",
+    "pdfmake": {
+      "version": "0.2.5",
+      "resolved": "https://registry.npmjs.org/pdfmake/-/pdfmake-0.2.5.tgz",
+      "integrity": "sha512-NlayjehMtuZEdw2Lyipf/MxOCR2vATZQ7jn8cH0/dHwsNb+mqof9/6SW4jZT5p+So4qz+0mD21KG81+dDQSEhA==",
       "dev": true,
       "requires": {
-        "copy-descriptor": "^0.1.0",
-        "define-property": "^0.2.5",
-        "kind-of": "^3.0.3"
+        "@foliojs-fork/linebreak": "^1.1.1",
+        "@foliojs-fork/pdfkit": "^0.13.0",
+        "iconv-lite": "^0.6.3",
+        "xmldoc": "^1.1.2"
       },
       "dependencies": {
-        "define-property": {
-          "version": "0.2.5",
-          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
-          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
-          "dev": true,
-          "requires": {
-            "is-descriptor": "^0.1.0"
-          }
-        },
-        "kind-of": {
-          "version": "3.2.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+        "iconv-lite": {
+          "version": "0.6.3",
+          "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+          "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
           "dev": true,
           "requires": {
-            "is-buffer": "^1.1.5"
+            "safer-buffer": ">= 2.1.2 < 3.0.0"
           }
         }
       }
     },
-    "object-is": {
-      "version": "1.1.5",
-      "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz",
-      "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==",
-      "dev": true,
-      "requires": {
-        "call-bind": "^1.0.2",
-        "define-properties": "^1.1.3"
-      }
+    "picocolors": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
+      "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
     },
-    "object-keys": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
-      "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+    "picomatch": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+      "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
       "dev": true
     },
-    "object-visit": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz",
-      "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=",
-      "dev": true,
-      "requires": {
-        "isobject": "^3.0.0"
-      }
-    },
-    "object.assign": {
-      "version": "4.1.2",
-      "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz",
-      "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==",
-      "dev": true,
-      "requires": {
-        "call-bind": "^1.0.0",
-        "define-properties": "^1.1.3",
-        "has-symbols": "^1.0.1",
-        "object-keys": "^1.1.1"
-      }
-    },
-    "object.pick": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz",
-      "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=",
-      "dev": true,
-      "requires": {
-        "isobject": "^3.0.1"
-      }
+    "pify": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
+      "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
+      "dev": true
     },
-    "obuf": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz",
-      "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==",
+    "pinkie": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
+      "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=",
       "dev": true
     },
-    "on-finished": {
-      "version": "2.3.0",
-      "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
-      "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
+    "pinkie-promise": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
+      "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
       "dev": true,
       "requires": {
-        "ee-first": "1.1.1"
+        "pinkie": "^2.0.0"
       }
     },
-    "on-headers": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
-      "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==",
+    "pirates": {
+      "version": "4.0.5",
+      "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz",
+      "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==",
       "dev": true
     },
-    "once": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
-      "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+    "piscina": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/piscina/-/piscina-3.1.0.tgz",
+      "integrity": "sha512-KTW4sjsCD34MHrUbx9eAAbuUSpVj407hQSgk/6Epkg0pbRBmv4a3UX7Sr8wxm9xYqQLnsN4mFOjqGDzHAdgKQg==",
       "dev": true,
       "requires": {
-        "wrappy": "1"
+        "eventemitter-asyncresource": "^1.0.0",
+        "hdr-histogram-js": "^2.0.1",
+        "hdr-histogram-percentiles-obj": "^3.0.0",
+        "nice-napi": "^1.0.2"
       }
     },
-    "onetime": {
-      "version": "5.1.2",
-      "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
-      "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+    "pkg-dir": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
+      "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
       "dev": true,
       "requires": {
-        "mimic-fn": "^2.1.0"
+        "find-up": "^4.0.0"
       }
     },
-    "open": {
-      "version": "8.2.1",
-      "resolved": "https://registry.npmjs.org/open/-/open-8.2.1.tgz",
-      "integrity": "sha512-rXILpcQlkF/QuFez2BJDf3GsqpjGKbkUUToAIGo9A0Q6ZkoSGogZJulrUdwRkrAsoQvoZsrjCYt8+zblOk7JQQ==",
+    "png-js": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/png-js/-/png-js-1.0.0.tgz",
+      "integrity": "sha512-k+YsbhpA9e+EFfKjTCH3VW6aoKlyNYI6NYdTfDL4CIvFnvsuO84ttonmZE7rc+v23SLTH8XX+5w/Ak9v0xGY4g==",
+      "dev": true
+    },
+    "pnp-webpack-plugin": {
+      "version": "1.6.4",
+      "resolved": "https://registry.npmjs.org/pnp-webpack-plugin/-/pnp-webpack-plugin-1.6.4.tgz",
+      "integrity": "sha512-7Wjy+9E3WwLOEL30D+m8TSTF7qJJUJLONBnwQp0518siuMxUQUbgZwssaFX+QKlZkjHZcw/IpZCt/H0srrntSg==",
       "dev": true,
       "requires": {
-        "define-lazy-prop": "^2.0.0",
-        "is-docker": "^2.1.1",
-        "is-wsl": "^2.2.0"
+        "ts-pnp": "^1.1.6"
       }
     },
-    "opn": {
-      "version": "5.5.0",
-      "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz",
-      "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==",
+    "polished": {
+      "version": "4.2.2",
+      "resolved": "https://registry.npmjs.org/polished/-/polished-4.2.2.tgz",
+      "integrity": "sha512-Sz2Lkdxz6F2Pgnpi9U5Ng/WdWAUZxmHrNPoVlm3aAemxoy2Qy7LGjQg4uf8qKelDAUW94F4np3iH2YPf2qefcQ==",
       "dev": true,
       "requires": {
-        "is-wsl": "^1.1.0"
+        "@babel/runtime": "^7.17.8"
       },
       "dependencies": {
-        "is-wsl": {
-          "version": "1.1.0",
-          "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz",
-          "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=",
-          "dev": true
+        "@babel/runtime": {
+          "version": "7.18.0",
+          "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.0.tgz",
+          "integrity": "sha512-YMQvx/6nKEaucl0MY56mwIG483xk8SDNdlUwb2Ts6FUpr7fm85DxEmsY18LXBNhcTz6tO6JwZV8w1W06v8UKeg==",
+          "dev": true,
+          "requires": {
+            "regenerator-runtime": "^0.13.4"
+          }
         }
       }
     },
-    "optionator": {
-      "version": "0.9.1",
-      "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
-      "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==",
-      "dev": true,
-      "requires": {
-        "deep-is": "^0.1.3",
-        "fast-levenshtein": "^2.0.6",
-        "levn": "^0.4.1",
-        "prelude-ls": "^1.2.1",
-        "type-check": "^0.4.0",
-        "word-wrap": "^1.2.3"
-      }
-    },
-    "ora": {
-      "version": "5.4.1",
-      "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz",
-      "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==",
+    "portfinder": {
+      "version": "1.0.28",
+      "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz",
+      "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==",
       "dev": true,
       "requires": {
-        "bl": "^4.1.0",
-        "chalk": "^4.1.0",
-        "cli-cursor": "^3.1.0",
-        "cli-spinners": "^2.5.0",
-        "is-interactive": "^1.0.0",
-        "is-unicode-supported": "^0.1.0",
-        "log-symbols": "^4.1.0",
-        "strip-ansi": "^6.0.0",
-        "wcwidth": "^1.0.1"
+        "async": "^2.6.2",
+        "debug": "^3.1.1",
+        "mkdirp": "^0.5.5"
       },
       "dependencies": {
-        "ansi-regex": {
-          "version": "5.0.1",
-          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
-          "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
-          "dev": true
-        },
-        "ansi-styles": {
-          "version": "4.3.0",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
-          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
-          "dev": true,
-          "requires": {
-            "color-convert": "^2.0.1"
-          }
-        },
-        "chalk": {
-          "version": "4.1.2",
-          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
-          "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
-          "dev": true,
-          "requires": {
-            "ansi-styles": "^4.1.0",
-            "supports-color": "^7.1.0"
-          }
-        },
-        "color-convert": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
-          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+        "debug": {
+          "version": "3.2.7",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+          "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
           "dev": true,
           "requires": {
-            "color-name": "~1.1.4"
+            "ms": "^2.1.1"
           }
-        },
-        "color-name": {
-          "version": "1.1.4",
-          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
-          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
-          "dev": true
-        },
-        "has-flag": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
-          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
-          "dev": true
-        },
-        "strip-ansi": {
-          "version": "6.0.1",
-          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
-          "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+        },
+        "mkdirp": {
+          "version": "0.5.6",
+          "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
+          "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
           "dev": true,
           "requires": {
-            "ansi-regex": "^5.0.1"
+            "minimist": "^1.2.6"
           }
+        }
+      }
+    },
+    "posix-character-classes": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
+      "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=",
+      "dev": true
+    },
+    "postcss": {
+      "version": "8.4.14",
+      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz",
+      "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==",
+      "requires": {
+        "nanoid": "^3.3.4",
+        "picocolors": "^1.0.0",
+        "source-map-js": "^1.0.2"
+      }
+    },
+    "postcss-attribute-case-insensitive": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-4.0.2.tgz",
+      "integrity": "sha512-clkFxk/9pcdb4Vkn0hAHq3YnxBQ2p0CGD1dy24jN+reBck+EWxMbxSUqN4Yj7t0w8csl87K6p0gxBe1utkJsYA==",
+      "dev": true,
+      "requires": {
+        "postcss": "^7.0.2",
+        "postcss-selector-parser": "^6.0.2"
+      },
+      "dependencies": {
+        "picocolors": {
+          "version": "0.2.1",
+          "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz",
+          "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==",
+          "dev": true
         },
-        "supports-color": {
-          "version": "7.2.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
-          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+        "postcss": {
+          "version": "7.0.39",
+          "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz",
+          "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==",
           "dev": true,
           "requires": {
-            "has-flag": "^4.0.0"
+            "picocolors": "^0.2.1",
+            "source-map": "^0.6.1"
           }
+        },
+        "source-map": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+          "dev": true
         }
       }
     },
-    "original": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz",
-      "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==",
+    "postcss-calc": {
+      "version": "8.2.4",
+      "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.2.4.tgz",
+      "integrity": "sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==",
       "dev": true,
       "requires": {
-        "url-parse": "^1.4.3"
+        "postcss-selector-parser": "^6.0.9",
+        "postcss-value-parser": "^4.2.0"
       }
     },
-    "os-tmpdir": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
-      "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
-      "dev": true
-    },
-    "p-defer": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz",
-      "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=",
-      "dev": true
-    },
-    "p-finally": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
-      "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=",
-      "dev": true
-    },
-    "p-limit": {
-      "version": "2.3.0",
-      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
-      "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+    "postcss-color-functional-notation": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-2.0.1.tgz",
+      "integrity": "sha512-ZBARCypjEDofW4P6IdPVTLhDNXPRn8T2s1zHbZidW6rPaaZvcnCS2soYFIQJrMZSxiePJ2XIYTlcb2ztr/eT2g==",
       "dev": true,
       "requires": {
-        "p-try": "^2.0.0"
+        "postcss": "^7.0.2",
+        "postcss-values-parser": "^2.0.0"
+      },
+      "dependencies": {
+        "picocolors": {
+          "version": "0.2.1",
+          "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz",
+          "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==",
+          "dev": true
+        },
+        "postcss": {
+          "version": "7.0.39",
+          "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz",
+          "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==",
+          "dev": true,
+          "requires": {
+            "picocolors": "^0.2.1",
+            "source-map": "^0.6.1"
+          }
+        },
+        "source-map": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+          "dev": true
+        }
       }
     },
-    "p-locate": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
-      "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+    "postcss-color-gray": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/postcss-color-gray/-/postcss-color-gray-5.0.0.tgz",
+      "integrity": "sha512-q6BuRnAGKM/ZRpfDascZlIZPjvwsRye7UDNalqVz3s7GDxMtqPY6+Q871liNxsonUw8oC61OG+PSaysYpl1bnw==",
       "dev": true,
       "requires": {
-        "p-limit": "^2.0.0"
+        "@csstools/convert-colors": "^1.4.0",
+        "postcss": "^7.0.5",
+        "postcss-values-parser": "^2.0.0"
+      },
+      "dependencies": {
+        "picocolors": {
+          "version": "0.2.1",
+          "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz",
+          "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==",
+          "dev": true
+        },
+        "postcss": {
+          "version": "7.0.39",
+          "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz",
+          "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==",
+          "dev": true,
+          "requires": {
+            "picocolors": "^0.2.1",
+            "source-map": "^0.6.1"
+          }
+        },
+        "source-map": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+          "dev": true
+        }
       }
     },
-    "p-map": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz",
-      "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==",
+    "postcss-color-hex-alpha": {
+      "version": "5.0.3",
+      "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-5.0.3.tgz",
+      "integrity": "sha512-PF4GDel8q3kkreVXKLAGNpHKilXsZ6xuu+mOQMHWHLPNyjiUBOr75sp5ZKJfmv1MCus5/DWUGcK9hm6qHEnXYw==",
       "dev": true,
       "requires": {
-        "aggregate-error": "^3.0.0"
+        "postcss": "^7.0.14",
+        "postcss-values-parser": "^2.0.1"
+      },
+      "dependencies": {
+        "picocolors": {
+          "version": "0.2.1",
+          "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz",
+          "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==",
+          "dev": true
+        },
+        "postcss": {
+          "version": "7.0.39",
+          "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz",
+          "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==",
+          "dev": true,
+          "requires": {
+            "picocolors": "^0.2.1",
+            "source-map": "^0.6.1"
+          }
+        },
+        "source-map": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+          "dev": true
+        }
       }
     },
-    "p-retry": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-3.0.1.tgz",
-      "integrity": "sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w==",
+    "postcss-color-mod-function": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/postcss-color-mod-function/-/postcss-color-mod-function-3.0.3.tgz",
+      "integrity": "sha512-YP4VG+xufxaVtzV6ZmhEtc+/aTXH3d0JLpnYfxqTvwZPbJhWqp8bSY3nfNzNRFLgB4XSaBA82OE4VjOOKpCdVQ==",
       "dev": true,
       "requires": {
-        "retry": "^0.12.0"
+        "@csstools/convert-colors": "^1.4.0",
+        "postcss": "^7.0.2",
+        "postcss-values-parser": "^2.0.0"
+      },
+      "dependencies": {
+        "picocolors": {
+          "version": "0.2.1",
+          "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz",
+          "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==",
+          "dev": true
+        },
+        "postcss": {
+          "version": "7.0.39",
+          "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz",
+          "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==",
+          "dev": true,
+          "requires": {
+            "picocolors": "^0.2.1",
+            "source-map": "^0.6.1"
+          }
+        },
+        "source-map": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+          "dev": true
+        }
       }
     },
-    "p-try": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
-      "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
-      "dev": true
-    },
-    "pacote": {
-      "version": "11.3.5",
-      "resolved": "https://registry.npmjs.org/pacote/-/pacote-11.3.5.tgz",
-      "integrity": "sha512-fT375Yczn4zi+6Hkk2TBe1x1sP8FgFsEIZ2/iWaXY2r/NkhDJfxbcn5paz1+RTFCyNf+dPnaoBDJoAxXSU8Bkg==",
+    "postcss-color-rebeccapurple": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-4.0.1.tgz",
+      "integrity": "sha512-aAe3OhkS6qJXBbqzvZth2Au4V3KieR5sRQ4ptb2b2O8wgvB3SJBsdG+jsn2BZbbwekDG8nTfcCNKcSfe/lEy8g==",
       "dev": true,
       "requires": {
-        "@npmcli/git": "^2.1.0",
-        "@npmcli/installed-package-contents": "^1.0.6",
-        "@npmcli/promise-spawn": "^1.2.0",
-        "@npmcli/run-script": "^1.8.2",
-        "cacache": "^15.0.5",
-        "chownr": "^2.0.0",
-        "fs-minipass": "^2.1.0",
-        "infer-owner": "^1.0.4",
-        "minipass": "^3.1.3",
-        "mkdirp": "^1.0.3",
-        "npm-package-arg": "^8.0.1",
-        "npm-packlist": "^2.1.4",
-        "npm-pick-manifest": "^6.0.0",
-        "npm-registry-fetch": "^11.0.0",
-        "promise-retry": "^2.0.1",
-        "read-package-json-fast": "^2.0.1",
-        "rimraf": "^3.0.2",
-        "ssri": "^8.0.1",
-        "tar": "^6.1.0"
+        "postcss": "^7.0.2",
+        "postcss-values-parser": "^2.0.0"
+      },
+      "dependencies": {
+        "picocolors": {
+          "version": "0.2.1",
+          "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz",
+          "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==",
+          "dev": true
+        },
+        "postcss": {
+          "version": "7.0.39",
+          "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz",
+          "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==",
+          "dev": true,
+          "requires": {
+            "picocolors": "^0.2.1",
+            "source-map": "^0.6.1"
+          }
+        },
+        "source-map": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+          "dev": true
+        }
       }
     },
-    "pako": {
-      "version": "1.0.11",
-      "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
-      "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
-    },
-    "parent-module": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
-      "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+    "postcss-colormin": {
+      "version": "5.3.0",
+      "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.3.0.tgz",
+      "integrity": "sha512-WdDO4gOFG2Z8n4P8TWBpshnL3JpmNmJwdnfP2gbk2qBA8PWwOYcmjmI/t3CmMeL72a7Hkd+x/Mg9O2/0rD54Pg==",
       "dev": true,
       "requires": {
-        "callsites": "^3.0.0"
+        "browserslist": "^4.16.6",
+        "caniuse-api": "^3.0.0",
+        "colord": "^2.9.1",
+        "postcss-value-parser": "^4.2.0"
       }
     },
-    "parse-json": {
-      "version": "5.2.0",
-      "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
-      "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
+    "postcss-convert-values": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.1.1.tgz",
+      "integrity": "sha512-UjcYfl3wJJdcabGKk8lgetPvhi1Et7VDc3sYr9EyhNBeB00YD4vHgPBp+oMVoG/dDWCc6ASbmzPNV6jADTwh8Q==",
       "dev": true,
       "requires": {
-        "@babel/code-frame": "^7.0.0",
-        "error-ex": "^1.3.1",
-        "json-parse-even-better-errors": "^2.3.0",
-        "lines-and-columns": "^1.1.6"
+        "browserslist": "^4.20.3",
+        "postcss-value-parser": "^4.2.0"
       }
     },
-    "parse-node-version": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz",
-      "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==",
-      "dev": true
-    },
-    "parse5": {
-      "version": "5.1.1",
-      "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz",
-      "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==",
-      "optional": true
-    },
-    "parse5-html-rewriting-stream": {
-      "version": "6.0.1",
-      "resolved": "https://registry.npmjs.org/parse5-html-rewriting-stream/-/parse5-html-rewriting-stream-6.0.1.tgz",
-      "integrity": "sha512-vwLQzynJVEfUlURxgnf51yAJDQTtVpNyGD8tKi2Za7m+akukNHxCcUQMAa/mUGLhCeicFdpy7Tlvj8ZNKadprg==",
+    "postcss-custom-media": {
+      "version": "7.0.8",
+      "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-7.0.8.tgz",
+      "integrity": "sha512-c9s5iX0Ge15o00HKbuRuTqNndsJUbaXdiNsksnVH8H4gdc+zbLzr/UasOwNG6CTDpLFekVY4672eWdiiWu2GUg==",
       "dev": true,
       "requires": {
-        "parse5": "^6.0.1",
-        "parse5-sax-parser": "^6.0.1"
+        "postcss": "^7.0.14"
       },
       "dependencies": {
-        "parse5": {
-          "version": "6.0.1",
-          "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
-          "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==",
+        "picocolors": {
+          "version": "0.2.1",
+          "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz",
+          "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==",
+          "dev": true
+        },
+        "postcss": {
+          "version": "7.0.39",
+          "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz",
+          "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==",
+          "dev": true,
+          "requires": {
+            "picocolors": "^0.2.1",
+            "source-map": "^0.6.1"
+          }
+        },
+        "source-map": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
           "dev": true
         }
       }
     },
-    "parse5-htmlparser2-tree-adapter": {
-      "version": "6.0.1",
-      "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz",
-      "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==",
+    "postcss-custom-properties": {
+      "version": "8.0.11",
+      "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-8.0.11.tgz",
+      "integrity": "sha512-nm+o0eLdYqdnJ5abAJeXp4CEU1c1k+eB2yMCvhgzsds/e0umabFrN6HoTy/8Q4K5ilxERdl/JD1LO5ANoYBeMA==",
       "dev": true,
       "requires": {
-        "parse5": "^6.0.1"
+        "postcss": "^7.0.17",
+        "postcss-values-parser": "^2.0.1"
       },
       "dependencies": {
-        "parse5": {
-          "version": "6.0.1",
-          "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
-          "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==",
+        "picocolors": {
+          "version": "0.2.1",
+          "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz",
+          "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==",
+          "dev": true
+        },
+        "postcss": {
+          "version": "7.0.39",
+          "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz",
+          "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==",
+          "dev": true,
+          "requires": {
+            "picocolors": "^0.2.1",
+            "source-map": "^0.6.1"
+          }
+        },
+        "source-map": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
           "dev": true
         }
       }
     },
-    "parse5-sax-parser": {
-      "version": "6.0.1",
-      "resolved": "https://registry.npmjs.org/parse5-sax-parser/-/parse5-sax-parser-6.0.1.tgz",
-      "integrity": "sha512-kXX+5S81lgESA0LsDuGjAlBybImAChYRMT+/uKCEXFBFOeEhS52qUCydGhU3qLRD8D9DVjaUo821WK7DM4iCeg==",
+    "postcss-custom-selectors": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-5.1.2.tgz",
+      "integrity": "sha512-DSGDhqinCqXqlS4R7KGxL1OSycd1lydugJ1ky4iRXPHdBRiozyMHrdu0H3o7qNOCiZwySZTUI5MV0T8QhCLu+w==",
       "dev": true,
       "requires": {
-        "parse5": "^6.0.1"
+        "postcss": "^7.0.2",
+        "postcss-selector-parser": "^5.0.0-rc.3"
       },
       "dependencies": {
-        "parse5": {
-          "version": "6.0.1",
-          "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
-          "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==",
+        "cssesc": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz",
+          "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==",
           "dev": true
-        }
-      }
-    },
-    "parseurl": {
-      "version": "1.3.3",
-      "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
-      "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
-      "dev": true
-    },
-    "pascalcase": {
-      "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
-      "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=",
-      "dev": true
-    },
-    "path-dirname": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz",
-      "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=",
-      "dev": true
-    },
-    "path-exists": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
-      "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
-      "dev": true
-    },
-    "path-is-absolute": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
-      "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
-      "dev": true
-    },
-    "path-is-inside": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
-      "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=",
-      "dev": true
-    },
-    "path-key": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
-      "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
-      "dev": true
-    },
-    "path-parse": {
-      "version": "1.0.7",
-      "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
-      "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
-      "dev": true
-    },
-    "path-to-regexp": {
-      "version": "0.1.7",
-      "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
-      "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=",
-      "dev": true
-    },
-    "path-type": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
-      "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
-      "dev": true
-    },
-    "performance-now": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
-      "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=",
-      "dev": true
-    },
-    "picocolors": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
-      "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
-    },
-    "picomatch": {
-      "version": "2.3.0",
-      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz",
-      "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==",
-      "dev": true
-    },
-    "pify": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
-      "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
-      "dev": true
-    },
-    "pinkie": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
-      "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=",
-      "dev": true
-    },
-    "pinkie-promise": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
-      "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
-      "dev": true,
-      "requires": {
-        "pinkie": "^2.0.0"
-      }
-    },
-    "piscina": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/piscina/-/piscina-3.1.0.tgz",
-      "integrity": "sha512-KTW4sjsCD34MHrUbx9eAAbuUSpVj407hQSgk/6Epkg0pbRBmv4a3UX7Sr8wxm9xYqQLnsN4mFOjqGDzHAdgKQg==",
-      "dev": true,
-      "requires": {
-        "eventemitter-asyncresource": "^1.0.0",
-        "hdr-histogram-js": "^2.0.1",
-        "hdr-histogram-percentiles-obj": "^3.0.0",
-        "nice-napi": "^1.0.2"
+        },
+        "picocolors": {
+          "version": "0.2.1",
+          "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz",
+          "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==",
+          "dev": true
+        },
+        "postcss": {
+          "version": "7.0.39",
+          "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz",
+          "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==",
+          "dev": true,
+          "requires": {
+            "picocolors": "^0.2.1",
+            "source-map": "^0.6.1"
+          }
+        },
+        "postcss-selector-parser": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz",
+          "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==",
+          "dev": true,
+          "requires": {
+            "cssesc": "^2.0.0",
+            "indexes-of": "^1.0.1",
+            "uniq": "^1.0.1"
+          }
+        },
+        "source-map": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+          "dev": true
+        }
       }
     },
-    "pkg-dir": {
-      "version": "4.2.0",
-      "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
-      "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
+    "postcss-dir-pseudo-class": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-5.0.0.tgz",
+      "integrity": "sha512-3pm4oq8HYWMZePJY+5ANriPs3P07q+LW6FAdTlkFH2XqDdP4HeeJYMOzn0HYLhRSjBO3fhiqSwwU9xEULSrPgw==",
       "dev": true,
       "requires": {
-        "find-up": "^4.0.0"
+        "postcss": "^7.0.2",
+        "postcss-selector-parser": "^5.0.0-rc.3"
       },
       "dependencies": {
-        "find-up": {
-          "version": "4.1.0",
-          "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
-          "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+        "cssesc": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz",
+          "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==",
+          "dev": true
+        },
+        "picocolors": {
+          "version": "0.2.1",
+          "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz",
+          "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==",
+          "dev": true
+        },
+        "postcss": {
+          "version": "7.0.39",
+          "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz",
+          "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==",
           "dev": true,
           "requires": {
-            "locate-path": "^5.0.0",
-            "path-exists": "^4.0.0"
+            "picocolors": "^0.2.1",
+            "source-map": "^0.6.1"
           }
         },
-        "locate-path": {
+        "postcss-selector-parser": {
           "version": "5.0.0",
-          "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
-          "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+          "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz",
+          "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==",
           "dev": true,
           "requires": {
-            "p-locate": "^4.1.0"
+            "cssesc": "^2.0.0",
+            "indexes-of": "^1.0.1",
+            "uniq": "^1.0.1"
           }
         },
-        "p-locate": {
-          "version": "4.1.0",
-          "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
-          "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+        "source-map": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+          "dev": true
+        }
+      }
+    },
+    "postcss-discard-comments": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.1.1.tgz",
+      "integrity": "sha512-5JscyFmvkUxz/5/+TB3QTTT9Gi9jHkcn8dcmmuN68JQcv3aQg4y88yEHHhwFB52l/NkaJ43O0dbksGMAo49nfQ==",
+      "dev": true
+    },
+    "postcss-discard-duplicates": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz",
+      "integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==",
+      "dev": true
+    },
+    "postcss-discard-empty": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz",
+      "integrity": "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==",
+      "dev": true
+    },
+    "postcss-discard-overridden": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz",
+      "integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==",
+      "dev": true
+    },
+    "postcss-double-position-gradients": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-1.0.0.tgz",
+      "integrity": "sha512-G+nV8EnQq25fOI8CH/B6krEohGWnF5+3A6H/+JEpOncu5dCnkS1QQ6+ct3Jkaepw1NGVqqOZH6lqrm244mCftA==",
+      "dev": true,
+      "requires": {
+        "postcss": "^7.0.5",
+        "postcss-values-parser": "^2.0.0"
+      },
+      "dependencies": {
+        "picocolors": {
+          "version": "0.2.1",
+          "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz",
+          "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==",
+          "dev": true
+        },
+        "postcss": {
+          "version": "7.0.39",
+          "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz",
+          "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==",
           "dev": true,
           "requires": {
-            "p-limit": "^2.2.0"
+            "picocolors": "^0.2.1",
+            "source-map": "^0.6.1"
           }
         },
-        "path-exists": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
-          "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+        "source-map": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
           "dev": true
         }
       }
     },
-    "portfinder": {
-      "version": "1.0.28",
-      "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz",
-      "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==",
+    "postcss-env-function": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/postcss-env-function/-/postcss-env-function-2.0.2.tgz",
+      "integrity": "sha512-rwac4BuZlITeUbiBq60h/xbLzXY43qOsIErngWa4l7Mt+RaSkT7QBjXVGTcBHupykkblHMDrBFh30zchYPaOUw==",
       "dev": true,
       "requires": {
-        "async": "^2.6.2",
-        "debug": "^3.1.1",
-        "mkdirp": "^0.5.5"
+        "postcss": "^7.0.2",
+        "postcss-values-parser": "^2.0.0"
       },
       "dependencies": {
-        "debug": {
-          "version": "3.2.7",
-          "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
-          "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
-          "dev": true,
-          "requires": {
-            "ms": "^2.1.1"
-          }
+        "picocolors": {
+          "version": "0.2.1",
+          "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz",
+          "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==",
+          "dev": true
         },
-        "mkdirp": {
-          "version": "0.5.5",
-          "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
-          "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
+        "postcss": {
+          "version": "7.0.39",
+          "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz",
+          "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==",
           "dev": true,
           "requires": {
-            "minimist": "^1.2.5"
+            "picocolors": "^0.2.1",
+            "source-map": "^0.6.1"
           }
+        },
+        "source-map": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+          "dev": true
         }
       }
     },
-    "posix-character-classes": {
-      "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
-      "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=",
-      "dev": true
-    },
-    "postcss": {
-      "version": "8.4.4",
-      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.4.tgz",
-      "integrity": "sha512-joU6fBsN6EIer28Lj6GDFoC/5yOZzLCfn0zHAn/MYXI7aPt4m4hK5KC5ovEZXy+lnCjmYIbQWngvju2ddyEr8Q==",
-      "requires": {
-        "nanoid": "^3.1.30",
-        "picocolors": "^1.0.0",
-        "source-map-js": "^1.0.1"
-      }
-    },
-    "postcss-attribute-case-insensitive": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-4.0.2.tgz",
-      "integrity": "sha512-clkFxk/9pcdb4Vkn0hAHq3YnxBQ2p0CGD1dy24jN+reBck+EWxMbxSUqN4Yj7t0w8csl87K6p0gxBe1utkJsYA==",
+    "postcss-flexbugs-fixes": {
+      "version": "4.2.1",
+      "resolved": "https://registry.npmjs.org/postcss-flexbugs-fixes/-/postcss-flexbugs-fixes-4.2.1.tgz",
+      "integrity": "sha512-9SiofaZ9CWpQWxOwRh1b/r85KD5y7GgvsNt1056k6OYLvWUun0czCvogfJgylC22uJTwW1KzY3Gz65NZRlvoiQ==",
       "dev": true,
       "requires": {
-        "postcss": "^7.0.2",
-        "postcss-selector-parser": "^6.0.2"
+        "postcss": "^7.0.26"
       },
       "dependencies": {
         "picocolors": {
@@ -8694,24 +20147,46 @@
         }
       }
     },
-    "postcss-calc": {
-      "version": "8.0.0",
-      "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.0.0.tgz",
-      "integrity": "sha512-5NglwDrcbiy8XXfPM11F3HeC6hoT9W7GUH/Zi5U/p7u3Irv4rHhdDcIZwG0llHXV4ftsBjpfWMXAnXNl4lnt8g==",
+    "postcss-focus-visible": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-4.0.0.tgz",
+      "integrity": "sha512-Z5CkWBw0+idJHSV6+Bgf2peDOFf/x4o+vX/pwcNYrWpXFrSfTkQ3JQ1ojrq9yS+upnAlNRHeg8uEwFTgorjI8g==",
       "dev": true,
       "requires": {
-        "postcss-selector-parser": "^6.0.2",
-        "postcss-value-parser": "^4.0.2"
+        "postcss": "^7.0.2"
+      },
+      "dependencies": {
+        "picocolors": {
+          "version": "0.2.1",
+          "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz",
+          "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==",
+          "dev": true
+        },
+        "postcss": {
+          "version": "7.0.39",
+          "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz",
+          "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==",
+          "dev": true,
+          "requires": {
+            "picocolors": "^0.2.1",
+            "source-map": "^0.6.1"
+          }
+        },
+        "source-map": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+          "dev": true
+        }
       }
     },
-    "postcss-color-functional-notation": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-2.0.1.tgz",
-      "integrity": "sha512-ZBARCypjEDofW4P6IdPVTLhDNXPRn8T2s1zHbZidW6rPaaZvcnCS2soYFIQJrMZSxiePJ2XIYTlcb2ztr/eT2g==",
+    "postcss-focus-within": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-3.0.0.tgz",
+      "integrity": "sha512-W0APui8jQeBKbCGZudW37EeMCjDeVxKgiYfIIEo8Bdh5SpB9sxds/Iq8SEuzS0Q4YFOlG7EPFulbbxujpkrV2w==",
       "dev": true,
       "requires": {
-        "postcss": "^7.0.2",
-        "postcss-values-parser": "^2.0.0"
+        "postcss": "^7.0.2"
       },
       "dependencies": {
         "picocolors": {
@@ -8738,15 +20213,13 @@
         }
       }
     },
-    "postcss-color-gray": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/postcss-color-gray/-/postcss-color-gray-5.0.0.tgz",
-      "integrity": "sha512-q6BuRnAGKM/ZRpfDascZlIZPjvwsRye7UDNalqVz3s7GDxMtqPY6+Q871liNxsonUw8oC61OG+PSaysYpl1bnw==",
+    "postcss-font-variant": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-4.0.1.tgz",
+      "integrity": "sha512-I3ADQSTNtLTTd8uxZhtSOrTCQ9G4qUVKPjHiDk0bV75QSxXjVWiJVJ2VLdspGUi9fbW9BcjKJoRvxAH1pckqmA==",
       "dev": true,
       "requires": {
-        "@csstools/convert-colors": "^1.4.0",
-        "postcss": "^7.0.5",
-        "postcss-values-parser": "^2.0.0"
+        "postcss": "^7.0.2"
       },
       "dependencies": {
         "picocolors": {
@@ -8773,14 +20246,13 @@
         }
       }
     },
-    "postcss-color-hex-alpha": {
-      "version": "5.0.3",
-      "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-5.0.3.tgz",
-      "integrity": "sha512-PF4GDel8q3kkreVXKLAGNpHKilXsZ6xuu+mOQMHWHLPNyjiUBOr75sp5ZKJfmv1MCus5/DWUGcK9hm6qHEnXYw==",
+    "postcss-gap-properties": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-2.0.0.tgz",
+      "integrity": "sha512-QZSqDaMgXCHuHTEzMsS2KfVDOq7ZFiknSpkrPJY6jmxbugUPTuSzs/vuE5I3zv0WAS+3vhrlqhijiprnuQfzmg==",
       "dev": true,
       "requires": {
-        "postcss": "^7.0.14",
-        "postcss-values-parser": "^2.0.1"
+        "postcss": "^7.0.2"
       },
       "dependencies": {
         "picocolors": {
@@ -8807,13 +20279,12 @@
         }
       }
     },
-    "postcss-color-mod-function": {
-      "version": "3.0.3",
-      "resolved": "https://registry.npmjs.org/postcss-color-mod-function/-/postcss-color-mod-function-3.0.3.tgz",
-      "integrity": "sha512-YP4VG+xufxaVtzV6ZmhEtc+/aTXH3d0JLpnYfxqTvwZPbJhWqp8bSY3nfNzNRFLgB4XSaBA82OE4VjOOKpCdVQ==",
+    "postcss-image-set-function": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-3.0.1.tgz",
+      "integrity": "sha512-oPTcFFip5LZy8Y/whto91L9xdRHCWEMs3e1MdJxhgt4jy2WYXfhkng59fH5qLXSCPN8k4n94p1Czrfe5IOkKUw==",
       "dev": true,
       "requires": {
-        "@csstools/convert-colors": "^1.4.0",
         "postcss": "^7.0.2",
         "postcss-values-parser": "^2.0.0"
       },
@@ -8842,14 +20313,24 @@
         }
       }
     },
-    "postcss-color-rebeccapurple": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-4.0.1.tgz",
-      "integrity": "sha512-aAe3OhkS6qJXBbqzvZth2Au4V3KieR5sRQ4ptb2b2O8wgvB3SJBsdG+jsn2BZbbwekDG8nTfcCNKcSfe/lEy8g==",
+    "postcss-import": {
+      "version": "14.0.2",
+      "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.0.2.tgz",
+      "integrity": "sha512-BJ2pVK4KhUyMcqjuKs9RijV5tatNzNa73e/32aBVE/ejYPe37iH+6vAu9WvqUkB5OAYgLHzbSvzHnorybJCm9g==",
+      "dev": true,
+      "requires": {
+        "postcss-value-parser": "^4.0.0",
+        "read-cache": "^1.0.0",
+        "resolve": "^1.1.7"
+      }
+    },
+    "postcss-initial": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-3.0.4.tgz",
+      "integrity": "sha512-3RLn6DIpMsK1l5UUy9jxQvoDeUN4gP939tDcKUHD/kM8SGSKbFAnvkpFpj3Bhtz3HGk1jWY5ZNWX6mPta5M9fg==",
       "dev": true,
       "requires": {
-        "postcss": "^7.0.2",
-        "postcss-values-parser": "^2.0.0"
+        "postcss": "^7.0.2"
       },
       "dependencies": {
         "picocolors": {
@@ -8876,34 +20357,15 @@
         }
       }
     },
-    "postcss-colormin": {
-      "version": "5.2.1",
-      "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.2.1.tgz",
-      "integrity": "sha512-VVwMrEYLcHYePUYV99Ymuoi7WhKrMGy/V9/kTS0DkCoJYmmjdOMneyhzYUxcNgteKDVbrewOkSM7Wje/MFwxzA==",
-      "dev": true,
-      "requires": {
-        "browserslist": "^4.16.6",
-        "caniuse-api": "^3.0.0",
-        "colord": "^2.9.1",
-        "postcss-value-parser": "^4.1.0"
-      }
-    },
-    "postcss-convert-values": {
-      "version": "5.0.2",
-      "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.0.2.tgz",
-      "integrity": "sha512-KQ04E2yadmfa1LqXm7UIDwW1ftxU/QWZmz6NKnHnUvJ3LEYbbcX6i329f/ig+WnEByHegulocXrECaZGLpL8Zg==",
-      "dev": true,
-      "requires": {
-        "postcss-value-parser": "^4.1.0"
-      }
-    },
-    "postcss-custom-media": {
-      "version": "7.0.8",
-      "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-7.0.8.tgz",
-      "integrity": "sha512-c9s5iX0Ge15o00HKbuRuTqNndsJUbaXdiNsksnVH8H4gdc+zbLzr/UasOwNG6CTDpLFekVY4672eWdiiWu2GUg==",
+    "postcss-lab-function": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-2.0.1.tgz",
+      "integrity": "sha512-whLy1IeZKY+3fYdqQFuDBf8Auw+qFuVnChWjmxm/UhHWqNHZx+B99EwxTvGYmUBqe3Fjxs4L1BoZTJmPu6usVg==",
       "dev": true,
       "requires": {
-        "postcss": "^7.0.14"
+        "@csstools/convert-colors": "^1.4.0",
+        "postcss": "^7.0.2",
+        "postcss-values-parser": "^2.0.0"
       },
       "dependencies": {
         "picocolors": {
@@ -8930,14 +20392,24 @@
         }
       }
     },
-    "postcss-custom-properties": {
-      "version": "8.0.11",
-      "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-8.0.11.tgz",
-      "integrity": "sha512-nm+o0eLdYqdnJ5abAJeXp4CEU1c1k+eB2yMCvhgzsds/e0umabFrN6HoTy/8Q4K5ilxERdl/JD1LO5ANoYBeMA==",
+    "postcss-loader": {
+      "version": "6.1.1",
+      "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.1.1.tgz",
+      "integrity": "sha512-lBmJMvRh1D40dqpWKr9Rpygwxn8M74U9uaCSeYGNKLGInbk9mXBt1ultHf2dH9Ghk6Ue4UXlXWwGMH9QdUJ5ug==",
       "dev": true,
       "requires": {
-        "postcss": "^7.0.17",
-        "postcss-values-parser": "^2.0.1"
+        "cosmiconfig": "^7.0.0",
+        "klona": "^2.0.4",
+        "semver": "^7.3.5"
+      }
+    },
+    "postcss-logical": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-3.0.0.tgz",
+      "integrity": "sha512-1SUKdJc2vuMOmeItqGuNaC+N8MzBWFWEkAnRnLpFYj1tGGa7NqyVBujfRtgNa2gXR+6RkGUiB2O5Vmh7E2RmiA==",
+      "dev": true,
+      "requires": {
+        "postcss": "^7.0.2"
       },
       "dependencies": {
         "picocolors": {
@@ -8964,22 +20436,15 @@
         }
       }
     },
-    "postcss-custom-selectors": {
-      "version": "5.1.2",
-      "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-5.1.2.tgz",
-      "integrity": "sha512-DSGDhqinCqXqlS4R7KGxL1OSycd1lydugJ1ky4iRXPHdBRiozyMHrdu0H3o7qNOCiZwySZTUI5MV0T8QhCLu+w==",
+    "postcss-media-minmax": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-4.0.0.tgz",
+      "integrity": "sha512-fo9moya6qyxsjbFAYl97qKO9gyre3qvbMnkOZeZwlsW6XYFsvs2DMGDlchVLfAd8LHPZDxivu/+qW2SMQeTHBw==",
       "dev": true,
       "requires": {
-        "postcss": "^7.0.2",
-        "postcss-selector-parser": "^5.0.0-rc.3"
+        "postcss": "^7.0.2"
       },
       "dependencies": {
-        "cssesc": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz",
-          "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==",
-          "dev": true
-        },
         "picocolors": {
           "version": "0.2.1",
           "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz",
@@ -8996,17 +20461,6 @@
             "source-map": "^0.6.1"
           }
         },
-        "postcss-selector-parser": {
-          "version": "5.0.0",
-          "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz",
-          "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==",
-          "dev": true,
-          "requires": {
-            "cssesc": "^2.0.0",
-            "indexes-of": "^1.0.1",
-            "uniq": "^1.0.1"
-          }
-        },
         "source-map": {
           "version": "0.6.1",
           "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
@@ -9015,22 +20469,112 @@
         }
       }
     },
-    "postcss-dir-pseudo-class": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-5.0.0.tgz",
-      "integrity": "sha512-3pm4oq8HYWMZePJY+5ANriPs3P07q+LW6FAdTlkFH2XqDdP4HeeJYMOzn0HYLhRSjBO3fhiqSwwU9xEULSrPgw==",
+    "postcss-merge-longhand": {
+      "version": "5.1.5",
+      "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.5.tgz",
+      "integrity": "sha512-NOG1grw9wIO+60arKa2YYsrbgvP6tp+jqc7+ZD5/MalIw234ooH2C6KlR6FEn4yle7GqZoBxSK1mLBE9KPur6w==",
       "dev": true,
       "requires": {
-        "postcss": "^7.0.2",
-        "postcss-selector-parser": "^5.0.0-rc.3"
+        "postcss-value-parser": "^4.2.0",
+        "stylehacks": "^5.1.0"
+      }
+    },
+    "postcss-merge-rules": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.1.1.tgz",
+      "integrity": "sha512-8wv8q2cXjEuCcgpIB1Xx1pIy8/rhMPIQqYKNzEdyx37m6gpq83mQQdCxgIkFgliyEnKvdwJf/C61vN4tQDq4Ww==",
+      "dev": true,
+      "requires": {
+        "browserslist": "^4.16.6",
+        "caniuse-api": "^3.0.0",
+        "cssnano-utils": "^3.1.0",
+        "postcss-selector-parser": "^6.0.5"
+      }
+    },
+    "postcss-minify-font-values": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz",
+      "integrity": "sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==",
+      "dev": true,
+      "requires": {
+        "postcss-value-parser": "^4.2.0"
+      }
+    },
+    "postcss-minify-gradients": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz",
+      "integrity": "sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==",
+      "dev": true,
+      "requires": {
+        "colord": "^2.9.1",
+        "cssnano-utils": "^3.1.0",
+        "postcss-value-parser": "^4.2.0"
+      }
+    },
+    "postcss-minify-params": {
+      "version": "5.1.3",
+      "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.1.3.tgz",
+      "integrity": "sha512-bkzpWcjykkqIujNL+EVEPOlLYi/eZ050oImVtHU7b4lFS82jPnsCb44gvC6pxaNt38Els3jWYDHTjHKf0koTgg==",
+      "dev": true,
+      "requires": {
+        "browserslist": "^4.16.6",
+        "cssnano-utils": "^3.1.0",
+        "postcss-value-parser": "^4.2.0"
+      }
+    },
+    "postcss-minify-selectors": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.2.0.tgz",
+      "integrity": "sha512-vYxvHkW+iULstA+ctVNx0VoRAR4THQQRkG77o0oa4/mBS0OzGvvzLIvHDv/nNEM0crzN2WIyFU5X7wZhaUK3RA==",
+      "dev": true,
+      "requires": {
+        "postcss-selector-parser": "^6.0.5"
+      }
+    },
+    "postcss-modules-extract-imports": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz",
+      "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==",
+      "dev": true
+    },
+    "postcss-modules-local-by-default": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz",
+      "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==",
+      "dev": true,
+      "requires": {
+        "icss-utils": "^5.0.0",
+        "postcss-selector-parser": "^6.0.2",
+        "postcss-value-parser": "^4.1.0"
+      }
+    },
+    "postcss-modules-scope": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz",
+      "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==",
+      "dev": true,
+      "requires": {
+        "postcss-selector-parser": "^6.0.4"
+      }
+    },
+    "postcss-modules-values": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz",
+      "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==",
+      "dev": true,
+      "requires": {
+        "icss-utils": "^5.0.0"
+      }
+    },
+    "postcss-nesting": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-7.0.1.tgz",
+      "integrity": "sha512-FrorPb0H3nuVq0Sff7W2rnc3SmIcruVC6YwpcS+k687VxyxO33iE1amna7wHuRVzM8vfiYofXSBHNAZ3QhLvYg==",
+      "dev": true,
+      "requires": {
+        "postcss": "^7.0.2"
       },
       "dependencies": {
-        "cssesc": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz",
-          "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==",
-          "dev": true
-        },
         "picocolors": {
           "version": "0.2.1",
           "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz",
@@ -9047,17 +20591,6 @@
             "source-map": "^0.6.1"
           }
         },
-        "postcss-selector-parser": {
-          "version": "5.0.0",
-          "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz",
-          "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==",
-          "dev": true,
-          "requires": {
-            "cssesc": "^2.0.0",
-            "indexes-of": "^1.0.1",
-            "uniq": "^1.0.1"
-          }
-        },
         "source-map": {
           "version": "0.6.1",
           "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
@@ -9066,38 +20599,103 @@
         }
       }
     },
-    "postcss-discard-comments": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.0.1.tgz",
-      "integrity": "sha512-lgZBPTDvWrbAYY1v5GYEv8fEO/WhKOu/hmZqmCYfrpD6eyDWWzAOsl2rF29lpvziKO02Gc5GJQtlpkTmakwOWg==",
+    "postcss-normalize-charset": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz",
+      "integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==",
       "dev": true
     },
-    "postcss-discard-duplicates": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.0.1.tgz",
-      "integrity": "sha512-svx747PWHKOGpAXXQkCc4k/DsWo+6bc5LsVrAsw+OU+Ibi7klFZCyX54gjYzX4TH+f2uzXjRviLARxkMurA2bA==",
-      "dev": true
+    "postcss-normalize-display-values": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz",
+      "integrity": "sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==",
+      "dev": true,
+      "requires": {
+        "postcss-value-parser": "^4.2.0"
+      }
     },
-    "postcss-discard-empty": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.0.1.tgz",
-      "integrity": "sha512-vfU8CxAQ6YpMxV2SvMcMIyF2LX1ZzWpy0lqHDsOdaKKLQVQGVP1pzhrI9JlsO65s66uQTfkQBKBD/A5gp9STFw==",
-      "dev": true
+    "postcss-normalize-positions": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.1.0.tgz",
+      "integrity": "sha512-8gmItgA4H5xiUxgN/3TVvXRoJxkAWLW6f/KKhdsH03atg0cB8ilXnrB5PpSshwVu/dD2ZsRFQcR1OEmSBDAgcQ==",
+      "dev": true,
+      "requires": {
+        "postcss-value-parser": "^4.2.0"
+      }
     },
-    "postcss-discard-overridden": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.0.1.tgz",
-      "integrity": "sha512-Y28H7y93L2BpJhrdUR2SR2fnSsT+3TVx1NmVQLbcnZWwIUpJ7mfcTC6Za9M2PG6w8j7UQRfzxqn8jU2VwFxo3Q==",
-      "dev": true
+    "postcss-normalize-repeat-style": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.0.tgz",
+      "integrity": "sha512-IR3uBjc+7mcWGL6CtniKNQ4Rr5fTxwkaDHwMBDGGs1x9IVRkYIT/M4NelZWkAOBdV6v3Z9S46zqaKGlyzHSchw==",
+      "dev": true,
+      "requires": {
+        "postcss-value-parser": "^4.2.0"
+      }
     },
-    "postcss-double-position-gradients": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-1.0.0.tgz",
-      "integrity": "sha512-G+nV8EnQq25fOI8CH/B6krEohGWnF5+3A6H/+JEpOncu5dCnkS1QQ6+ct3Jkaepw1NGVqqOZH6lqrm244mCftA==",
+    "postcss-normalize-string": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz",
+      "integrity": "sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==",
+      "dev": true,
+      "requires": {
+        "postcss-value-parser": "^4.2.0"
+      }
+    },
+    "postcss-normalize-timing-functions": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz",
+      "integrity": "sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==",
+      "dev": true,
+      "requires": {
+        "postcss-value-parser": "^4.2.0"
+      }
+    },
+    "postcss-normalize-unicode": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.0.tgz",
+      "integrity": "sha512-J6M3MizAAZ2dOdSjy2caayJLQT8E8K9XjLce8AUQMwOrCvjCHv24aLC/Lps1R1ylOfol5VIDMaM/Lo9NGlk1SQ==",
+      "dev": true,
+      "requires": {
+        "browserslist": "^4.16.6",
+        "postcss-value-parser": "^4.2.0"
+      }
+    },
+    "postcss-normalize-url": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz",
+      "integrity": "sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==",
+      "dev": true,
+      "requires": {
+        "normalize-url": "^6.0.1",
+        "postcss-value-parser": "^4.2.0"
+      }
+    },
+    "postcss-normalize-whitespace": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz",
+      "integrity": "sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==",
+      "dev": true,
+      "requires": {
+        "postcss-value-parser": "^4.2.0"
+      }
+    },
+    "postcss-ordered-values": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.1.1.tgz",
+      "integrity": "sha512-7lxgXF0NaoMIgyihL/2boNAEZKiW0+HkMhdKMTD93CjW8TdCy2hSdj8lsAo+uwm7EDG16Da2Jdmtqpedl0cMfw==",
+      "dev": true,
+      "requires": {
+        "cssnano-utils": "^3.1.0",
+        "postcss-value-parser": "^4.2.0"
+      }
+    },
+    "postcss-overflow-shorthand": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-2.0.0.tgz",
+      "integrity": "sha512-aK0fHc9CBNx8jbzMYhshZcEv8LtYnBIRYQD5i7w/K/wS9c2+0NSR6B3OVMu5y0hBHYLcMGjfU+dmWYNKH0I85g==",
       "dev": true,
       "requires": {
-        "postcss": "^7.0.5",
-        "postcss-values-parser": "^2.0.0"
+        "postcss": "^7.0.2"
       },
       "dependencies": {
         "picocolors": {
@@ -9124,14 +20722,13 @@
         }
       }
     },
-    "postcss-env-function": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/postcss-env-function/-/postcss-env-function-2.0.2.tgz",
-      "integrity": "sha512-rwac4BuZlITeUbiBq60h/xbLzXY43qOsIErngWa4l7Mt+RaSkT7QBjXVGTcBHupykkblHMDrBFh30zchYPaOUw==",
+    "postcss-page-break": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-2.0.0.tgz",
+      "integrity": "sha512-tkpTSrLpfLfD9HvgOlJuigLuk39wVTbbd8RKcy8/ugV2bNBUW3xU+AIqyxhDrQr1VUj1RmyJrBn1YWrqUm9zAQ==",
       "dev": true,
       "requires": {
-        "postcss": "^7.0.2",
-        "postcss-values-parser": "^2.0.0"
+        "postcss": "^7.0.2"
       },
       "dependencies": {
         "picocolors": {
@@ -9158,13 +20755,14 @@
         }
       }
     },
-    "postcss-focus-visible": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-4.0.0.tgz",
-      "integrity": "sha512-Z5CkWBw0+idJHSV6+Bgf2peDOFf/x4o+vX/pwcNYrWpXFrSfTkQ3JQ1ojrq9yS+upnAlNRHeg8uEwFTgorjI8g==",
+    "postcss-place": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-4.0.1.tgz",
+      "integrity": "sha512-Zb6byCSLkgRKLODj/5mQugyuj9bvAAw9LqJJjgwz5cYryGeXfFZfSXoP1UfveccFmeq0b/2xxwcTEVScnqGxBg==",
       "dev": true,
       "requires": {
-        "postcss": "^7.0.2"
+        "postcss": "^7.0.2",
+        "postcss-values-parser": "^2.0.0"
       },
       "dependencies": {
         "picocolors": {
@@ -9191,13 +20789,49 @@
         }
       }
     },
-    "postcss-focus-within": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-3.0.0.tgz",
-      "integrity": "sha512-W0APui8jQeBKbCGZudW37EeMCjDeVxKgiYfIIEo8Bdh5SpB9sxds/Iq8SEuzS0Q4YFOlG7EPFulbbxujpkrV2w==",
+    "postcss-preset-env": {
+      "version": "6.7.0",
+      "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-6.7.0.tgz",
+      "integrity": "sha512-eU4/K5xzSFwUFJ8hTdTQzo2RBLbDVt83QZrAvI07TULOkmyQlnYlpwep+2yIK+K+0KlZO4BvFcleOCCcUtwchg==",
       "dev": true,
       "requires": {
-        "postcss": "^7.0.2"
+        "autoprefixer": "^9.6.1",
+        "browserslist": "^4.6.4",
+        "caniuse-lite": "^1.0.30000981",
+        "css-blank-pseudo": "^0.1.4",
+        "css-has-pseudo": "^0.10.0",
+        "css-prefers-color-scheme": "^3.1.1",
+        "cssdb": "^4.4.0",
+        "postcss": "^7.0.17",
+        "postcss-attribute-case-insensitive": "^4.0.1",
+        "postcss-color-functional-notation": "^2.0.1",
+        "postcss-color-gray": "^5.0.0",
+        "postcss-color-hex-alpha": "^5.0.3",
+        "postcss-color-mod-function": "^3.0.3",
+        "postcss-color-rebeccapurple": "^4.0.1",
+        "postcss-custom-media": "^7.0.8",
+        "postcss-custom-properties": "^8.0.11",
+        "postcss-custom-selectors": "^5.1.2",
+        "postcss-dir-pseudo-class": "^5.0.0",
+        "postcss-double-position-gradients": "^1.0.0",
+        "postcss-env-function": "^2.0.2",
+        "postcss-focus-visible": "^4.0.0",
+        "postcss-focus-within": "^3.0.0",
+        "postcss-font-variant": "^4.0.0",
+        "postcss-gap-properties": "^2.0.0",
+        "postcss-image-set-function": "^3.0.1",
+        "postcss-initial": "^3.0.0",
+        "postcss-lab-function": "^2.0.1",
+        "postcss-logical": "^3.0.0",
+        "postcss-media-minmax": "^4.0.0",
+        "postcss-nesting": "^7.0.0",
+        "postcss-overflow-shorthand": "^2.0.0",
+        "postcss-page-break": "^2.0.0",
+        "postcss-place": "^4.0.1",
+        "postcss-pseudo-class-any-link": "^6.0.0",
+        "postcss-replace-overflow-wrap": "^3.0.0",
+        "postcss-selector-matches": "^4.0.0",
+        "postcss-selector-not": "^4.0.0"
       },
       "dependencies": {
         "picocolors": {
@@ -9224,15 +20858,22 @@
         }
       }
     },
-    "postcss-font-variant": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-4.0.1.tgz",
-      "integrity": "sha512-I3ADQSTNtLTTd8uxZhtSOrTCQ9G4qUVKPjHiDk0bV75QSxXjVWiJVJ2VLdspGUi9fbW9BcjKJoRvxAH1pckqmA==",
+    "postcss-pseudo-class-any-link": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-6.0.0.tgz",
+      "integrity": "sha512-lgXW9sYJdLqtmw23otOzrtbDXofUdfYzNm4PIpNE322/swES3VU9XlXHeJS46zT2onFO7V1QFdD4Q9LiZj8mew==",
       "dev": true,
       "requires": {
-        "postcss": "^7.0.2"
+        "postcss": "^7.0.2",
+        "postcss-selector-parser": "^5.0.0-rc.3"
       },
       "dependencies": {
+        "cssesc": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz",
+          "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==",
+          "dev": true
+        },
         "picocolors": {
           "version": "0.2.1",
           "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz",
@@ -9249,6 +20890,17 @@
             "source-map": "^0.6.1"
           }
         },
+        "postcss-selector-parser": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz",
+          "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==",
+          "dev": true,
+          "requires": {
+            "cssesc": "^2.0.0",
+            "indexes-of": "^1.0.1",
+            "uniq": "^1.0.1"
+          }
+        },
         "source-map": {
           "version": "0.6.1",
           "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
@@ -9257,10 +20909,29 @@
         }
       }
     },
-    "postcss-gap-properties": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-2.0.0.tgz",
-      "integrity": "sha512-QZSqDaMgXCHuHTEzMsS2KfVDOq7ZFiknSpkrPJY6jmxbugUPTuSzs/vuE5I3zv0WAS+3vhrlqhijiprnuQfzmg==",
+    "postcss-reduce-initial": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.1.0.tgz",
+      "integrity": "sha512-5OgTUviz0aeH6MtBjHfbr57tml13PuedK/Ecg8szzd4XRMbYxH4572JFG067z+FqBIf6Zp/d+0581glkvvWMFw==",
+      "dev": true,
+      "requires": {
+        "browserslist": "^4.16.6",
+        "caniuse-api": "^3.0.0"
+      }
+    },
+    "postcss-reduce-transforms": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz",
+      "integrity": "sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==",
+      "dev": true,
+      "requires": {
+        "postcss-value-parser": "^4.2.0"
+      }
+    },
+    "postcss-replace-overflow-wrap": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-3.0.0.tgz",
+      "integrity": "sha512-2T5hcEHArDT6X9+9dVSPQdo7QHzG4XKclFT8rU5TzJPDN7RIRTbO9c4drUISOVemLj03aezStHCR2AIcr8XLpw==",
       "dev": true,
       "requires": {
         "postcss": "^7.0.2"
@@ -9290,14 +20961,14 @@
         }
       }
     },
-    "postcss-image-set-function": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-3.0.1.tgz",
-      "integrity": "sha512-oPTcFFip5LZy8Y/whto91L9xdRHCWEMs3e1MdJxhgt4jy2WYXfhkng59fH5qLXSCPN8k4n94p1Czrfe5IOkKUw==",
+    "postcss-selector-matches": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/postcss-selector-matches/-/postcss-selector-matches-4.0.0.tgz",
+      "integrity": "sha512-LgsHwQR/EsRYSqlwdGzeaPKVT0Ml7LAT6E75T8W8xLJY62CE4S/l03BWIt3jT8Taq22kXP08s2SfTSzaraoPww==",
       "dev": true,
       "requires": {
-        "postcss": "^7.0.2",
-        "postcss-values-parser": "^2.0.0"
+        "balanced-match": "^1.0.0",
+        "postcss": "^7.0.2"
       },
       "dependencies": {
         "picocolors": {
@@ -9324,23 +20995,13 @@
         }
       }
     },
-    "postcss-import": {
-      "version": "14.0.2",
-      "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.0.2.tgz",
-      "integrity": "sha512-BJ2pVK4KhUyMcqjuKs9RijV5tatNzNa73e/32aBVE/ejYPe37iH+6vAu9WvqUkB5OAYgLHzbSvzHnorybJCm9g==",
-      "dev": true,
-      "requires": {
-        "postcss-value-parser": "^4.0.0",
-        "read-cache": "^1.0.0",
-        "resolve": "^1.1.7"
-      }
-    },
-    "postcss-initial": {
-      "version": "3.0.4",
-      "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-3.0.4.tgz",
-      "integrity": "sha512-3RLn6DIpMsK1l5UUy9jxQvoDeUN4gP939tDcKUHD/kM8SGSKbFAnvkpFpj3Bhtz3HGk1jWY5ZNWX6mPta5M9fg==",
+    "postcss-selector-not": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-4.0.1.tgz",
+      "integrity": "sha512-YolvBgInEK5/79C+bdFMyzqTg6pkYqDbzZIST/PDMqa/o3qtXenD05apBG2jLgT0/BQ77d4U2UK12jWpilqMAQ==",
       "dev": true,
       "requires": {
+        "balanced-match": "^1.0.0",
         "postcss": "^7.0.2"
       },
       "dependencies": {
@@ -9368,659 +21029,993 @@
         }
       }
     },
-    "postcss-lab-function": {
+    "postcss-selector-parser": {
+      "version": "6.0.10",
+      "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz",
+      "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==",
+      "dev": true,
+      "requires": {
+        "cssesc": "^3.0.0",
+        "util-deprecate": "^1.0.2"
+      }
+    },
+    "postcss-svgo": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.1.0.tgz",
+      "integrity": "sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==",
+      "dev": true,
+      "requires": {
+        "postcss-value-parser": "^4.2.0",
+        "svgo": "^2.7.0"
+      }
+    },
+    "postcss-unique-selectors": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz",
+      "integrity": "sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==",
+      "dev": true,
+      "requires": {
+        "postcss-selector-parser": "^6.0.5"
+      }
+    },
+    "postcss-value-parser": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
+      "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
+      "dev": true
+    },
+    "postcss-values-parser": {
       "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-2.0.1.tgz",
-      "integrity": "sha512-whLy1IeZKY+3fYdqQFuDBf8Auw+qFuVnChWjmxm/UhHWqNHZx+B99EwxTvGYmUBqe3Fjxs4L1BoZTJmPu6usVg==",
+      "resolved": "https://registry.npmjs.org/postcss-values-parser/-/postcss-values-parser-2.0.1.tgz",
+      "integrity": "sha512-2tLuBsA6P4rYTNKCXYG/71C7j1pU6pK503suYOmn4xYrQIzW+opD+7FAFNuGSdZC/3Qfy334QbeMu7MEb8gOxg==",
       "dev": true,
       "requires": {
-        "@csstools/convert-colors": "^1.4.0",
-        "postcss": "^7.0.2",
-        "postcss-values-parser": "^2.0.0"
+        "flatten": "^1.0.2",
+        "indexes-of": "^1.0.1",
+        "uniq": "^1.0.1"
+      }
+    },
+    "prelude-ls": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
+      "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=",
+      "dev": true
+    },
+    "prettier": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.0.tgz",
+      "integrity": "sha512-kXtO4s0Lz/DW/IJ9QdWhAf7/NmPWQXkFr/r/WkR3vyI+0v8amTDxiaQSLzs8NBlytfLWX/7uQUMIW677yLKl4w==",
+      "dev": true
+    },
+    "pretty-bytes": {
+      "version": "5.6.0",
+      "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz",
+      "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==",
+      "dev": true
+    },
+    "pretty-error": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.2.tgz",
+      "integrity": "sha512-EY5oDzmsX5wvuynAByrmY0P0hcp+QpnAKbJng2A2MPjVKXCxrDSUkzghVJ4ZGPIv+JC4gX8fPUWscC0RtjsWGw==",
+      "dev": true,
+      "requires": {
+        "lodash": "^4.17.20",
+        "renderkid": "^2.0.4"
+      }
+    },
+    "pretty-format": {
+      "version": "27.5.1",
+      "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz",
+      "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==",
+      "dev": true,
+      "requires": {
+        "ansi-regex": "^5.0.1",
+        "ansi-styles": "^5.0.0",
+        "react-is": "^17.0.1"
       },
       "dependencies": {
-        "picocolors": {
-          "version": "0.2.1",
-          "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz",
-          "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==",
+        "ansi-styles": {
+          "version": "5.2.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
+          "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
           "dev": true
         },
-        "postcss": {
-          "version": "7.0.39",
-          "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz",
-          "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==",
-          "dev": true,
-          "requires": {
-            "picocolors": "^0.2.1",
-            "source-map": "^0.6.1"
-          }
-        },
-        "source-map": {
-          "version": "0.6.1",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+        "react-is": {
+          "version": "17.0.2",
+          "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
+          "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
           "dev": true
         }
       }
     },
-    "postcss-loader": {
-      "version": "6.1.1",
-      "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.1.1.tgz",
-      "integrity": "sha512-lBmJMvRh1D40dqpWKr9Rpygwxn8M74U9uaCSeYGNKLGInbk9mXBt1ultHf2dH9Ghk6Ue4UXlXWwGMH9QdUJ5ug==",
+    "pretty-hrtime": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz",
+      "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=",
+      "dev": true
+    },
+    "prismjs": {
+      "version": "1.28.0",
+      "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.28.0.tgz",
+      "integrity": "sha512-8aaXdYvl1F7iC7Xm1spqSaY/OJBpYW3v+KJ+F17iYxvdc8sfjW194COK5wVhMZX45tGteiBQgdvD/nhxcRwylw==",
+      "dev": true
+    },
+    "process": {
+      "version": "0.11.10",
+      "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
+      "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=",
+      "dev": true
+    },
+    "process-nextick-args": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+      "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
+    },
+    "progress": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
+      "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
+      "dev": true
+    },
+    "promise-inflight": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz",
+      "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=",
+      "dev": true
+    },
+    "promise-retry": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz",
+      "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==",
+      "dev": true,
+      "requires": {
+        "err-code": "^2.0.2",
+        "retry": "^0.12.0"
+      }
+    },
+    "promise.allsettled": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/promise.allsettled/-/promise.allsettled-1.0.5.tgz",
+      "integrity": "sha512-tVDqeZPoBC0SlzJHzWGZ2NKAguVq2oiYj7gbggbiTvH2itHohijTp7njOUA0aQ/nl+0lr/r6egmhoYu63UZ/pQ==",
       "dev": true,
       "requires": {
-        "cosmiconfig": "^7.0.0",
-        "klona": "^2.0.4",
-        "semver": "^7.3.5"
+        "array.prototype.map": "^1.0.4",
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.3",
+        "es-abstract": "^1.19.1",
+        "get-intrinsic": "^1.1.1",
+        "iterate-value": "^1.0.2"
       }
     },
-    "postcss-logical": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-3.0.0.tgz",
-      "integrity": "sha512-1SUKdJc2vuMOmeItqGuNaC+N8MzBWFWEkAnRnLpFYj1tGGa7NqyVBujfRtgNa2gXR+6RkGUiB2O5Vmh7E2RmiA==",
+    "promise.prototype.finally": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/promise.prototype.finally/-/promise.prototype.finally-3.1.3.tgz",
+      "integrity": "sha512-EXRF3fC9/0gz4qkt/f5EP5iW4kj9oFpBICNpCNOb/52+8nlHIX07FPLbi/q4qYBQ1xZqivMzTpNQSnArVASolQ==",
       "dev": true,
       "requires": {
-        "postcss": "^7.0.2"
-      },
-      "dependencies": {
-        "picocolors": {
-          "version": "0.2.1",
-          "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz",
-          "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==",
-          "dev": true
-        },
-        "postcss": {
-          "version": "7.0.39",
-          "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz",
-          "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==",
-          "dev": true,
-          "requires": {
-            "picocolors": "^0.2.1",
-            "source-map": "^0.6.1"
-          }
-        },
-        "source-map": {
-          "version": "0.6.1",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
-          "dev": true
-        }
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.3",
+        "es-abstract": "^1.19.1"
       }
     },
-    "postcss-media-minmax": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-4.0.0.tgz",
-      "integrity": "sha512-fo9moya6qyxsjbFAYl97qKO9gyre3qvbMnkOZeZwlsW6XYFsvs2DMGDlchVLfAd8LHPZDxivu/+qW2SMQeTHBw==",
+    "prompts": {
+      "version": "2.4.2",
+      "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz",
+      "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==",
       "dev": true,
       "requires": {
-        "postcss": "^7.0.2"
-      },
-      "dependencies": {
-        "picocolors": {
-          "version": "0.2.1",
-          "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz",
-          "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==",
-          "dev": true
-        },
-        "postcss": {
-          "version": "7.0.39",
-          "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz",
-          "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==",
-          "dev": true,
-          "requires": {
-            "picocolors": "^0.2.1",
-            "source-map": "^0.6.1"
-          }
-        },
-        "source-map": {
-          "version": "0.6.1",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
-          "dev": true
-        }
+        "kleur": "^3.0.3",
+        "sisteransi": "^1.0.5"
       }
     },
-    "postcss-merge-longhand": {
-      "version": "5.0.4",
-      "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.0.4.tgz",
-      "integrity": "sha512-2lZrOVD+d81aoYkZDpWu6+3dTAAGkCKbV5DoRhnIR7KOULVrI/R7bcMjhrH9KTRy6iiHKqmtG+n/MMj1WmqHFw==",
+    "prop-types": {
+      "version": "15.8.1",
+      "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
+      "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
       "dev": true,
       "requires": {
-        "postcss-value-parser": "^4.1.0",
-        "stylehacks": "^5.0.1"
+        "loose-envify": "^1.4.0",
+        "object-assign": "^4.1.1",
+        "react-is": "^16.13.1"
       }
     },
-    "postcss-merge-rules": {
-      "version": "5.0.3",
-      "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.0.3.tgz",
-      "integrity": "sha512-cEKTMEbWazVa5NXd8deLdCnXl+6cYG7m2am+1HzqH0EnTdy8fRysatkaXb2dEnR+fdaDxTvuZ5zoBdv6efF6hg==",
+    "property-information": {
+      "version": "5.6.0",
+      "resolved": "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz",
+      "integrity": "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==",
       "dev": true,
       "requires": {
-        "browserslist": "^4.16.6",
-        "caniuse-api": "^3.0.0",
-        "cssnano-utils": "^2.0.1",
-        "postcss-selector-parser": "^6.0.5"
+        "xtend": "^4.0.0"
       }
     },
-    "postcss-minify-font-values": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.0.1.tgz",
-      "integrity": "sha512-7JS4qIsnqaxk+FXY1E8dHBDmraYFWmuL6cgt0T1SWGRO5bzJf8sUoelwa4P88LEWJZweHevAiDKxHlofuvtIoA==",
+    "proxy-addr": {
+      "version": "2.0.7",
+      "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
+      "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
       "dev": true,
       "requires": {
-        "postcss-value-parser": "^4.1.0"
+        "forwarded": "0.2.0",
+        "ipaddr.js": "1.9.1"
       }
     },
-    "postcss-minify-gradients": {
-      "version": "5.0.3",
-      "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.0.3.tgz",
-      "integrity": "sha512-Z91Ol22nB6XJW+5oe31+YxRsYooxOdFKcbOqY/V8Fxse1Y3vqlNRpi1cxCqoACZTQEhl+xvt4hsbWiV5R+XI9Q==",
+    "proxy-middleware": {
+      "version": "0.15.0",
+      "resolved": "https://registry.npmjs.org/proxy-middleware/-/proxy-middleware-0.15.0.tgz",
+      "integrity": "sha1-o/3xvvtzD5UZZYcqwvYHTGFHelY=",
+      "dev": true
+    },
+    "prr": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
+      "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=",
+      "dev": true
+    },
+    "public-encrypt": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz",
+      "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==",
       "dev": true,
       "requires": {
-        "colord": "^2.9.1",
-        "cssnano-utils": "^2.0.1",
-        "postcss-value-parser": "^4.1.0"
+        "bn.js": "^4.1.0",
+        "browserify-rsa": "^4.0.0",
+        "create-hash": "^1.1.0",
+        "parse-asn1": "^5.0.0",
+        "randombytes": "^2.0.1",
+        "safe-buffer": "^5.1.2"
+      },
+      "dependencies": {
+        "bn.js": {
+          "version": "4.12.0",
+          "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
+          "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==",
+          "dev": true
+        }
       }
     },
-    "postcss-minify-params": {
-      "version": "5.0.2",
-      "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.0.2.tgz",
-      "integrity": "sha512-qJAPuBzxO1yhLad7h2Dzk/F7n1vPyfHfCCh5grjGfjhi1ttCnq4ZXGIW77GSrEbh9Hus9Lc/e/+tB4vh3/GpDg==",
+    "pump": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
+      "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
       "dev": true,
       "requires": {
-        "alphanum-sort": "^1.0.2",
-        "browserslist": "^4.16.6",
-        "cssnano-utils": "^2.0.1",
-        "postcss-value-parser": "^4.1.0"
+        "end-of-stream": "^1.1.0",
+        "once": "^1.3.1"
       }
     },
-    "postcss-minify-selectors": {
-      "version": "5.1.0",
-      "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.1.0.tgz",
-      "integrity": "sha512-NzGBXDa7aPsAcijXZeagnJBKBPMYLaJJzB8CQh6ncvyl2sIndLVWfbcDi0SBjRWk5VqEjXvf8tYwzoKf4Z07og==",
+    "pumpify": {
+      "version": "1.5.1",
+      "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz",
+      "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==",
       "dev": true,
       "requires": {
-        "alphanum-sort": "^1.0.2",
-        "postcss-selector-parser": "^6.0.5"
+        "duplexify": "^3.6.0",
+        "inherits": "^2.0.3",
+        "pump": "^2.0.0"
+      },
+      "dependencies": {
+        "pump": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz",
+          "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==",
+          "dev": true,
+          "requires": {
+            "end-of-stream": "^1.1.0",
+            "once": "^1.3.1"
+          }
+        }
       }
     },
-    "postcss-modules-extract-imports": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz",
-      "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==",
+    "punycode": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+      "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
+    },
+    "qjobs": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz",
+      "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==",
       "dev": true
     },
-    "postcss-modules-local-by-default": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz",
-      "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==",
+    "qs": {
+      "version": "6.10.3",
+      "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz",
+      "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==",
       "dev": true,
       "requires": {
-        "icss-utils": "^5.0.0",
-        "postcss-selector-parser": "^6.0.2",
-        "postcss-value-parser": "^4.1.0"
+        "side-channel": "^1.0.4"
       }
     },
-    "postcss-modules-scope": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz",
-      "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==",
+    "querystring": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
+      "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=",
+      "dev": true
+    },
+    "querystring-es3": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz",
+      "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=",
+      "dev": true
+    },
+    "querystringify": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
+      "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
+      "dev": true
+    },
+    "queue-microtask": {
+      "version": "1.2.3",
+      "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+      "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+      "dev": true
+    },
+    "quote-stream": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/quote-stream/-/quote-stream-1.0.2.tgz",
+      "integrity": "sha1-hJY/jJwmuULhU/7rU6rnRlK34LI=",
       "dev": true,
       "requires": {
-        "postcss-selector-parser": "^6.0.4"
+        "buffer-equal": "0.0.1",
+        "minimist": "^1.1.3",
+        "through2": "^2.0.0"
       }
     },
-    "postcss-modules-values": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz",
-      "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==",
+    "ramda": {
+      "version": "0.28.0",
+      "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.28.0.tgz",
+      "integrity": "sha512-9QnLuG/kPVgWvMQ4aODhsBUFKOUmnbUnsSXACv+NCQZcHbeb+v8Lodp8OVxtRULN1/xOyYLLaL6npE6dMq5QTA==",
+      "dev": true
+    },
+    "randombytes": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
+      "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
       "dev": true,
       "requires": {
-        "icss-utils": "^5.0.0"
+        "safe-buffer": "^5.1.0"
       }
     },
-    "postcss-nesting": {
-      "version": "7.0.1",
-      "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-7.0.1.tgz",
-      "integrity": "sha512-FrorPb0H3nuVq0Sff7W2rnc3SmIcruVC6YwpcS+k687VxyxO33iE1amna7wHuRVzM8vfiYofXSBHNAZ3QhLvYg==",
+    "randomfill": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz",
+      "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==",
       "dev": true,
       "requires": {
-        "postcss": "^7.0.2"
+        "randombytes": "^2.0.5",
+        "safe-buffer": "^5.1.0"
+      }
+    },
+    "range-parser": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+      "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
+      "dev": true
+    },
+    "raw-body": {
+      "version": "2.5.1",
+      "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
+      "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
+      "dev": true,
+      "requires": {
+        "bytes": "3.1.2",
+        "http-errors": "2.0.0",
+        "iconv-lite": "0.4.24",
+        "unpipe": "1.0.0"
       },
       "dependencies": {
-        "picocolors": {
-          "version": "0.2.1",
-          "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz",
-          "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==",
-          "dev": true
-        },
-        "postcss": {
-          "version": "7.0.39",
-          "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz",
-          "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==",
-          "dev": true,
-          "requires": {
-            "picocolors": "^0.2.1",
-            "source-map": "^0.6.1"
-          }
-        },
-        "source-map": {
-          "version": "0.6.1",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+        "bytes": {
+          "version": "3.1.2",
+          "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
+          "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
           "dev": true
         }
       }
     },
-    "postcss-normalize-charset": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.0.1.tgz",
-      "integrity": "sha512-6J40l6LNYnBdPSk+BHZ8SF+HAkS4q2twe5jnocgd+xWpz/mx/5Sa32m3W1AA8uE8XaXN+eg8trIlfu8V9x61eg==",
-      "dev": true
+    "raw-loader": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-4.0.2.tgz",
+      "integrity": "sha512-ZnScIV3ag9A4wPX/ZayxL/jZH+euYb6FcUinPcgiQW0+UBtEv0O6Q3lGd3cqJ+GHH+rksEv3Pj99oxJ3u3VIKA==",
+      "requires": {
+        "loader-utils": "^2.0.0",
+        "schema-utils": "^3.0.0"
+      }
     },
-    "postcss-normalize-display-values": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.0.1.tgz",
-      "integrity": "sha512-uupdvWk88kLDXi5HEyI9IaAJTE3/Djbcrqq8YgjvAVuzgVuqIk3SuJWUisT2gaJbZm1H9g5k2w1xXilM3x8DjQ==",
+    "react": {
+      "version": "16.14.0",
+      "resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz",
+      "integrity": "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==",
       "dev": true,
       "requires": {
-        "cssnano-utils": "^2.0.1",
-        "postcss-value-parser": "^4.1.0"
+        "loose-envify": "^1.1.0",
+        "object-assign": "^4.1.1",
+        "prop-types": "^15.6.2"
       }
     },
-    "postcss-normalize-positions": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.0.1.tgz",
-      "integrity": "sha512-rvzWAJai5xej9yWqlCb1OWLd9JjW2Ex2BCPzUJrbaXmtKtgfL8dBMOOMTX6TnvQMtjk3ei1Lswcs78qKO1Skrg==",
+    "react-dom": {
+      "version": "16.14.0",
+      "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.14.0.tgz",
+      "integrity": "sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw==",
       "dev": true,
       "requires": {
-        "postcss-value-parser": "^4.1.0"
+        "loose-envify": "^1.1.0",
+        "object-assign": "^4.1.1",
+        "prop-types": "^15.6.2",
+        "scheduler": "^0.19.1"
       }
     },
-    "postcss-normalize-repeat-style": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.0.1.tgz",
-      "integrity": "sha512-syZ2itq0HTQjj4QtXZOeefomckiV5TaUO6ReIEabCh3wgDs4Mr01pkif0MeVwKyU/LHEkPJnpwFKRxqWA/7O3w==",
+    "react-inspector": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/react-inspector/-/react-inspector-5.1.1.tgz",
+      "integrity": "sha512-GURDaYzoLbW8pMGXwYPDBIv6nqei4kK7LPRZ9q9HCZF54wqXz/dnylBp/kfE9XmekBhHvLDdcYeyIwSrvtOiWg==",
       "dev": true,
       "requires": {
-        "cssnano-utils": "^2.0.1",
-        "postcss-value-parser": "^4.1.0"
+        "@babel/runtime": "^7.0.0",
+        "is-dom": "^1.0.0",
+        "prop-types": "^15.0.0"
       }
     },
-    "postcss-normalize-string": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.0.1.tgz",
-      "integrity": "sha512-Ic8GaQ3jPMVl1OEn2U//2pm93AXUcF3wz+OriskdZ1AOuYV25OdgS7w9Xu2LO5cGyhHCgn8dMXh9bO7vi3i9pA==",
+    "react-is": {
+      "version": "16.13.1",
+      "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+      "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
+      "dev": true
+    },
+    "react-merge-refs": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/react-merge-refs/-/react-merge-refs-1.1.0.tgz",
+      "integrity": "sha512-alTKsjEL0dKH/ru1Iyn7vliS2QRcBp9zZPGoWxUOvRGWPUYgjo+V01is7p04It6KhgrzhJGnIj9GgX8W4bZoCQ==",
+      "dev": true
+    },
+    "react-syntax-highlighter": {
+      "version": "15.5.0",
+      "resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-15.5.0.tgz",
+      "integrity": "sha512-+zq2myprEnQmH5yw6Gqc8lD55QHnpKaU8TOcFeC/Lg/MQSs8UknEA0JC4nTZGFAXC2J2Hyj/ijJ7NlabyPi2gg==",
       "dev": true,
       "requires": {
-        "postcss-value-parser": "^4.1.0"
+        "@babel/runtime": "^7.3.1",
+        "highlight.js": "^10.4.1",
+        "lowlight": "^1.17.0",
+        "prismjs": "^1.27.0",
+        "refractor": "^3.6.0"
       }
     },
-    "postcss-normalize-timing-functions": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.0.1.tgz",
-      "integrity": "sha512-cPcBdVN5OsWCNEo5hiXfLUnXfTGtSFiBU9SK8k7ii8UD7OLuznzgNRYkLZow11BkQiiqMcgPyh4ZqXEEUrtQ1Q==",
+    "read-cache": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
+      "integrity": "sha1-5mTvMRYRZsl1HNvo28+GtftY93Q=",
       "dev": true,
       "requires": {
-        "cssnano-utils": "^2.0.1",
-        "postcss-value-parser": "^4.1.0"
+        "pify": "^2.3.0"
+      },
+      "dependencies": {
+        "pify": {
+          "version": "2.3.0",
+          "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+          "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+          "dev": true
+        }
       }
     },
-    "postcss-normalize-unicode": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.0.1.tgz",
-      "integrity": "sha512-kAtYD6V3pK0beqrU90gpCQB7g6AOfP/2KIPCVBKJM2EheVsBQmx/Iof+9zR9NFKLAx4Pr9mDhogB27pmn354nA==",
+    "read-package-json-fast": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-2.0.3.tgz",
+      "integrity": "sha512-W/BKtbL+dUjTuRL2vziuYhp76s5HZ9qQhd/dKfWIZveD0O40453QNyZhC0e63lqZrAQ4jiOapVoeJ7JrszenQQ==",
       "dev": true,
       "requires": {
-        "browserslist": "^4.16.0",
-        "postcss-value-parser": "^4.1.0"
+        "json-parse-even-better-errors": "^2.3.0",
+        "npm-normalize-package-bin": "^1.0.1"
       }
     },
-    "postcss-normalize-url": {
-      "version": "5.0.3",
-      "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.0.3.tgz",
-      "integrity": "sha512-qWiUMbvkRx3kc1Dp5opzUwc7MBWZcSDK2yofCmdvFBCpx+zFPkxBC1FASQ59Pt+flYfj/nTZSkmF56+XG5elSg==",
+    "read-pkg": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz",
+      "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==",
       "dev": true,
       "requires": {
-        "is-absolute-url": "^3.0.3",
-        "normalize-url": "^6.0.1",
-        "postcss-value-parser": "^4.1.0"
+        "@types/normalize-package-data": "^2.4.0",
+        "normalize-package-data": "^2.5.0",
+        "parse-json": "^5.0.0",
+        "type-fest": "^0.6.0"
+      },
+      "dependencies": {
+        "type-fest": {
+          "version": "0.6.0",
+          "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz",
+          "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==",
+          "dev": true
+        }
       }
     },
-    "postcss-normalize-whitespace": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.0.1.tgz",
-      "integrity": "sha512-iPklmI5SBnRvwceb/XH568yyzK0qRVuAG+a1HFUsFRf11lEJTiQQa03a4RSCQvLKdcpX7XsI1Gen9LuLoqwiqA==",
+    "read-pkg-up": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz",
+      "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==",
       "dev": true,
       "requires": {
-        "postcss-value-parser": "^4.1.0"
+        "find-up": "^4.1.0",
+        "read-pkg": "^5.2.0",
+        "type-fest": "^0.8.1"
+      },
+      "dependencies": {
+        "type-fest": {
+          "version": "0.8.1",
+          "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
+          "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==",
+          "dev": true
+        }
       }
     },
-    "postcss-ordered-values": {
-      "version": "5.0.2",
-      "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.0.2.tgz",
-      "integrity": "sha512-8AFYDSOYWebJYLyJi3fyjl6CqMEG/UVworjiyK1r573I56kb3e879sCJLGvR3merj+fAdPpVplXKQZv+ey6CgQ==",
+    "readable-stream": {
+      "version": "2.3.7",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+      "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+      "requires": {
+        "core-util-is": "~1.0.0",
+        "inherits": "~2.0.3",
+        "isarray": "~1.0.0",
+        "process-nextick-args": "~2.0.0",
+        "safe-buffer": "~5.1.1",
+        "string_decoder": "~1.1.1",
+        "util-deprecate": "~1.0.1"
+      }
+    },
+    "readdirp": {
+      "version": "3.6.0",
+      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+      "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
       "dev": true,
       "requires": {
-        "cssnano-utils": "^2.0.1",
-        "postcss-value-parser": "^4.1.0"
+        "picomatch": "^2.2.1"
       }
     },
-    "postcss-overflow-shorthand": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-2.0.0.tgz",
-      "integrity": "sha512-aK0fHc9CBNx8jbzMYhshZcEv8LtYnBIRYQD5i7w/K/wS9c2+0NSR6B3OVMu5y0hBHYLcMGjfU+dmWYNKH0I85g==",
+    "redent": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz",
+      "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=",
       "dev": true,
+      "optional": true,
       "requires": {
-        "postcss": "^7.0.2"
+        "indent-string": "^2.1.0",
+        "strip-indent": "^1.0.1"
       },
       "dependencies": {
-        "picocolors": {
-          "version": "0.2.1",
-          "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz",
-          "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==",
-          "dev": true
-        },
-        "postcss": {
-          "version": "7.0.39",
-          "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz",
-          "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==",
+        "indent-string": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz",
+          "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=",
           "dev": true,
+          "optional": true,
           "requires": {
-            "picocolors": "^0.2.1",
-            "source-map": "^0.6.1"
+            "repeating": "^2.0.0"
           }
-        },
-        "source-map": {
-          "version": "0.6.1",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
-          "dev": true
         }
       }
     },
-    "postcss-page-break": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-2.0.0.tgz",
-      "integrity": "sha512-tkpTSrLpfLfD9HvgOlJuigLuk39wVTbbd8RKcy8/ugV2bNBUW3xU+AIqyxhDrQr1VUj1RmyJrBn1YWrqUm9zAQ==",
+    "reflect-metadata": {
+      "version": "0.1.13",
+      "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz",
+      "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==",
+      "dev": true
+    },
+    "refractor": {
+      "version": "3.6.0",
+      "resolved": "https://registry.npmjs.org/refractor/-/refractor-3.6.0.tgz",
+      "integrity": "sha512-MY9W41IOWxxk31o+YvFCNyNzdkc9M20NoZK5vq6jkv4I/uh2zkWcfudj0Q1fovjUQJrNewS9NMzeTtqPf+n5EA==",
       "dev": true,
       "requires": {
-        "postcss": "^7.0.2"
+        "hastscript": "^6.0.0",
+        "parse-entities": "^2.0.0",
+        "prismjs": "~1.27.0"
       },
       "dependencies": {
-        "picocolors": {
-          "version": "0.2.1",
-          "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz",
-          "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==",
-          "dev": true
-        },
-        "postcss": {
-          "version": "7.0.39",
-          "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz",
-          "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==",
-          "dev": true,
-          "requires": {
-            "picocolors": "^0.2.1",
-            "source-map": "^0.6.1"
-          }
-        },
-        "source-map": {
-          "version": "0.6.1",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+        "prismjs": {
+          "version": "1.27.0",
+          "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.27.0.tgz",
+          "integrity": "sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==",
           "dev": true
         }
       }
     },
-    "postcss-place": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-4.0.1.tgz",
-      "integrity": "sha512-Zb6byCSLkgRKLODj/5mQugyuj9bvAAw9LqJJjgwz5cYryGeXfFZfSXoP1UfveccFmeq0b/2xxwcTEVScnqGxBg==",
+    "regenerate": {
+      "version": "1.4.2",
+      "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
+      "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==",
+      "dev": true
+    },
+    "regenerate-unicode-properties": {
+      "version": "10.0.1",
+      "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz",
+      "integrity": "sha512-vn5DU6yg6h8hP/2OkQo3K7uVILvY4iu0oI4t3HFa81UPkhGJwkRwM10JEc3upjdhHjs/k8GJY1sRBhk5sr69Bw==",
       "dev": true,
       "requires": {
-        "postcss": "^7.0.2",
-        "postcss-values-parser": "^2.0.0"
-      },
-      "dependencies": {
-        "picocolors": {
-          "version": "0.2.1",
-          "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz",
-          "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==",
-          "dev": true
-        },
-        "postcss": {
-          "version": "7.0.39",
-          "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz",
-          "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==",
-          "dev": true,
-          "requires": {
-            "picocolors": "^0.2.1",
-            "source-map": "^0.6.1"
-          }
-        },
-        "source-map": {
-          "version": "0.6.1",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
-          "dev": true
-        }
+        "regenerate": "^1.4.2"
       }
     },
-    "postcss-preset-env": {
-      "version": "6.7.0",
-      "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-6.7.0.tgz",
-      "integrity": "sha512-eU4/K5xzSFwUFJ8hTdTQzo2RBLbDVt83QZrAvI07TULOkmyQlnYlpwep+2yIK+K+0KlZO4BvFcleOCCcUtwchg==",
+    "regenerator-runtime": {
+      "version": "0.13.9",
+      "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz",
+      "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==",
+      "dev": true
+    },
+    "regenerator-transform": {
+      "version": "0.15.0",
+      "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.0.tgz",
+      "integrity": "sha512-LsrGtPmbYg19bcPHwdtmXwbW+TqNvtY4riE3P83foeHRroMbH6/2ddFBfab3t7kbzc7v7p4wbkIecHImqt0QNg==",
       "dev": true,
       "requires": {
-        "autoprefixer": "^9.6.1",
-        "browserslist": "^4.6.4",
-        "caniuse-lite": "^1.0.30000981",
-        "css-blank-pseudo": "^0.1.4",
-        "css-has-pseudo": "^0.10.0",
-        "css-prefers-color-scheme": "^3.1.1",
-        "cssdb": "^4.4.0",
-        "postcss": "^7.0.17",
-        "postcss-attribute-case-insensitive": "^4.0.1",
-        "postcss-color-functional-notation": "^2.0.1",
-        "postcss-color-gray": "^5.0.0",
-        "postcss-color-hex-alpha": "^5.0.3",
-        "postcss-color-mod-function": "^3.0.3",
-        "postcss-color-rebeccapurple": "^4.0.1",
-        "postcss-custom-media": "^7.0.8",
-        "postcss-custom-properties": "^8.0.11",
-        "postcss-custom-selectors": "^5.1.2",
-        "postcss-dir-pseudo-class": "^5.0.0",
-        "postcss-double-position-gradients": "^1.0.0",
-        "postcss-env-function": "^2.0.2",
-        "postcss-focus-visible": "^4.0.0",
-        "postcss-focus-within": "^3.0.0",
-        "postcss-font-variant": "^4.0.0",
-        "postcss-gap-properties": "^2.0.0",
-        "postcss-image-set-function": "^3.0.1",
-        "postcss-initial": "^3.0.0",
-        "postcss-lab-function": "^2.0.1",
-        "postcss-logical": "^3.0.0",
-        "postcss-media-minmax": "^4.0.0",
-        "postcss-nesting": "^7.0.0",
-        "postcss-overflow-shorthand": "^2.0.0",
-        "postcss-page-break": "^2.0.0",
-        "postcss-place": "^4.0.1",
-        "postcss-pseudo-class-any-link": "^6.0.0",
-        "postcss-replace-overflow-wrap": "^3.0.0",
-        "postcss-selector-matches": "^4.0.0",
-        "postcss-selector-not": "^4.0.0"
+        "@babel/runtime": "^7.8.4"
+      }
+    },
+    "regex-not": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz",
+      "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==",
+      "dev": true,
+      "requires": {
+        "extend-shallow": "^3.0.2",
+        "safe-regex": "^1.1.0"
+      }
+    },
+    "regex-parser": {
+      "version": "2.2.11",
+      "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.11.tgz",
+      "integrity": "sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==",
+      "dev": true
+    },
+    "regexp.prototype.flags": {
+      "version": "1.4.3",
+      "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz",
+      "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==",
+      "dev": true,
+      "requires": {
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.3",
+        "functions-have-names": "^1.2.2"
+      }
+    },
+    "regexpp": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz",
+      "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==",
+      "dev": true
+    },
+    "regexpu-core": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.0.1.tgz",
+      "integrity": "sha512-CriEZlrKK9VJw/xQGJpQM5rY88BtuL8DM+AEwvcThHilbxiTAy8vq4iJnd2tqq8wLmjbGZzP7ZcKFjbGkmEFrw==",
+      "dev": true,
+      "requires": {
+        "regenerate": "^1.4.2",
+        "regenerate-unicode-properties": "^10.0.1",
+        "regjsgen": "^0.6.0",
+        "regjsparser": "^0.8.2",
+        "unicode-match-property-ecmascript": "^2.0.0",
+        "unicode-match-property-value-ecmascript": "^2.0.0"
+      }
+    },
+    "regjsgen": {
+      "version": "0.6.0",
+      "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.6.0.tgz",
+      "integrity": "sha512-ozE883Uigtqj3bx7OhL1KNbCzGyW2NQZPl6Hs09WTvCuZD5sTI4JY58bkbQWa/Y9hxIsvJ3M8Nbf7j54IqeZbA==",
+      "dev": true
+    },
+    "regjsparser": {
+      "version": "0.8.4",
+      "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.8.4.tgz",
+      "integrity": "sha512-J3LABycON/VNEu3abOviqGHuB/LOtOQj8SKmfP9anY5GfAVw/SPjwzSjxGjbZXIxbGfqTHtJw58C2Li/WkStmA==",
+      "dev": true,
+      "requires": {
+        "jsesc": "~0.5.0"
       },
       "dependencies": {
-        "picocolors": {
-          "version": "0.2.1",
-          "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz",
-          "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==",
-          "dev": true
-        },
-        "postcss": {
-          "version": "7.0.39",
-          "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz",
-          "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==",
-          "dev": true,
-          "requires": {
-            "picocolors": "^0.2.1",
-            "source-map": "^0.6.1"
-          }
-        },
-        "source-map": {
-          "version": "0.6.1",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+        "jsesc": {
+          "version": "0.5.0",
+          "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz",
+          "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=",
           "dev": true
         }
       }
     },
-    "postcss-pseudo-class-any-link": {
-      "version": "6.0.0",
-      "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-6.0.0.tgz",
-      "integrity": "sha512-lgXW9sYJdLqtmw23otOzrtbDXofUdfYzNm4PIpNE322/swES3VU9XlXHeJS46zT2onFO7V1QFdD4Q9LiZj8mew==",
+    "relateurl": {
+      "version": "0.2.7",
+      "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz",
+      "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=",
+      "dev": true
+    },
+    "remark-external-links": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/remark-external-links/-/remark-external-links-8.0.0.tgz",
+      "integrity": "sha512-5vPSX0kHoSsqtdftSHhIYofVINC8qmp0nctkeU9YoJwV3YfiBRiI6cbFRJ0oI/1F9xS+bopXG0m2KS8VFscuKA==",
       "dev": true,
       "requires": {
-        "postcss": "^7.0.2",
-        "postcss-selector-parser": "^5.0.0-rc.3"
+        "extend": "^3.0.0",
+        "is-absolute-url": "^3.0.0",
+        "mdast-util-definitions": "^4.0.0",
+        "space-separated-tokens": "^1.0.0",
+        "unist-util-visit": "^2.0.0"
+      }
+    },
+    "remark-footnotes": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/remark-footnotes/-/remark-footnotes-2.0.0.tgz",
+      "integrity": "sha512-3Clt8ZMH75Ayjp9q4CorNeyjwIxHFcTkaektplKGl2A1jNGEUey8cKL0ZC5vJwfcD5GFGsNLImLG/NGzWIzoMQ==",
+      "dev": true
+    },
+    "remark-mdx": {
+      "version": "1.6.22",
+      "resolved": "https://registry.npmjs.org/remark-mdx/-/remark-mdx-1.6.22.tgz",
+      "integrity": "sha512-phMHBJgeV76uyFkH4rvzCftLfKCr2RZuF+/gmVcaKrpsihyzmhXjA0BEMDaPTXG5y8qZOKPVo83NAOX01LPnOQ==",
+      "dev": true,
+      "requires": {
+        "@babel/core": "7.12.9",
+        "@babel/helper-plugin-utils": "7.10.4",
+        "@babel/plugin-proposal-object-rest-spread": "7.12.1",
+        "@babel/plugin-syntax-jsx": "7.12.1",
+        "@mdx-js/util": "1.6.22",
+        "is-alphabetical": "1.0.4",
+        "remark-parse": "8.0.3",
+        "unified": "9.2.0"
       },
       "dependencies": {
-        "cssesc": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz",
-          "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==",
-          "dev": true
+        "@babel/core": {
+          "version": "7.12.9",
+          "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.9.tgz",
+          "integrity": "sha512-gTXYh3M5wb7FRXQy+FErKFAv90BnlOuNn1QkCK2lREoPAjrQCO49+HVSrFoe5uakFAF5eenS75KbO2vQiLrTMQ==",
+          "dev": true,
+          "requires": {
+            "@babel/code-frame": "^7.10.4",
+            "@babel/generator": "^7.12.5",
+            "@babel/helper-module-transforms": "^7.12.1",
+            "@babel/helpers": "^7.12.5",
+            "@babel/parser": "^7.12.7",
+            "@babel/template": "^7.12.7",
+            "@babel/traverse": "^7.12.9",
+            "@babel/types": "^7.12.7",
+            "convert-source-map": "^1.7.0",
+            "debug": "^4.1.0",
+            "gensync": "^1.0.0-beta.1",
+            "json5": "^2.1.2",
+            "lodash": "^4.17.19",
+            "resolve": "^1.3.2",
+            "semver": "^5.4.1",
+            "source-map": "^0.5.0"
+          }
         },
-        "picocolors": {
-          "version": "0.2.1",
-          "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz",
-          "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==",
+        "@babel/helper-plugin-utils": {
+          "version": "7.10.4",
+          "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz",
+          "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==",
           "dev": true
         },
-        "postcss": {
-          "version": "7.0.39",
-          "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz",
-          "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==",
+        "@babel/plugin-proposal-object-rest-spread": {
+          "version": "7.12.1",
+          "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.12.1.tgz",
+          "integrity": "sha512-s6SowJIjzlhx8o7lsFx5zmY4At6CTtDvgNQDdPzkBQucle58A6b/TTeEBYtyDgmcXjUTM+vE8YOGHZzzbc/ioA==",
           "dev": true,
           "requires": {
-            "picocolors": "^0.2.1",
-            "source-map": "^0.6.1"
+            "@babel/helper-plugin-utils": "^7.10.4",
+            "@babel/plugin-syntax-object-rest-spread": "^7.8.0",
+            "@babel/plugin-transform-parameters": "^7.12.1"
           }
         },
-        "postcss-selector-parser": {
-          "version": "5.0.0",
-          "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz",
-          "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==",
+        "@babel/plugin-syntax-jsx": {
+          "version": "7.12.1",
+          "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.12.1.tgz",
+          "integrity": "sha512-1yRi7yAtB0ETgxdY9ti/p2TivUxJkTdhu/ZbF9MshVGqOx1TdB3b7xCXs49Fupgg50N45KcAsRP/ZqWjs9SRjg==",
           "dev": true,
           "requires": {
-            "cssesc": "^2.0.0",
-            "indexes-of": "^1.0.1",
-            "uniq": "^1.0.1"
+            "@babel/helper-plugin-utils": "^7.10.4"
           }
         },
+        "semver": {
+          "version": "5.7.1",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+          "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+          "dev": true
+        },
         "source-map": {
-          "version": "0.6.1",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+          "version": "0.5.7",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
           "dev": true
         }
       }
     },
-    "postcss-reduce-initial": {
-      "version": "5.0.2",
-      "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.0.2.tgz",
-      "integrity": "sha512-v/kbAAQ+S1V5v9TJvbGkV98V2ERPdU6XvMcKMjqAlYiJ2NtsHGlKYLPjWWcXlaTKNxooId7BGxeraK8qXvzKtw==",
+    "remark-parse": {
+      "version": "8.0.3",
+      "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-8.0.3.tgz",
+      "integrity": "sha512-E1K9+QLGgggHxCQtLt++uXltxEprmWzNfg+MxpfHsZlrddKzZ/hZyWHDbK3/Ap8HJQqYJRXP+jHczdL6q6i85Q==",
       "dev": true,
       "requires": {
-        "browserslist": "^4.16.6",
-        "caniuse-api": "^3.0.0"
+        "ccount": "^1.0.0",
+        "collapse-white-space": "^1.0.2",
+        "is-alphabetical": "^1.0.0",
+        "is-decimal": "^1.0.0",
+        "is-whitespace-character": "^1.0.0",
+        "is-word-character": "^1.0.0",
+        "markdown-escapes": "^1.0.0",
+        "parse-entities": "^2.0.0",
+        "repeat-string": "^1.5.4",
+        "state-toggle": "^1.0.0",
+        "trim": "0.0.1",
+        "trim-trailing-lines": "^1.0.0",
+        "unherit": "^1.0.4",
+        "unist-util-remove-position": "^2.0.0",
+        "vfile-location": "^3.0.0",
+        "xtend": "^4.0.1"
       }
     },
-    "postcss-reduce-transforms": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.0.1.tgz",
-      "integrity": "sha512-a//FjoPeFkRuAguPscTVmRQUODP+f3ke2HqFNgGPwdYnpeC29RZdCBvGRGTsKpMURb/I3p6jdKoBQ2zI+9Q7kA==",
+    "remark-slug": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/remark-slug/-/remark-slug-6.1.0.tgz",
+      "integrity": "sha512-oGCxDF9deA8phWvxFuyr3oSJsdyUAxMFbA0mZ7Y1Sas+emILtO+e5WutF9564gDsEN4IXaQXm5pFo6MLH+YmwQ==",
       "dev": true,
       "requires": {
-        "cssnano-utils": "^2.0.1",
-        "postcss-value-parser": "^4.1.0"
+        "github-slugger": "^1.0.0",
+        "mdast-util-to-string": "^1.0.0",
+        "unist-util-visit": "^2.0.0"
       }
     },
-    "postcss-replace-overflow-wrap": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-3.0.0.tgz",
-      "integrity": "sha512-2T5hcEHArDT6X9+9dVSPQdo7QHzG4XKclFT8rU5TzJPDN7RIRTbO9c4drUISOVemLj03aezStHCR2AIcr8XLpw==",
+    "remark-squeeze-paragraphs": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/remark-squeeze-paragraphs/-/remark-squeeze-paragraphs-4.0.0.tgz",
+      "integrity": "sha512-8qRqmL9F4nuLPIgl92XUuxI3pFxize+F1H0e/W3llTk0UsjJaj01+RrirkMw7P21RKe4X6goQhYRSvNWX+70Rw==",
       "dev": true,
       "requires": {
-        "postcss": "^7.0.2"
+        "mdast-squeeze-paragraphs": "^4.0.0"
+      }
+    },
+    "remove-trailing-separator": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
+      "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=",
+      "dev": true
+    },
+    "renderkid": {
+      "version": "2.0.7",
+      "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.7.tgz",
+      "integrity": "sha512-oCcFyxaMrKsKcTY59qnCAtmDVSLfPbrv6A3tVbPdFMMrv5jaK10V6m40cKsoPNhAqN6rmHW9sswW4o3ruSrwUQ==",
+      "dev": true,
+      "requires": {
+        "css-select": "^4.1.3",
+        "dom-converter": "^0.2.0",
+        "htmlparser2": "^6.1.0",
+        "lodash": "^4.17.21",
+        "strip-ansi": "^3.0.1"
       },
       "dependencies": {
-        "picocolors": {
-          "version": "0.2.1",
-          "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz",
-          "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==",
+        "ansi-regex": {
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+          "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
           "dev": true
         },
-        "postcss": {
-          "version": "7.0.39",
-          "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz",
-          "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==",
+        "htmlparser2": {
+          "version": "6.1.0",
+          "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz",
+          "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==",
           "dev": true,
           "requires": {
-            "picocolors": "^0.2.1",
-            "source-map": "^0.6.1"
+            "domelementtype": "^2.0.1",
+            "domhandler": "^4.0.0",
+            "domutils": "^2.5.2",
+            "entities": "^2.0.0"
           }
         },
-        "source-map": {
-          "version": "0.6.1",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
-          "dev": true
+        "strip-ansi": {
+          "version": "3.0.1",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+          "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+          "dev": true,
+          "requires": {
+            "ansi-regex": "^2.0.0"
+          }
         }
       }
     },
-    "postcss-selector-matches": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/postcss-selector-matches/-/postcss-selector-matches-4.0.0.tgz",
-      "integrity": "sha512-LgsHwQR/EsRYSqlwdGzeaPKVT0Ml7LAT6E75T8W8xLJY62CE4S/l03BWIt3jT8Taq22kXP08s2SfTSzaraoPww==",
+    "repeat-element": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz",
+      "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==",
+      "dev": true
+    },
+    "repeat-string": {
+      "version": "1.6.1",
+      "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
+      "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=",
+      "dev": true
+    },
+    "repeating": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz",
+      "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=",
       "dev": true,
+      "optional": true,
       "requires": {
-        "balanced-match": "^1.0.0",
-        "postcss": "^7.0.2"
+        "is-finite": "^1.0.0"
+      }
+    },
+    "require-directory": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+      "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
+      "dev": true
+    },
+    "require-from-string": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
+      "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+      "dev": true
+    },
+    "require-main-filename": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
+      "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
+      "dev": true
+    },
+    "requireindex": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/requireindex/-/requireindex-1.2.0.tgz",
+      "integrity": "sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww==",
+      "dev": true
+    },
+    "requires-port": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
+      "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=",
+      "dev": true
+    },
+    "resolve": {
+      "version": "1.22.0",
+      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz",
+      "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==",
+      "dev": true,
+      "requires": {
+        "is-core-module": "^2.8.1",
+        "path-parse": "^1.0.7",
+        "supports-preserve-symlinks-flag": "^1.0.0"
+      }
+    },
+    "resolve-cwd": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz",
+      "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=",
+      "dev": true,
+      "requires": {
+        "resolve-from": "^3.0.0"
       },
       "dependencies": {
-        "picocolors": {
-          "version": "0.2.1",
-          "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz",
-          "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==",
-          "dev": true
-        },
-        "postcss": {
-          "version": "7.0.39",
-          "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz",
-          "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==",
-          "dev": true,
-          "requires": {
-            "picocolors": "^0.2.1",
-            "source-map": "^0.6.1"
-          }
-        },
-        "source-map": {
-          "version": "0.6.1",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+        "resolve-from": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
+          "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=",
           "dev": true
         }
       }
     },
-    "postcss-selector-not": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-4.0.1.tgz",
-      "integrity": "sha512-YolvBgInEK5/79C+bdFMyzqTg6pkYqDbzZIST/PDMqa/o3qtXenD05apBG2jLgT0/BQ77d4U2UK12jWpilqMAQ==",
+    "resolve-from": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+      "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+      "dev": true
+    },
+    "resolve-url": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
+      "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=",
+      "dev": true
+    },
+    "resolve-url-loader": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-4.0.0.tgz",
+      "integrity": "sha512-05VEMczVREcbtT7Bz+C+96eUO5HDNvdthIiMB34t7FcF8ehcu4wC0sSgPUubs3XW2Q3CNLJk/BJrCU9wVRymiA==",
       "dev": true,
       "requires": {
-        "balanced-match": "^1.0.0",
-        "postcss": "^7.0.2"
+        "adjust-sourcemap-loader": "^4.0.0",
+        "convert-source-map": "^1.7.0",
+        "loader-utils": "^2.0.0",
+        "postcss": "^7.0.35",
+        "source-map": "0.6.1"
       },
       "dependencies": {
         "picocolors": {
@@ -10047,682 +22042,693 @@
         }
       }
     },
-    "postcss-selector-parser": {
-      "version": "6.0.6",
-      "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz",
-      "integrity": "sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg==",
-      "dev": true,
-      "requires": {
-        "cssesc": "^3.0.0",
-        "util-deprecate": "^1.0.2"
-      }
-    },
-    "postcss-svgo": {
-      "version": "5.0.3",
-      "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.0.3.tgz",
-      "integrity": "sha512-41XZUA1wNDAZrQ3XgWREL/M2zSw8LJPvb5ZWivljBsUQAGoEKMYm6okHsTjJxKYI4M75RQEH4KYlEM52VwdXVA==",
-      "dev": true,
-      "requires": {
-        "postcss-value-parser": "^4.1.0",
-        "svgo": "^2.7.0"
-      }
-    },
-    "postcss-unique-selectors": {
-      "version": "5.0.2",
-      "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.0.2.tgz",
-      "integrity": "sha512-w3zBVlrtZm7loQWRPVC0yjUwwpty7OM6DnEHkxcSQXO1bMS3RJ+JUS5LFMSDZHJcvGsRwhZinCWVqn8Kej4EDA==",
-      "dev": true,
-      "requires": {
-        "alphanum-sort": "^1.0.2",
-        "postcss-selector-parser": "^6.0.5"
-      }
-    },
-    "postcss-value-parser": {
-      "version": "4.2.0",
-      "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
-      "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
-      "dev": true
-    },
-    "postcss-values-parser": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/postcss-values-parser/-/postcss-values-parser-2.0.1.tgz",
-      "integrity": "sha512-2tLuBsA6P4rYTNKCXYG/71C7j1pU6pK503suYOmn4xYrQIzW+opD+7FAFNuGSdZC/3Qfy334QbeMu7MEb8gOxg==",
+    "restore-cursor": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
+      "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
       "dev": true,
       "requires": {
-        "flatten": "^1.0.2",
-        "indexes-of": "^1.0.1",
-        "uniq": "^1.0.1"
+        "onetime": "^5.1.0",
+        "signal-exit": "^3.0.2"
       }
     },
-    "prelude-ls": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
-      "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+    "ret": {
+      "version": "0.1.15",
+      "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
+      "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==",
       "dev": true
     },
-    "pretty-bytes": {
-      "version": "5.6.0",
-      "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz",
-      "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==",
+    "retry": {
+      "version": "0.12.0",
+      "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
+      "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=",
       "dev": true
     },
-    "process-nextick-args": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
-      "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
-    },
-    "progress": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
-      "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
+    "reusify": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
+      "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
       "dev": true
     },
-    "promise-inflight": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz",
-      "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=",
+    "rfdc": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz",
+      "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==",
       "dev": true
     },
-    "promise-retry": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz",
-      "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==",
+    "rimraf": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+      "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
       "dev": true,
       "requires": {
-        "err-code": "^2.0.2",
-        "retry": "^0.12.0"
+        "glob": "^7.1.3"
       }
     },
-    "proxy-addr": {
-      "version": "2.0.7",
-      "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
-      "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
+    "ripemd160": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz",
+      "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==",
       "dev": true,
       "requires": {
-        "forwarded": "0.2.0",
-        "ipaddr.js": "1.9.1"
+        "hash-base": "^3.0.0",
+        "inherits": "^2.0.1"
       }
     },
-    "prr": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
-      "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=",
+    "rsvp": {
+      "version": "4.8.5",
+      "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz",
+      "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==",
       "dev": true
     },
-    "psl": {
-      "version": "1.8.0",
-      "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz",
-      "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==",
+    "run-async": {
+      "version": "2.4.1",
+      "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz",
+      "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==",
       "dev": true
     },
-    "pump": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
-      "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
+    "run-parallel": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+      "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
       "dev": true,
       "requires": {
-        "end-of-stream": "^1.1.0",
-        "once": "^1.3.1"
+        "queue-microtask": "^1.2.2"
       }
     },
-    "punycode": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
-      "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
-    },
-    "qjobs": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz",
-      "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==",
-      "dev": true
-    },
-    "qs": {
-      "version": "6.7.0",
-      "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
-      "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==",
-      "dev": true
-    },
-    "querystring": {
-      "version": "0.2.0",
-      "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
-      "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=",
-      "dev": true
-    },
-    "querystringify": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
-      "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
-      "dev": true
-    },
-    "queue-microtask": {
-      "version": "1.2.3",
-      "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
-      "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
-      "dev": true
-    },
-    "randombytes": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
-      "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
+    "run-queue": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz",
+      "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=",
       "dev": true,
       "requires": {
-        "safe-buffer": "^5.1.0"
+        "aproba": "^1.1.1"
+      },
+      "dependencies": {
+        "aproba": {
+          "version": "1.2.0",
+          "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
+          "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
+          "dev": true
+        }
       }
     },
-    "range-parser": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
-      "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
-      "dev": true
-    },
-    "raw-body": {
-      "version": "2.4.0",
-      "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
-      "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
-      "dev": true,
+    "rxjs": {
+      "version": "6.6.7",
+      "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz",
+      "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==",
       "requires": {
-        "bytes": "3.1.0",
-        "http-errors": "1.7.2",
-        "iconv-lite": "0.4.24",
-        "unpipe": "1.0.0"
+        "tslib": "^1.9.0"
       },
       "dependencies": {
-        "bytes": {
-          "version": "3.1.0",
-          "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
-          "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==",
-          "dev": true
+        "tslib": {
+          "version": "1.14.1",
+          "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+          "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
         }
       }
     },
-    "raw-loader": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-4.0.2.tgz",
-      "integrity": "sha512-ZnScIV3ag9A4wPX/ZayxL/jZH+euYb6FcUinPcgiQW0+UBtEv0O6Q3lGd3cqJ+GHH+rksEv3Pj99oxJ3u3VIKA==",
+    "safe-buffer": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+      "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+    },
+    "safe-regex": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
+      "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=",
+      "dev": true,
       "requires": {
-        "loader-utils": "^2.0.0",
-        "schema-utils": "^3.0.0"
+        "ret": "~0.1.10"
       }
     },
-    "read-cache": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
-      "integrity": "sha1-5mTvMRYRZsl1HNvo28+GtftY93Q=",
+    "safer-buffer": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+      "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+      "dev": true
+    },
+    "sane": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz",
+      "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==",
       "dev": true,
       "requires": {
-        "pify": "^2.3.0"
+        "@cnakazawa/watch": "^1.0.3",
+        "anymatch": "^2.0.0",
+        "capture-exit": "^2.0.0",
+        "exec-sh": "^0.3.2",
+        "execa": "^1.0.0",
+        "fb-watchman": "^2.0.0",
+        "micromatch": "^3.1.4",
+        "minimist": "^1.1.1",
+        "walker": "~1.0.5"
       },
       "dependencies": {
-        "pify": {
-          "version": "2.3.0",
-          "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
-          "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
-          "dev": true
+        "anymatch": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
+          "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==",
+          "dev": true,
+          "requires": {
+            "micromatch": "^3.1.4",
+            "normalize-path": "^2.1.1"
+          }
+        },
+        "braces": {
+          "version": "2.3.2",
+          "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
+          "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
+          "dev": true,
+          "requires": {
+            "arr-flatten": "^1.1.0",
+            "array-unique": "^0.3.2",
+            "extend-shallow": "^2.0.1",
+            "fill-range": "^4.0.0",
+            "isobject": "^3.0.1",
+            "repeat-element": "^1.1.2",
+            "snapdragon": "^0.8.1",
+            "snapdragon-node": "^2.0.1",
+            "split-string": "^3.0.2",
+            "to-regex": "^3.0.1"
+          },
+          "dependencies": {
+            "extend-shallow": {
+              "version": "2.0.1",
+              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+              "dev": true,
+              "requires": {
+                "is-extendable": "^0.1.0"
+              }
+            }
+          }
+        },
+        "fill-range": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
+          "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
+          "dev": true,
+          "requires": {
+            "extend-shallow": "^2.0.1",
+            "is-number": "^3.0.0",
+            "repeat-string": "^1.6.1",
+            "to-regex-range": "^2.1.0"
+          },
+          "dependencies": {
+            "extend-shallow": {
+              "version": "2.0.1",
+              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+              "dev": true,
+              "requires": {
+                "is-extendable": "^0.1.0"
+              }
+            }
+          }
+        },
+        "is-number": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
+          "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
+          "dev": true,
+          "requires": {
+            "kind-of": "^3.0.2"
+          },
+          "dependencies": {
+            "kind-of": {
+              "version": "3.2.2",
+              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+              "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+              "dev": true,
+              "requires": {
+                "is-buffer": "^1.1.5"
+              }
+            }
+          }
+        },
+        "micromatch": {
+          "version": "3.1.10",
+          "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
+          "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
+          "dev": true,
+          "requires": {
+            "arr-diff": "^4.0.0",
+            "array-unique": "^0.3.2",
+            "braces": "^2.3.1",
+            "define-property": "^2.0.2",
+            "extend-shallow": "^3.0.2",
+            "extglob": "^2.0.4",
+            "fragment-cache": "^0.2.1",
+            "kind-of": "^6.0.2",
+            "nanomatch": "^1.2.9",
+            "object.pick": "^1.3.0",
+            "regex-not": "^1.0.0",
+            "snapdragon": "^0.8.1",
+            "to-regex": "^3.0.2"
+          }
+        },
+        "normalize-path": {
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
+          "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
+          "dev": true,
+          "requires": {
+            "remove-trailing-separator": "^1.0.1"
+          }
+        },
+        "to-regex-range": {
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
+          "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
+          "dev": true,
+          "requires": {
+            "is-number": "^3.0.0",
+            "repeat-string": "^1.6.1"
+          }
         }
       }
     },
-    "read-package-json-fast": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-2.0.3.tgz",
-      "integrity": "sha512-W/BKtbL+dUjTuRL2vziuYhp76s5HZ9qQhd/dKfWIZveD0O40453QNyZhC0e63lqZrAQ4jiOapVoeJ7JrszenQQ==",
+    "sass": {
+      "version": "1.36.0",
+      "resolved": "https://registry.npmjs.org/sass/-/sass-1.36.0.tgz",
+      "integrity": "sha512-fQzEjipfOv5kh930nu3Imzq3ie/sGDc/4KtQMJlt7RRdrkQSfe37Bwi/Rf/gfuYHsIuE1fIlDMvpyMcEwjnPvg==",
       "dev": true,
       "requires": {
-        "json-parse-even-better-errors": "^2.3.0",
-        "npm-normalize-package-bin": "^1.0.1"
-      }
-    },
-    "readable-stream": {
-      "version": "2.3.7",
-      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
-      "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
-      "requires": {
-        "core-util-is": "~1.0.0",
-        "inherits": "~2.0.3",
-        "isarray": "~1.0.0",
-        "process-nextick-args": "~2.0.0",
-        "safe-buffer": "~5.1.1",
-        "string_decoder": "~1.1.1",
-        "util-deprecate": "~1.0.1"
+        "chokidar": ">=3.0.0 <4.0.0"
       }
     },
-    "readdirp": {
-      "version": "3.6.0",
-      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
-      "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+    "sass-loader": {
+      "version": "12.1.0",
+      "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.1.0.tgz",
+      "integrity": "sha512-FVJZ9kxVRYNZTIe2xhw93n3xJNYZADr+q69/s98l9nTCrWASo+DR2Ot0s5xTKQDDEosUkatsGeHxcH4QBp5bSg==",
       "dev": true,
       "requires": {
-        "picomatch": "^2.2.1"
+        "klona": "^2.0.4",
+        "neo-async": "^2.6.2"
       }
     },
-    "reflect-metadata": {
-      "version": "0.1.13",
-      "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz",
-      "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==",
-      "dev": true
-    },
-    "regenerate": {
-      "version": "1.4.2",
-      "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
-      "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==",
+    "sax": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
+      "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
       "dev": true
     },
-    "regenerate-unicode-properties": {
-      "version": "9.0.0",
-      "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-9.0.0.tgz",
-      "integrity": "sha512-3E12UeNSPfjrgwjkR81m5J7Aw/T55Tu7nUyZVQYCKEOs+2dkxEY+DpPtZzO4YruuiPb7NkYLVcyJC4+zCbk5pA==",
+    "scheduler": {
+      "version": "0.19.1",
+      "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz",
+      "integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==",
       "dev": true,
       "requires": {
-        "regenerate": "^1.4.2"
+        "loose-envify": "^1.1.0",
+        "object-assign": "^4.1.1"
       }
     },
-    "regenerator-runtime": {
-      "version": "0.13.9",
-      "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz",
-      "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==",
-      "dev": true
-    },
-    "regenerator-transform": {
-      "version": "0.14.5",
-      "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.5.tgz",
-      "integrity": "sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw==",
-      "dev": true,
+    "schema-utils": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz",
+      "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==",
       "requires": {
-        "@babel/runtime": "^7.8.4"
+        "@types/json-schema": "^7.0.8",
+        "ajv": "^6.12.5",
+        "ajv-keywords": "^3.5.2"
       }
     },
-    "regex-not": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz",
-      "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==",
+    "scope-analyzer": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/scope-analyzer/-/scope-analyzer-2.1.2.tgz",
+      "integrity": "sha512-5cfCmsTYV/wPaRIItNxatw02ua/MThdIUNnUOCYp+3LSEJvnG804ANw2VLaavNILIfWXF1D1G2KNANkBBvInwQ==",
       "dev": true,
       "requires": {
-        "extend-shallow": "^3.0.2",
-        "safe-regex": "^1.1.0"
+        "array-from": "^2.1.1",
+        "dash-ast": "^2.0.1",
+        "es6-map": "^0.1.5",
+        "es6-set": "^0.1.5",
+        "es6-symbol": "^3.1.1",
+        "estree-is-function": "^1.0.0",
+        "get-assigned-identifiers": "^1.1.0"
       }
     },
-    "regex-parser": {
-      "version": "2.2.11",
-      "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.11.tgz",
-      "integrity": "sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==",
+    "select-hose": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
+      "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=",
       "dev": true
     },
-    "regexp.prototype.flags": {
-      "version": "1.3.1",
-      "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz",
-      "integrity": "sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA==",
+    "selfsigned": {
+      "version": "1.10.14",
+      "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.14.tgz",
+      "integrity": "sha512-lkjaiAye+wBZDCBsu5BGi0XiLRxeUlsGod5ZP924CRSEoGuZAw/f7y9RKu28rwTfiHVhdavhB0qH0INV6P1lEA==",
       "dev": true,
       "requires": {
-        "call-bind": "^1.0.2",
-        "define-properties": "^1.1.3"
+        "node-forge": "^0.10.0"
       }
     },
-    "regexpp": {
-      "version": "3.2.0",
-      "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz",
-      "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==",
-      "dev": true
-    },
-    "regexpu-core": {
-      "version": "4.8.0",
-      "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.8.0.tgz",
-      "integrity": "sha512-1F6bYsoYiz6is+oz70NWur2Vlh9KWtswuRuzJOfeYUrfPX2o8n74AnUVaOGDbUqVGO9fNHu48/pjJO4sNVwsOg==",
+    "semver": {
+      "version": "7.3.5",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
+      "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
       "dev": true,
       "requires": {
-        "regenerate": "^1.4.2",
-        "regenerate-unicode-properties": "^9.0.0",
-        "regjsgen": "^0.5.2",
-        "regjsparser": "^0.7.0",
-        "unicode-match-property-ecmascript": "^2.0.0",
-        "unicode-match-property-value-ecmascript": "^2.0.0"
+        "lru-cache": "^6.0.0"
       }
     },
-    "regjsgen": {
-      "version": "0.5.2",
-      "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.2.tgz",
-      "integrity": "sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A==",
-      "dev": true
-    },
-    "regjsparser": {
-      "version": "0.7.0",
-      "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.7.0.tgz",
-      "integrity": "sha512-A4pcaORqmNMDVwUjWoTzuhwMGpP+NykpfqAsEgI1FSH/EzC7lrN5TMd+kN8YCovX+jMpu8eaqXgXPCa0g8FQNQ==",
+    "send": {
+      "version": "0.18.0",
+      "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
+      "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
       "dev": true,
       "requires": {
-        "jsesc": "~0.5.0"
-      },
-      "dependencies": {
-        "jsesc": {
-          "version": "0.5.0",
-          "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz",
-          "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=",
-          "dev": true
-        }
-      }
-    },
-    "remove-trailing-separator": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
-      "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=",
-      "dev": true
-    },
-    "repeat-element": {
-      "version": "1.1.4",
-      "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz",
-      "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==",
-      "dev": true
-    },
-    "repeat-string": {
-      "version": "1.6.1",
-      "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
-      "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=",
-      "dev": true
-    },
-    "request": {
-      "version": "2.88.2",
-      "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
-      "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==",
-      "dev": true,
-      "requires": {
-        "aws-sign2": "~0.7.0",
-        "aws4": "^1.8.0",
-        "caseless": "~0.12.0",
-        "combined-stream": "~1.0.6",
-        "extend": "~3.0.2",
-        "forever-agent": "~0.6.1",
-        "form-data": "~2.3.2",
-        "har-validator": "~5.1.3",
-        "http-signature": "~1.2.0",
-        "is-typedarray": "~1.0.0",
-        "isstream": "~0.1.2",
-        "json-stringify-safe": "~5.0.1",
-        "mime-types": "~2.1.19",
-        "oauth-sign": "~0.9.0",
-        "performance-now": "^2.1.0",
-        "qs": "~6.5.2",
-        "safe-buffer": "^5.1.2",
-        "tough-cookie": "~2.5.0",
-        "tunnel-agent": "^0.6.0",
-        "uuid": "^3.3.2"
+        "debug": "2.6.9",
+        "depd": "2.0.0",
+        "destroy": "1.2.0",
+        "encodeurl": "~1.0.2",
+        "escape-html": "~1.0.3",
+        "etag": "~1.8.1",
+        "fresh": "0.5.2",
+        "http-errors": "2.0.0",
+        "mime": "1.6.0",
+        "ms": "2.1.3",
+        "on-finished": "2.4.1",
+        "range-parser": "~1.2.1",
+        "statuses": "2.0.1"
       },
       "dependencies": {
-        "qs": {
-          "version": "6.5.2",
-          "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
-          "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==",
+        "debug": {
+          "version": "2.6.9",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+          "dev": true,
+          "requires": {
+            "ms": "2.0.0"
+          },
+          "dependencies": {
+            "ms": {
+              "version": "2.0.0",
+              "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+              "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+              "dev": true
+            }
+          }
+        },
+        "ms": {
+          "version": "2.1.3",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+          "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
           "dev": true
         }
       }
     },
-    "require-directory": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
-      "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I="
-    },
-    "require-from-string": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
-      "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
-      "dev": true
-    },
-    "require-main-filename": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
-      "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
-      "dev": true
-    },
-    "requires-port": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
-      "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=",
-      "dev": true
-    },
-    "resolve": {
-      "version": "1.20.0",
-      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz",
-      "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==",
+    "serialize-javascript": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz",
+      "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==",
       "dev": true,
       "requires": {
-        "is-core-module": "^2.2.0",
-        "path-parse": "^1.0.6"
+        "randombytes": "^2.1.0"
       }
     },
-    "resolve-cwd": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz",
-      "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=",
+    "serve-favicon": {
+      "version": "2.5.0",
+      "resolved": "https://registry.npmjs.org/serve-favicon/-/serve-favicon-2.5.0.tgz",
+      "integrity": "sha1-k10kDN/g9YBTB/3+ln2IlCosvPA=",
       "dev": true,
       "requires": {
-        "resolve-from": "^3.0.0"
+        "etag": "~1.8.1",
+        "fresh": "0.5.2",
+        "ms": "2.1.1",
+        "parseurl": "~1.3.2",
+        "safe-buffer": "5.1.1"
       },
       "dependencies": {
-        "resolve-from": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
-          "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=",
+        "ms": {
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+          "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
+          "dev": true
+        },
+        "safe-buffer": {
+          "version": "5.1.1",
+          "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
+          "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==",
           "dev": true
         }
       }
     },
-    "resolve-from": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
-      "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
-      "dev": true
-    },
-    "resolve-url": {
-      "version": "0.2.1",
-      "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
-      "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=",
-      "dev": true
-    },
-    "resolve-url-loader": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-4.0.0.tgz",
-      "integrity": "sha512-05VEMczVREcbtT7Bz+C+96eUO5HDNvdthIiMB34t7FcF8ehcu4wC0sSgPUubs3XW2Q3CNLJk/BJrCU9wVRymiA==",
+    "serve-index": {
+      "version": "1.9.1",
+      "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz",
+      "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=",
       "dev": true,
       "requires": {
-        "adjust-sourcemap-loader": "^4.0.0",
-        "convert-source-map": "^1.7.0",
-        "loader-utils": "^2.0.0",
-        "postcss": "^7.0.35",
-        "source-map": "0.6.1"
+        "accepts": "~1.3.4",
+        "batch": "0.6.1",
+        "debug": "2.6.9",
+        "escape-html": "~1.0.3",
+        "http-errors": "~1.6.2",
+        "mime-types": "~2.1.17",
+        "parseurl": "~1.3.2"
       },
       "dependencies": {
-        "picocolors": {
-          "version": "0.2.1",
-          "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz",
-          "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==",
+        "debug": {
+          "version": "2.6.9",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+          "dev": true,
+          "requires": {
+            "ms": "2.0.0"
+          }
+        },
+        "depd": {
+          "version": "1.1.2",
+          "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
+          "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=",
           "dev": true
         },
-        "postcss": {
-          "version": "7.0.39",
-          "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz",
-          "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==",
+        "http-errors": {
+          "version": "1.6.3",
+          "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
+          "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
           "dev": true,
           "requires": {
-            "picocolors": "^0.2.1",
-            "source-map": "^0.6.1"
+            "depd": "~1.1.2",
+            "inherits": "2.0.3",
+            "setprototypeof": "1.1.0",
+            "statuses": ">= 1.4.0 < 2"
           }
         },
-        "source-map": {
-          "version": "0.6.1",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+        "inherits": {
+          "version": "2.0.3",
+          "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+          "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
+          "dev": true
+        },
+        "ms": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+          "dev": true
+        },
+        "setprototypeof": {
+          "version": "1.1.0",
+          "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
+          "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==",
+          "dev": true
+        },
+        "statuses": {
+          "version": "1.5.0",
+          "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
+          "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=",
           "dev": true
         }
       }
     },
-    "restore-cursor": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
-      "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
+    "serve-static": {
+      "version": "1.15.0",
+      "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
+      "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
       "dev": true,
       "requires": {
-        "onetime": "^5.1.0",
-        "signal-exit": "^3.0.2"
+        "encodeurl": "~1.0.2",
+        "escape-html": "~1.0.3",
+        "parseurl": "~1.3.3",
+        "send": "0.18.0"
       }
     },
-    "ret": {
-      "version": "0.1.15",
-      "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
-      "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==",
-      "dev": true
-    },
-    "retry": {
-      "version": "0.12.0",
-      "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
-      "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=",
-      "dev": true
-    },
-    "reusify": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
-      "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
-      "dev": true
-    },
-    "rfdc": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz",
-      "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==",
+    "set-blocking": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
+      "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
       "dev": true
     },
-    "rimraf": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
-      "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+    "set-value": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
+      "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==",
       "dev": true,
       "requires": {
-        "glob": "^7.1.3"
+        "extend-shallow": "^2.0.1",
+        "is-extendable": "^0.1.1",
+        "is-plain-object": "^2.0.3",
+        "split-string": "^3.0.1"
+      },
+      "dependencies": {
+        "extend-shallow": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+          "dev": true,
+          "requires": {
+            "is-extendable": "^0.1.0"
+          }
+        }
       }
     },
-    "run-async": {
-      "version": "2.4.1",
-      "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz",
-      "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==",
-      "dev": true
+    "setimmediate": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
+      "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU="
     },
-    "run-parallel": {
+    "setprototypeof": {
       "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
-      "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+      "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
+      "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
+      "dev": true
+    },
+    "sha.js": {
+      "version": "2.4.11",
+      "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
+      "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
       "dev": true,
       "requires": {
-        "queue-microtask": "^1.2.2"
+        "inherits": "^2.0.1",
+        "safe-buffer": "^5.0.1"
       }
     },
-    "rxjs": {
-      "version": "6.6.7",
-      "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz",
-      "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==",
+    "shallow-clone": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz",
+      "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==",
+      "dev": true,
       "requires": {
-        "tslib": "^1.9.0"
-      },
-      "dependencies": {
-        "tslib": {
-          "version": "1.14.1",
-          "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
-          "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
-        }
+        "kind-of": "^6.0.2"
       }
     },
-    "safe-buffer": {
-      "version": "5.1.2",
-      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
-      "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+    "shallow-copy": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/shallow-copy/-/shallow-copy-0.0.1.tgz",
+      "integrity": "sha1-QV9CcC1z2BAzApLMXuhurhoRoXA=",
+      "dev": true
     },
-    "safe-regex": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
-      "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=",
+    "shebang-command": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
+      "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
       "dev": true,
       "requires": {
-        "ret": "~0.1.10"
+        "shebang-regex": "^1.0.0"
       }
     },
-    "safer-buffer": {
-      "version": "2.1.2",
-      "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
-      "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+    "shebang-regex": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
+      "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
       "dev": true
     },
-    "sass": {
-      "version": "1.36.0",
-      "resolved": "https://registry.npmjs.org/sass/-/sass-1.36.0.tgz",
-      "integrity": "sha512-fQzEjipfOv5kh930nu3Imzq3ie/sGDc/4KtQMJlt7RRdrkQSfe37Bwi/Rf/gfuYHsIuE1fIlDMvpyMcEwjnPvg==",
-      "dev": true,
+    "showdown": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/showdown/-/showdown-2.1.0.tgz",
+      "integrity": "sha512-/6NVYu4U819R2pUIk79n67SYgJHWCce0a5xTP979WbNp0FL9MN1I1QK662IDU1b6JzKTvmhgI7T7JYIxBi3kMQ==",
       "requires": {
-        "chokidar": ">=3.0.0 <4.0.0"
+        "commander": "^9.0.0"
       }
     },
-    "sass-loader": {
-      "version": "12.1.0",
-      "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.1.0.tgz",
-      "integrity": "sha512-FVJZ9kxVRYNZTIe2xhw93n3xJNYZADr+q69/s98l9nTCrWASo+DR2Ot0s5xTKQDDEosUkatsGeHxcH4QBp5bSg==",
+    "side-channel": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
+      "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
       "dev": true,
       "requires": {
-        "klona": "^2.0.4",
-        "neo-async": "^2.6.2"
+        "call-bind": "^1.0.0",
+        "get-intrinsic": "^1.0.2",
+        "object-inspect": "^1.9.0"
       }
     },
-    "sax": {
-      "version": "1.2.4",
-      "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
-      "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
+    "signal-exit": {
+      "version": "3.0.7",
+      "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
+      "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
       "dev": true
     },
-    "schema-utils": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz",
-      "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==",
-      "requires": {
-        "@types/json-schema": "^7.0.8",
-        "ajv": "^6.12.5",
-        "ajv-keywords": "^3.5.2"
-      }
+    "sisteransi": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
+      "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==",
+      "dev": true
     },
-    "select-hose": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
-      "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=",
+    "slash": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+      "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
       "dev": true
     },
-    "selfsigned": {
-      "version": "1.10.11",
-      "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.11.tgz",
-      "integrity": "sha512-aVmbPOfViZqOZPgRBT0+3u4yZFHpmnIghLMlAcb5/xhp5ZtB/RVnKhz5vl2M32CLXAqR4kha9zfhNg0Lf/sxKA==",
+    "slice-ansi": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz",
+      "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==",
       "dev": true,
       "requires": {
-        "node-forge": "^0.10.0"
+        "ansi-styles": "^4.0.0",
+        "astral-regex": "^2.0.0",
+        "is-fullwidth-code-point": "^3.0.0"
+      },
+      "dependencies": {
+        "ansi-styles": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+          "dev": true,
+          "requires": {
+            "color-convert": "^2.0.1"
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "dev": true,
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
+        }
       }
     },
-    "semver": {
-      "version": "7.3.5",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
-      "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
-      "dev": true,
-      "requires": {
-        "lru-cache": "^6.0.0"
-      }
+    "smart-buffer": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
+      "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==",
+      "dev": true
     },
-    "send": {
-      "version": "0.17.1",
-      "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
-      "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
+    "snapdragon": {
+      "version": "0.8.2",
+      "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
+      "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==",
       "dev": true,
       "requires": {
-        "debug": "2.6.9",
-        "depd": "~1.1.2",
-        "destroy": "~1.0.4",
-        "encodeurl": "~1.0.2",
-        "escape-html": "~1.0.3",
-        "etag": "~1.8.1",
-        "fresh": "0.5.2",
-        "http-errors": "~1.7.2",
-        "mime": "1.6.0",
-        "ms": "2.1.1",
-        "on-finished": "~2.3.0",
-        "range-parser": "~1.2.1",
-        "statuses": "~1.5.0"
+        "base": "^0.11.1",
+        "debug": "^2.2.0",
+        "define-property": "^0.2.5",
+        "extend-shallow": "^2.0.1",
+        "map-cache": "^0.2.2",
+        "source-map": "^0.5.6",
+        "source-map-resolve": "^0.5.0",
+        "use": "^3.1.0"
       },
       "dependencies": {
         "debug": {
@@ -10732,259 +22738,428 @@
           "dev": true,
           "requires": {
             "ms": "2.0.0"
-          },
-          "dependencies": {
-            "ms": {
-              "version": "2.0.0",
-              "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-              "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
-              "dev": true
-            }
+          }
+        },
+        "define-property": {
+          "version": "0.2.5",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+          "dev": true,
+          "requires": {
+            "is-descriptor": "^0.1.0"
+          }
+        },
+        "extend-shallow": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+          "dev": true,
+          "requires": {
+            "is-extendable": "^0.1.0"
           }
         },
         "ms": {
-          "version": "2.1.1",
-          "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
-          "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+          "dev": true
+        },
+        "source-map": {
+          "version": "0.5.7",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
           "dev": true
         }
       }
     },
-    "serialize-javascript": {
-      "version": "6.0.0",
-      "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz",
-      "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==",
+    "snapdragon-node": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz",
+      "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==",
       "dev": true,
       "requires": {
-        "randombytes": "^2.1.0"
+        "define-property": "^1.0.0",
+        "isobject": "^3.0.0",
+        "snapdragon-util": "^3.0.1"
+      },
+      "dependencies": {
+        "define-property": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+          "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+          "dev": true,
+          "requires": {
+            "is-descriptor": "^1.0.0"
+          }
+        },
+        "is-accessor-descriptor": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+          "dev": true,
+          "requires": {
+            "kind-of": "^6.0.0"
+          }
+        },
+        "is-data-descriptor": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+          "dev": true,
+          "requires": {
+            "kind-of": "^6.0.0"
+          }
+        },
+        "is-descriptor": {
+          "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+          "dev": true,
+          "requires": {
+            "is-accessor-descriptor": "^1.0.0",
+            "is-data-descriptor": "^1.0.0",
+            "kind-of": "^6.0.2"
+          }
+        }
       }
     },
-    "serve-index": {
-      "version": "1.9.1",
-      "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz",
-      "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=",
+    "snapdragon-util": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz",
+      "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==",
+      "dev": true,
+      "requires": {
+        "kind-of": "^3.2.0"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "dev": true,
+          "requires": {
+            "is-buffer": "^1.1.5"
+          }
+        }
+      }
+    },
+    "socket.io": {
+      "version": "4.5.1",
+      "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.5.1.tgz",
+      "integrity": "sha512-0y9pnIso5a9i+lJmsCdtmTTgJFFSvNQKDnPQRz28mGNnxbmqYg2QPtJTLFxhymFZhAIn50eHAKzJeiNaKr+yUQ==",
       "dev": true,
       "requires": {
         "accepts": "~1.3.4",
-        "batch": "0.6.1",
-        "debug": "2.6.9",
-        "escape-html": "~1.0.3",
-        "http-errors": "~1.6.2",
-        "mime-types": "~2.1.17",
-        "parseurl": "~1.3.2"
+        "base64id": "~2.0.0",
+        "debug": "~4.3.2",
+        "engine.io": "~6.2.0",
+        "socket.io-adapter": "~2.4.0",
+        "socket.io-parser": "~4.0.4"
+      }
+    },
+    "socket.io-adapter": {
+      "version": "2.4.0",
+      "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.4.0.tgz",
+      "integrity": "sha512-W4N+o69rkMEGVuk2D/cvca3uYsvGlMwsySWV447y99gUPghxq42BxqLNMndb+a1mm/5/7NeXVQS7RLa2XyXvYg==",
+      "dev": true
+    },
+    "socket.io-parser": {
+      "version": "4.0.4",
+      "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.4.tgz",
+      "integrity": "sha512-t+b0SS+IxG7Rxzda2EVvyBZbvFPBCjJoyHuE0P//7OAsN23GItzDRdWa6ALxZI/8R5ygK7jAR6t028/z+7295g==",
+      "dev": true,
+      "requires": {
+        "@types/component-emitter": "^1.2.10",
+        "component-emitter": "~1.3.0",
+        "debug": "~4.3.1"
+      }
+    },
+    "sockjs": {
+      "version": "0.3.24",
+      "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz",
+      "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==",
+      "dev": true,
+      "requires": {
+        "faye-websocket": "^0.11.3",
+        "uuid": "^8.3.2",
+        "websocket-driver": "^0.7.4"
+      }
+    },
+    "sockjs-client": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.6.0.tgz",
+      "integrity": "sha512-qVHJlyfdHFht3eBFZdKEXKTlb7I4IV41xnVNo8yUKA1UHcPJwgW2SvTq9LhnjjCywSkSK7c/e4nghU0GOoMCRQ==",
+      "dev": true,
+      "requires": {
+        "debug": "^3.2.7",
+        "eventsource": "^1.1.0",
+        "faye-websocket": "^0.11.4",
+        "inherits": "^2.0.4",
+        "url-parse": "^1.5.10"
       },
       "dependencies": {
         "debug": {
-          "version": "2.6.9",
-          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
-          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
-          "dev": true,
-          "requires": {
-            "ms": "2.0.0"
-          }
-        },
-        "http-errors": {
-          "version": "1.6.3",
-          "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
-          "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
+          "version": "3.2.7",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+          "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
           "dev": true,
           "requires": {
-            "depd": "~1.1.2",
-            "inherits": "2.0.3",
-            "setprototypeof": "1.1.0",
-            "statuses": ">= 1.4.0 < 2"
+            "ms": "^2.1.1"
           }
-        },
-        "inherits": {
-          "version": "2.0.3",
-          "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
-          "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
-          "dev": true
-        },
-        "ms": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
-          "dev": true
-        },
-        "setprototypeof": {
-          "version": "1.1.0",
-          "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
-          "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==",
-          "dev": true
         }
       }
     },
-    "serve-static": {
-      "version": "1.14.1",
-      "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
-      "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==",
+    "socks": {
+      "version": "2.6.2",
+      "resolved": "https://registry.npmjs.org/socks/-/socks-2.6.2.tgz",
+      "integrity": "sha512-zDZhHhZRY9PxRruRMR7kMhnf3I8hDs4S3f9RecfnGxvcBHQcKcIH/oUcEWffsfl1XxdYlA7nnlGbbTvPz9D8gA==",
       "dev": true,
       "requires": {
-        "encodeurl": "~1.0.2",
-        "escape-html": "~1.0.3",
-        "parseurl": "~1.3.3",
-        "send": "0.17.1"
+        "ip": "^1.1.5",
+        "smart-buffer": "^4.2.0"
       }
     },
-    "set-blocking": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
-      "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
+    "socks-proxy-agent": {
+      "version": "6.2.0",
+      "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.2.0.tgz",
+      "integrity": "sha512-wWqJhjb32Q6GsrUqzuFkukxb/zzide5quXYcMVpIjxalDBBYy2nqKCFQ/9+Ie4dvOYSQdOk3hUlZSdzZOd3zMQ==",
+      "dev": true,
+      "requires": {
+        "agent-base": "^6.0.2",
+        "debug": "^4.3.3",
+        "socks": "^2.6.2"
+      }
+    },
+    "source-list-map": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz",
+      "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==",
       "dev": true
     },
-    "set-immediate-shim": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz",
-      "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E="
+    "source-map": {
+      "version": "0.7.3",
+      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
+      "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
+      "dev": true
     },
-    "set-value": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
-      "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==",
+    "source-map-js": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
+      "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw=="
+    },
+    "source-map-loader": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-3.0.0.tgz",
+      "integrity": "sha512-GKGWqWvYr04M7tn8dryIWvb0s8YM41z82iQv01yBtIylgxax0CwvSy6gc2Y02iuXwEfGWRlMicH0nvms9UZphw==",
       "dev": true,
       "requires": {
-        "extend-shallow": "^2.0.1",
-        "is-extendable": "^0.1.1",
-        "is-plain-object": "^2.0.3",
-        "split-string": "^3.0.1"
+        "abab": "^2.0.5",
+        "iconv-lite": "^0.6.2",
+        "source-map-js": "^0.6.2"
       },
       "dependencies": {
-        "extend-shallow": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+        "iconv-lite": {
+          "version": "0.6.3",
+          "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+          "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
           "dev": true,
           "requires": {
-            "is-extendable": "^0.1.0"
+            "safer-buffer": ">= 2.1.2 < 3.0.0"
           }
+        },
+        "source-map-js": {
+          "version": "0.6.2",
+          "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-0.6.2.tgz",
+          "integrity": "sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==",
+          "dev": true
         }
       }
     },
-    "setprototypeof": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
-      "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==",
-      "dev": true
-    },
-    "shallow-clone": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz",
-      "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==",
+    "source-map-resolve": {
+      "version": "0.5.3",
+      "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz",
+      "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==",
       "dev": true,
       "requires": {
-        "kind-of": "^6.0.2"
+        "atob": "^2.1.2",
+        "decode-uri-component": "^0.2.0",
+        "resolve-url": "^0.2.1",
+        "source-map-url": "^0.4.0",
+        "urix": "^0.1.0"
       }
     },
-    "shebang-command": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
-      "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
+    "source-map-support": {
+      "version": "0.5.19",
+      "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz",
+      "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==",
       "dev": true,
       "requires": {
-        "shebang-regex": "^1.0.0"
+        "buffer-from": "^1.0.0",
+        "source-map": "^0.6.0"
+      },
+      "dependencies": {
+        "source-map": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+          "dev": true
+        }
       }
     },
-    "shebang-regex": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
-      "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
+    "source-map-url": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz",
+      "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==",
       "dev": true
     },
-    "showdown": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/showdown/-/showdown-2.0.0.tgz",
-      "integrity": "sha512-Gz0wkh/EBFbEH+Nb85nyRSrcRvPecgt8c6fk/ICaOQ2dVsWCvZU5jfViPtBIyLXVYooICO0WTl7vLsoPaIH09w==",
+    "sourcemap-codec": {
+      "version": "1.4.8",
+      "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
+      "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
+      "dev": true
+    },
+    "space-separated-tokens": {
+      "version": "1.1.5",
+      "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz",
+      "integrity": "sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==",
+      "dev": true
+    },
+    "spdx-correct": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz",
+      "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==",
+      "dev": true,
       "requires": {
-        "yargs": "^17.2.1"
+        "spdx-expression-parse": "^3.0.0",
+        "spdx-license-ids": "^3.0.0"
       }
     },
-    "signal-exit": {
-      "version": "3.0.6",
-      "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz",
-      "integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==",
+    "spdx-exceptions": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz",
+      "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==",
       "dev": true
     },
-    "slash": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
-      "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+    "spdx-expression-parse": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
+      "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
+      "dev": true,
+      "requires": {
+        "spdx-exceptions": "^2.1.0",
+        "spdx-license-ids": "^3.0.0"
+      }
+    },
+    "spdx-license-ids": {
+      "version": "3.0.11",
+      "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz",
+      "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==",
       "dev": true
     },
-    "slice-ansi": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz",
-      "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==",
+    "spdy": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz",
+      "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==",
       "dev": true,
       "requires": {
-        "ansi-styles": "^4.0.0",
-        "astral-regex": "^2.0.0",
-        "is-fullwidth-code-point": "^3.0.0"
+        "debug": "^4.1.0",
+        "handle-thing": "^2.0.0",
+        "http-deceiver": "^1.2.7",
+        "select-hose": "^2.0.0",
+        "spdy-transport": "^3.0.0"
+      }
+    },
+    "spdy-transport": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz",
+      "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==",
+      "dev": true,
+      "requires": {
+        "debug": "^4.1.0",
+        "detect-node": "^2.0.4",
+        "hpack.js": "^2.1.6",
+        "obuf": "^1.1.2",
+        "readable-stream": "^3.0.6",
+        "wbuf": "^1.7.3"
       },
       "dependencies": {
-        "ansi-styles": {
-          "version": "4.3.0",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
-          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
-          "dev": true,
-          "requires": {
-            "color-convert": "^2.0.1"
-          }
-        },
-        "color-convert": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
-          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+        "readable-stream": {
+          "version": "3.6.0",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+          "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
           "dev": true,
           "requires": {
-            "color-name": "~1.1.4"
+            "inherits": "^2.0.3",
+            "string_decoder": "^1.1.1",
+            "util-deprecate": "^1.0.1"
           }
-        },
-        "color-name": {
-          "version": "1.1.4",
-          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
-          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
-          "dev": true
-        },
-        "is-fullwidth-code-point": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
-          "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
-          "dev": true
         }
       }
     },
-    "smart-buffer": {
-      "version": "4.2.0",
-      "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
-      "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==",
+    "split": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz",
+      "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==",
+      "dev": true,
+      "requires": {
+        "through": "2"
+      }
+    },
+    "split-string": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
+      "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==",
+      "dev": true,
+      "requires": {
+        "extend-shallow": "^3.0.0"
+      }
+    },
+    "sprintf-js": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+      "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
+      "dev": true
+    },
+    "ssri": {
+      "version": "8.0.1",
+      "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz",
+      "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==",
+      "dev": true,
+      "requires": {
+        "minipass": "^3.1.1"
+      }
+    },
+    "stable": {
+      "version": "0.1.8",
+      "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz",
+      "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==",
       "dev": true
     },
-    "snapdragon": {
-      "version": "0.8.2",
-      "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
-      "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==",
+    "state-toggle": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/state-toggle/-/state-toggle-1.0.3.tgz",
+      "integrity": "sha512-d/5Z4/2iiCnHw6Xzghyhb+GcmF89bxwgXG60wjIiZaxnymbyOmI8Hk4VqHXiVVp6u2ysaskFfXg3ekCj4WNftQ==",
+      "dev": true
+    },
+    "static-eval": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/static-eval/-/static-eval-2.1.0.tgz",
+      "integrity": "sha512-agtxZ/kWSsCkI5E4QifRwsaPs0P0JmZV6dkLz6ILYfFYQGn+5plctanRN+IC8dJRiFkyXHrwEE3W9Wmx67uDbw==",
+      "dev": true,
+      "requires": {
+        "escodegen": "^1.11.1"
+      }
+    },
+    "static-extend": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
+      "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=",
       "dev": true,
       "requires": {
-        "base": "^0.11.1",
-        "debug": "^2.2.0",
         "define-property": "^0.2.5",
-        "extend-shallow": "^2.0.1",
-        "map-cache": "^0.2.2",
-        "source-map": "^0.5.6",
-        "source-map-resolve": "^0.5.0",
-        "use": "^3.1.0"
+        "object-copy": "^0.1.0"
       },
       "dependencies": {
-        "debug": {
-          "version": "2.6.9",
-          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
-          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
-          "dev": true,
-          "requires": {
-            "ms": "2.0.0"
-          }
-        },
         "define-property": {
           "version": "0.2.5",
           "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
@@ -10993,257 +23168,521 @@
           "requires": {
             "is-descriptor": "^0.1.0"
           }
-        },
-        "extend-shallow": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-          "dev": true,
-          "requires": {
-            "is-extendable": "^0.1.0"
-          }
-        },
-        "ms": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
-          "dev": true
-        },
-        "source-map": {
-          "version": "0.5.7",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
-          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
-          "dev": true
         }
       }
     },
-    "snapdragon-node": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz",
-      "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==",
+    "static-module": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/static-module/-/static-module-3.0.4.tgz",
+      "integrity": "sha512-gb0v0rrgpBkifXCa3yZXxqVmXDVE+ETXj6YlC/jt5VzOnGXR2C15+++eXuMDUYsePnbhf+lwW0pE1UXyOLtGCw==",
       "dev": true,
       "requires": {
-        "define-property": "^1.0.0",
-        "isobject": "^3.0.0",
-        "snapdragon-util": "^3.0.1"
+        "acorn-node": "^1.3.0",
+        "concat-stream": "~1.6.0",
+        "convert-source-map": "^1.5.1",
+        "duplexer2": "~0.1.4",
+        "escodegen": "^1.11.1",
+        "has": "^1.0.1",
+        "magic-string": "0.25.1",
+        "merge-source-map": "1.0.4",
+        "object-inspect": "^1.6.0",
+        "readable-stream": "~2.3.3",
+        "scope-analyzer": "^2.0.1",
+        "shallow-copy": "~0.0.1",
+        "static-eval": "^2.0.5",
+        "through2": "~2.0.3"
       },
       "dependencies": {
-        "define-property": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
-          "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
-          "dev": true,
-          "requires": {
-            "is-descriptor": "^1.0.0"
-          }
-        },
-        "is-accessor-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
-          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+        "magic-string": {
+          "version": "0.25.1",
+          "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.1.tgz",
+          "integrity": "sha512-sCuTz6pYom8Rlt4ISPFn6wuFodbKMIHUMv4Qko9P17dpxb7s52KJTmRuZZqHdGmLCK9AOcDare039nRIcfdkEg==",
           "dev": true,
           "requires": {
-            "kind-of": "^6.0.0"
+            "sourcemap-codec": "^1.4.1"
           }
         },
-        "is-data-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
-          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+        "merge-source-map": {
+          "version": "1.0.4",
+          "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.0.4.tgz",
+          "integrity": "sha1-pd5GU42uhNQRTMXqArR3KmNGcB8=",
           "dev": true,
           "requires": {
-            "kind-of": "^6.0.0"
+            "source-map": "^0.5.6"
           }
         },
-        "is-descriptor": {
-          "version": "1.0.2",
-          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
-          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
-          "dev": true,
-          "requires": {
-            "is-accessor-descriptor": "^1.0.0",
-            "is-data-descriptor": "^1.0.0",
-            "kind-of": "^6.0.2"
-          }
+        "source-map": {
+          "version": "0.5.7",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+          "dev": true
         }
       }
     },
-    "snapdragon-util": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz",
-      "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==",
+    "statuses": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
+      "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
+      "dev": true
+    },
+    "store2": {
+      "version": "2.13.2",
+      "resolved": "https://registry.npmjs.org/store2/-/store2-2.13.2.tgz",
+      "integrity": "sha512-CMtO2Uneg3SAz/d6fZ/6qbqqQHi2ynq6/KzMD/26gTkiEShCcpqFfTHgOxsE0egAq6SX3FmN4CeSqn8BzXQkJg==",
+      "dev": true
+    },
+    "storybook-dark-mode": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/storybook-dark-mode/-/storybook-dark-mode-1.1.0.tgz",
+      "integrity": "sha512-F+hG02zYGBzxGTUonA1XDV/CtMYm3OjF38Tu1CIUN+w+8hwUrwLcOtgtLLw6VjSrZdJ/ECK+tjXdKTV4oZqAXw==",
       "dev": true,
       "requires": {
-        "kind-of": "^3.2.0"
-      },
-      "dependencies": {
-        "kind-of": {
-          "version": "3.2.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-          "dev": true,
-          "requires": {
-            "is-buffer": "^1.1.5"
-          }
-        }
+        "fast-deep-equal": "^3.0.0",
+        "memoizerific": "^1.11.3"
       }
     },
-    "socket.io": {
-      "version": "4.4.1",
-      "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.4.1.tgz",
-      "integrity": "sha512-s04vrBswdQBUmuWJuuNTmXUVJhP0cVky8bBDhdkf8y0Ptsu7fKU2LuLbts9g+pdmAdyMMn8F/9Mf1/wbtUN0fg==",
+    "stream-browserify": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz",
+      "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==",
       "dev": true,
       "requires": {
-        "accepts": "~1.3.4",
-        "base64id": "~2.0.0",
-        "debug": "~4.3.2",
-        "engine.io": "~6.1.0",
-        "socket.io-adapter": "~2.3.3",
-        "socket.io-parser": "~4.0.4"
+        "inherits": "~2.0.1",
+        "readable-stream": "^2.0.2"
       }
     },
-    "socket.io-adapter": {
-      "version": "2.3.3",
-      "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.3.3.tgz",
-      "integrity": "sha512-Qd/iwn3VskrpNO60BeRyCyr8ZWw9CPZyitW4AQwmRZ8zCiyDiL+znRnWX6tDHXnWn1sJrM1+b6Mn6wEDJJ4aYQ==",
+    "stream-combiner": {
+      "version": "0.2.2",
+      "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.2.2.tgz",
+      "integrity": "sha1-rsjLrBd7Vrb0+kec7YwZEs7lKFg=",
+      "dev": true,
+      "requires": {
+        "duplexer": "~0.1.1",
+        "through": "~2.3.4"
+      }
+    },
+    "stream-each": {
+      "version": "1.2.3",
+      "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz",
+      "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==",
+      "dev": true,
+      "requires": {
+        "end-of-stream": "^1.1.0",
+        "stream-shift": "^1.0.0"
+      }
+    },
+    "stream-http": {
+      "version": "2.8.3",
+      "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz",
+      "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==",
+      "dev": true,
+      "requires": {
+        "builtin-status-codes": "^3.0.0",
+        "inherits": "^2.0.1",
+        "readable-stream": "^2.3.6",
+        "to-arraybuffer": "^1.0.0",
+        "xtend": "^4.0.0"
+      }
+    },
+    "stream-shift": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz",
+      "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==",
+      "dev": true
+    },
+    "streamroller": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.1.tgz",
+      "integrity": "sha512-iPhtd9unZ6zKdWgMeYGfSBuqCngyJy1B/GPi/lTpwGpa3bajuX30GjUVd0/Tn/Xhg0mr4DOSENozz9Y06qyonQ==",
+      "dev": true,
+      "requires": {
+        "date-format": "^4.0.10",
+        "debug": "^4.3.4",
+        "fs-extra": "^10.1.0"
+      }
+    },
+    "string-width": {
+      "version": "4.2.3",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+      "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+      "dev": true,
+      "requires": {
+        "emoji-regex": "^8.0.0",
+        "is-fullwidth-code-point": "^3.0.0",
+        "strip-ansi": "^6.0.1"
+      }
+    },
+    "string.prototype.matchall": {
+      "version": "4.0.7",
+      "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz",
+      "integrity": "sha512-f48okCX7JiwVi1NXCVWcFnZgADDC/n2vePlQ/KUCNqCikLLilQvwjMO8+BHVKvgzH0JB0J9LEPgxOGT02RoETg==",
+      "dev": true,
+      "requires": {
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.3",
+        "es-abstract": "^1.19.1",
+        "get-intrinsic": "^1.1.1",
+        "has-symbols": "^1.0.3",
+        "internal-slot": "^1.0.3",
+        "regexp.prototype.flags": "^1.4.1",
+        "side-channel": "^1.0.4"
+      }
+    },
+    "string.prototype.padend": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.3.tgz",
+      "integrity": "sha512-jNIIeokznm8SD/TZISQsZKYu7RJyheFNt84DUPrh482GC8RVp2MKqm2O5oBRdGxbDQoXrhhWtPIWQOiy20svUg==",
+      "dev": true,
+      "requires": {
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.3",
+        "es-abstract": "^1.19.1"
+      }
+    },
+    "string.prototype.padstart": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/string.prototype.padstart/-/string.prototype.padstart-3.1.3.tgz",
+      "integrity": "sha512-NZydyOMtYxpTjGqp0VN5PYUF/tsU15yDMZnUdj16qRUIUiMJkHHSDElYyQFrMu+/WloTpA7MQSiADhBicDfaoA==",
+      "dev": true,
+      "requires": {
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.3",
+        "es-abstract": "^1.19.1"
+      }
+    },
+    "string.prototype.trimend": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz",
+      "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==",
+      "dev": true,
+      "requires": {
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.4",
+        "es-abstract": "^1.19.5"
+      }
+    },
+    "string.prototype.trimstart": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz",
+      "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==",
+      "dev": true,
+      "requires": {
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.4",
+        "es-abstract": "^1.19.5"
+      }
+    },
+    "string_decoder": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+      "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+      "requires": {
+        "safe-buffer": "~5.1.0"
+      }
+    },
+    "strip-ansi": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+      "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+      "dev": true,
+      "requires": {
+        "ansi-regex": "^5.0.1"
+      }
+    },
+    "strip-bom": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz",
+      "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=",
+      "dev": true,
+      "optional": true,
+      "requires": {
+        "is-utf8": "^0.2.0"
+      }
+    },
+    "strip-eof": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
+      "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=",
+      "dev": true
+    },
+    "strip-final-newline": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
+      "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
+      "dev": true
+    },
+    "strip-indent": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz",
+      "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=",
+      "dev": true,
+      "optional": true,
+      "requires": {
+        "get-stdin": "^4.0.1"
+      }
+    },
+    "strip-json-comments": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+      "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+      "dev": true
+    },
+    "style-loader": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.2.1.tgz",
+      "integrity": "sha512-1k9ZosJCRFaRbY6hH49JFlRB0fVSbmnyq1iTPjNxUmGVjBNEmwrrHPenhlp+Lgo51BojHSf6pl2FcqYaN3PfVg==",
       "dev": true
     },
-    "socket.io-parser": {
-      "version": "4.0.4",
-      "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.4.tgz",
-      "integrity": "sha512-t+b0SS+IxG7Rxzda2EVvyBZbvFPBCjJoyHuE0P//7OAsN23GItzDRdWa6ALxZI/8R5ygK7jAR6t028/z+7295g==",
+    "style-to-object": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-0.3.0.tgz",
+      "integrity": "sha512-CzFnRRXhzWIdItT3OmF8SQfWyahHhjq3HwcMNCNLn+N7klOOqPjMeG/4JSu77D7ypZdGvSzvkrbyeTMizz2VrA==",
       "dev": true,
       "requires": {
-        "@types/component-emitter": "^1.2.10",
-        "component-emitter": "~1.3.0",
-        "debug": "~4.3.1"
+        "inline-style-parser": "0.1.1"
       }
     },
-    "sockjs": {
-      "version": "0.3.21",
-      "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.21.tgz",
-      "integrity": "sha512-DhbPFGpxjc6Z3I+uX07Id5ZO2XwYsWOrYjaSeieES78cq+JaJvVe5q/m1uvjIQhXinhIeCFRH6JgXe+mvVMyXw==",
+    "stylehacks": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.0.tgz",
+      "integrity": "sha512-SzLmvHQTrIWfSgljkQCw2++C9+Ne91d/6Sp92I8c5uHTcy/PgeHamwITIbBW9wnFTY/3ZfSXR9HIL6Ikqmcu6Q==",
       "dev": true,
       "requires": {
-        "faye-websocket": "^0.11.3",
-        "uuid": "^3.4.0",
-        "websocket-driver": "^0.7.4"
+        "browserslist": "^4.16.6",
+        "postcss-selector-parser": "^6.0.4"
       }
     },
-    "sockjs-client": {
-      "version": "1.5.2",
-      "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.5.2.tgz",
-      "integrity": "sha512-ZzRxPBISQE7RpzlH4tKJMQbHM9pabHluk0WBaxAQ+wm/UieeBVBou0p4wVnSQGN9QmpAZygQ0cDIypWuqOFmFQ==",
+    "stylus": {
+      "version": "0.54.8",
+      "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.54.8.tgz",
+      "integrity": "sha512-vr54Or4BZ7pJafo2mpf0ZcwA74rpuYCZbxrHBsH8kbcXOwSfvBFwsRfpGO5OD5fhG5HDCFW737PKaawI7OqEAg==",
       "dev": true,
       "requires": {
-        "debug": "^3.2.6",
-        "eventsource": "^1.0.7",
-        "faye-websocket": "^0.11.3",
-        "inherits": "^2.0.4",
-        "json3": "^3.3.3",
-        "url-parse": "^1.5.3"
+        "css-parse": "~2.0.0",
+        "debug": "~3.1.0",
+        "glob": "^7.1.6",
+        "mkdirp": "~1.0.4",
+        "safer-buffer": "^2.1.2",
+        "sax": "~1.2.4",
+        "semver": "^6.3.0",
+        "source-map": "^0.7.3"
       },
       "dependencies": {
         "debug": {
-          "version": "3.2.7",
-          "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
-          "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+          "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
           "dev": true,
           "requires": {
-            "ms": "^2.1.1"
+            "ms": "2.0.0"
           }
+        },
+        "ms": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+          "dev": true
+        },
+        "semver": {
+          "version": "6.3.0",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+          "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+          "dev": true
         }
       }
     },
-    "socks": {
-      "version": "2.6.1",
-      "resolved": "https://registry.npmjs.org/socks/-/socks-2.6.1.tgz",
-      "integrity": "sha512-kLQ9N5ucj8uIcxrDwjm0Jsqk06xdpBjGNQtpXy4Q8/QY2k+fY7nZH8CARy+hkbG+SGAovmzzuauCpBlb8FrnBA==",
+    "stylus-loader": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/stylus-loader/-/stylus-loader-6.1.0.tgz",
+      "integrity": "sha512-qKO34QCsOtSJrXxQQmXsPeaVHh6hMumBAFIoJTcsSr2VzrA6o/CW9HCGR8spCjzJhN8oKQHdj/Ytx0wwXyElkw==",
       "dev": true,
       "requires": {
-        "ip": "^1.1.5",
-        "smart-buffer": "^4.1.0"
+        "fast-glob": "^3.2.5",
+        "klona": "^2.0.4",
+        "normalize-path": "^3.0.0"
       }
     },
-    "socks-proxy-agent": {
-      "version": "6.1.1",
-      "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.1.1.tgz",
-      "integrity": "sha512-t8J0kG3csjA4g6FTbsMOWws+7R7vuRC8aQ/wy3/1OWmsgwA68zs/+cExQ0koSitUDXqhufF/YJr9wtNMZHw5Ew==",
+    "supports-color": {
+      "version": "5.5.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+      "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
       "dev": true,
       "requires": {
-        "agent-base": "^6.0.2",
-        "debug": "^4.3.1",
-        "socks": "^2.6.1"
+        "has-flag": "^3.0.0"
       }
     },
-    "source-list-map": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz",
-      "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==",
+    "supports-preserve-symlinks-flag": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+      "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
       "dev": true
     },
-    "source-map": {
-      "version": "0.7.3",
-      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
-      "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
+    "svgo": {
+      "version": "2.8.0",
+      "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz",
+      "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==",
+      "dev": true,
+      "requires": {
+        "@trysound/sax": "0.2.0",
+        "commander": "^7.2.0",
+        "css-select": "^4.1.3",
+        "css-tree": "^1.1.3",
+        "csso": "^4.2.0",
+        "picocolors": "^1.0.0",
+        "stable": "^0.1.8"
+      },
+      "dependencies": {
+        "commander": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
+          "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
+          "dev": true
+        }
+      }
+    },
+    "symbol-observable": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz",
+      "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==",
       "dev": true
     },
-    "source-map-js": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.1.tgz",
-      "integrity": "sha512-4+TN2b3tqOCd/kaGRJ/sTYA0tR0mdXx26ipdolxcwtJVqEnqNYvlCAt1q3ypy4QMlYus+Zh34RNtYLoq2oQ4IA=="
+    "symbol.prototype.description": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/symbol.prototype.description/-/symbol.prototype.description-1.0.5.tgz",
+      "integrity": "sha512-x738iXRYsrAt9WBhRCVG5BtIC3B7CUkFwbHW2zOvGtwM33s7JjrCDyq8V0zgMYVb5ymsL8+qkzzpANH63CPQaQ==",
+      "dev": true,
+      "requires": {
+        "call-bind": "^1.0.2",
+        "get-symbol-description": "^1.0.0",
+        "has-symbols": "^1.0.2",
+        "object.getownpropertydescriptors": "^2.1.2"
+      }
     },
-    "source-map-loader": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-3.0.0.tgz",
-      "integrity": "sha512-GKGWqWvYr04M7tn8dryIWvb0s8YM41z82iQv01yBtIylgxax0CwvSy6gc2Y02iuXwEfGWRlMicH0nvms9UZphw==",
+    "synchronous-promise": {
+      "version": "2.0.15",
+      "resolved": "https://registry.npmjs.org/synchronous-promise/-/synchronous-promise-2.0.15.tgz",
+      "integrity": "sha512-k8uzYIkIVwmT+TcglpdN50pS2y1BDcUnBPK9iJeGu0Pl1lOI8pD6wtzgw91Pjpe+RxtTncw32tLxs/R0yNL2Mg==",
+      "dev": true
+    },
+    "table": {
+      "version": "6.8.0",
+      "resolved": "https://registry.npmjs.org/table/-/table-6.8.0.tgz",
+      "integrity": "sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA==",
       "dev": true,
       "requires": {
-        "abab": "^2.0.5",
-        "iconv-lite": "^0.6.2",
-        "source-map-js": "^0.6.2"
+        "ajv": "^8.0.1",
+        "lodash.truncate": "^4.4.2",
+        "slice-ansi": "^4.0.0",
+        "string-width": "^4.2.3",
+        "strip-ansi": "^6.0.1"
       },
       "dependencies": {
-        "iconv-lite": {
-          "version": "0.6.3",
-          "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
-          "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+        "ajv": {
+          "version": "8.11.0",
+          "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz",
+          "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==",
           "dev": true,
           "requires": {
-            "safer-buffer": ">= 2.1.2 < 3.0.0"
+            "fast-deep-equal": "^3.1.1",
+            "json-schema-traverse": "^1.0.0",
+            "require-from-string": "^2.0.2",
+            "uri-js": "^4.2.2"
           }
         },
-        "source-map-js": {
-          "version": "0.6.2",
-          "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-0.6.2.tgz",
-          "integrity": "sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==",
+        "json-schema-traverse": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+          "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
           "dev": true
         }
       }
     },
-    "source-map-resolve": {
-      "version": "0.5.3",
-      "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz",
-      "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==",
+    "tapable": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
+      "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
+      "dev": true
+    },
+    "tar": {
+      "version": "6.1.11",
+      "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz",
+      "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==",
       "dev": true,
       "requires": {
-        "atob": "^2.1.2",
-        "decode-uri-component": "^0.2.0",
-        "resolve-url": "^0.2.1",
-        "source-map-url": "^0.4.0",
-        "urix": "^0.1.0"
+        "chownr": "^2.0.0",
+        "fs-minipass": "^2.0.0",
+        "minipass": "^3.0.0",
+        "minizlib": "^2.1.1",
+        "mkdirp": "^1.0.3",
+        "yallist": "^4.0.0"
       }
     },
-    "source-map-support": {
-      "version": "0.5.19",
-      "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz",
-      "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==",
+    "telejson": {
+      "version": "6.0.8",
+      "resolved": "https://registry.npmjs.org/telejson/-/telejson-6.0.8.tgz",
+      "integrity": "sha512-nerNXi+j8NK1QEfBHtZUN/aLdDcyupA//9kAboYLrtzZlPLpUfqbVGWb9zz91f/mIjRbAYhbgtnJHY8I1b5MBg==",
       "dev": true,
       "requires": {
-        "buffer-from": "^1.0.0",
-        "source-map": "^0.6.0"
+        "@types/is-function": "^1.0.0",
+        "global": "^4.4.0",
+        "is-function": "^1.0.2",
+        "is-regex": "^1.1.2",
+        "is-symbol": "^1.0.3",
+        "isobject": "^4.0.0",
+        "lodash": "^4.17.21",
+        "memoizerific": "^1.11.3"
+      },
+      "dependencies": {
+        "isobject": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/isobject/-/isobject-4.0.0.tgz",
+          "integrity": "sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA==",
+          "dev": true
+        }
+      }
+    },
+    "terser": {
+      "version": "5.7.1",
+      "resolved": "https://registry.npmjs.org/terser/-/terser-5.7.1.tgz",
+      "integrity": "sha512-b3e+d5JbHAe/JSjwsC3Zn55wsBIM7AsHLjKxT31kGCldgbpFePaFo+PiddtO6uwRZWRw7sPXmAN8dTW61xmnSg==",
+      "dev": true,
+      "requires": {
+        "commander": "^2.20.0",
+        "source-map": "~0.7.2",
+        "source-map-support": "~0.5.19"
+      },
+      "dependencies": {
+        "commander": {
+          "version": "2.20.3",
+          "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+          "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+          "dev": true
+        }
+      }
+    },
+    "terser-webpack-plugin": {
+      "version": "5.1.4",
+      "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.1.4.tgz",
+      "integrity": "sha512-C2WkFwstHDhVEmsmlCxrXUtVklS+Ir1A7twrYzrDrQQOIMOaVAYykaoo/Aq1K0QRkMoY2hhvDQY1cm4jnIMFwA==",
+      "dev": true,
+      "requires": {
+        "jest-worker": "^27.0.2",
+        "p-limit": "^3.1.0",
+        "schema-utils": "^3.0.0",
+        "serialize-javascript": "^6.0.0",
+        "source-map": "^0.6.1",
+        "terser": "^5.7.0"
       },
       "dependencies": {
+        "p-limit": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+          "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+          "dev": true,
+          "requires": {
+            "yocto-queue": "^0.1.0"
+          }
+        },
         "source-map": {
           "version": "0.6.1",
           "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
@@ -11252,863 +23691,1207 @@
         }
       }
     },
-    "source-map-url": {
-      "version": "0.4.1",
-      "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz",
-      "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==",
+    "test-exclude": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz",
+      "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==",
+      "dev": true,
+      "requires": {
+        "@istanbuljs/schema": "^0.1.2",
+        "glob": "^7.1.4",
+        "minimatch": "^3.0.4"
+      }
+    },
+    "text-table": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+      "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
+      "dev": true
+    },
+    "through": {
+      "version": "2.3.8",
+      "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
+      "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
+      "dev": true
+    },
+    "through2": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
+      "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
+      "dev": true,
+      "requires": {
+        "readable-stream": "~2.3.6",
+        "xtend": "~4.0.1"
+      }
+    },
+    "thunky": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz",
+      "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==",
+      "dev": true
+    },
+    "time-stamp": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz",
+      "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=",
       "dev": true
     },
-    "sourcemap-codec": {
-      "version": "1.4.8",
-      "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
-      "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
+    "timers-browserify": {
+      "version": "2.0.12",
+      "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz",
+      "integrity": "sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==",
+      "dev": true,
+      "requires": {
+        "setimmediate": "^1.0.4"
+      }
+    },
+    "tiny-inflate": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz",
+      "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==",
       "dev": true
     },
-    "spdy": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz",
-      "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==",
+    "tmp": {
+      "version": "0.0.33",
+      "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
+      "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
       "dev": true,
       "requires": {
-        "debug": "^4.1.0",
-        "handle-thing": "^2.0.0",
-        "http-deceiver": "^1.2.7",
-        "select-hose": "^2.0.0",
-        "spdy-transport": "^3.0.0"
+        "os-tmpdir": "~1.0.2"
       }
     },
-    "spdy-transport": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz",
-      "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==",
+    "tmpl": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz",
+      "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==",
+      "dev": true
+    },
+    "to-arraybuffer": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz",
+      "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=",
+      "dev": true
+    },
+    "to-fast-properties": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
+      "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=",
+      "dev": true
+    },
+    "to-object-path": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz",
+      "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=",
       "dev": true,
       "requires": {
-        "debug": "^4.1.0",
-        "detect-node": "^2.0.4",
-        "hpack.js": "^2.1.6",
-        "obuf": "^1.1.2",
-        "readable-stream": "^3.0.6",
-        "wbuf": "^1.7.3"
+        "kind-of": "^3.0.2"
       },
       "dependencies": {
-        "readable-stream": {
-          "version": "3.6.0",
-          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
-          "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
           "dev": true,
           "requires": {
-            "inherits": "^2.0.3",
-            "string_decoder": "^1.1.1",
-            "util-deprecate": "^1.0.1"
+            "is-buffer": "^1.1.5"
           }
         }
       }
     },
-    "split-string": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
-      "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==",
+    "to-regex": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz",
+      "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==",
       "dev": true,
       "requires": {
-        "extend-shallow": "^3.0.0"
+        "define-property": "^2.0.2",
+        "extend-shallow": "^3.0.2",
+        "regex-not": "^1.0.2",
+        "safe-regex": "^1.1.0"
       }
     },
-    "sprintf-js": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
-      "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
-      "dev": true
-    },
-    "sshpk": {
-      "version": "1.16.1",
-      "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz",
-      "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==",
+    "to-regex-range": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+      "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
       "dev": true,
       "requires": {
-        "asn1": "~0.2.3",
-        "assert-plus": "^1.0.0",
-        "bcrypt-pbkdf": "^1.0.0",
-        "dashdash": "^1.12.0",
-        "ecc-jsbn": "~0.1.1",
-        "getpass": "^0.1.1",
-        "jsbn": "~0.1.0",
-        "safer-buffer": "^2.0.2",
-        "tweetnacl": "~0.14.0"
+        "is-number": "^7.0.0"
       }
     },
-    "ssri": {
-      "version": "8.0.1",
-      "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz",
-      "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==",
+    "toidentifier": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
+      "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
+      "dev": true
+    },
+    "tr46": {
+      "version": "0.0.3",
+      "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+      "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=",
+      "dev": true
+    },
+    "traverse": {
+      "version": "0.6.6",
+      "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.6.tgz",
+      "integrity": "sha1-y99WD9e5r2MlAv7UD5GMFX6pcTc=",
+      "dev": true
+    },
+    "tree-kill": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
+      "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==",
+      "dev": true
+    },
+    "trim": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/trim/-/trim-0.0.1.tgz",
+      "integrity": "sha1-WFhUf2spB1fulczMZm+1AITEYN0=",
+      "dev": true
+    },
+    "trim-newlines": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz",
+      "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=",
       "dev": true,
-      "requires": {
-        "minipass": "^3.1.1"
-      }
+      "optional": true
     },
-    "stable": {
-      "version": "0.1.8",
-      "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz",
-      "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==",
+    "trim-trailing-lines": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/trim-trailing-lines/-/trim-trailing-lines-1.1.4.tgz",
+      "integrity": "sha512-rjUWSqnfTNrjbB9NQWfPMH/xRK1deHeGsHoVfpxJ++XeYXE0d6B1En37AHfw3jtfTU7dzMzZL2jjpe8Qb5gLIQ==",
       "dev": true
     },
-    "static-extend": {
-      "version": "0.1.2",
-      "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
-      "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=",
+    "trough": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/trough/-/trough-1.0.5.tgz",
+      "integrity": "sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==",
+      "dev": true
+    },
+    "ts-dedent": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz",
+      "integrity": "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==",
+      "dev": true
+    },
+    "ts-loader": {
+      "version": "8.4.0",
+      "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-8.4.0.tgz",
+      "integrity": "sha512-6nFY3IZ2//mrPc+ImY3hNWx1vCHyEhl6V+wLmL4CZcm6g1CqX7UKrkc6y0i4FwcfOhxyMPCfaEvh20f4r9GNpw==",
       "dev": true,
       "requires": {
-        "define-property": "^0.2.5",
-        "object-copy": "^0.1.0"
+        "chalk": "^4.1.0",
+        "enhanced-resolve": "^4.0.0",
+        "loader-utils": "^2.0.0",
+        "micromatch": "^4.0.0",
+        "semver": "^7.3.4"
       },
       "dependencies": {
-        "define-property": {
-          "version": "0.2.5",
-          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
-          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+        "ansi-styles": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
           "dev": true,
           "requires": {
-            "is-descriptor": "^0.1.0"
+            "color-convert": "^2.0.1"
+          }
+        },
+        "chalk": {
+          "version": "4.1.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+          "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "dev": true,
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
+        },
+        "enhanced-resolve": {
+          "version": "4.5.0",
+          "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz",
+          "integrity": "sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg==",
+          "dev": true,
+          "requires": {
+            "graceful-fs": "^4.1.2",
+            "memory-fs": "^0.5.0",
+            "tapable": "^1.0.0"
+          }
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
+        "memory-fs": {
+          "version": "0.5.0",
+          "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz",
+          "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==",
+          "dev": true,
+          "requires": {
+            "errno": "^0.1.3",
+            "readable-stream": "^2.0.1"
+          }
+        },
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
           }
+        },
+        "tapable": {
+          "version": "1.1.3",
+          "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",
+          "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==",
+          "dev": true
         }
       }
     },
-    "statuses": {
-      "version": "1.5.0",
-      "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
-      "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=",
+    "ts-morph": {
+      "version": "13.0.3",
+      "resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-13.0.3.tgz",
+      "integrity": "sha512-pSOfUMx8Ld/WUreoSzvMFQG5i9uEiWIsBYjpU9+TTASOeUa89j5HykomeqVULm1oqWtBdleI3KEFRLrlA3zGIw==",
+      "dev": true,
+      "requires": {
+        "@ts-morph/common": "~0.12.3",
+        "code-block-writer": "^11.0.0"
+      }
+    },
+    "ts-pnp": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/ts-pnp/-/ts-pnp-1.2.0.tgz",
+      "integrity": "sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw==",
       "dev": true
     },
-    "streamroller": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.0.2.tgz",
-      "integrity": "sha512-ur6y5S5dopOaRXBuRIZ1u6GC5bcEXHRZKgfBjfCglMhmIf+roVCECjvkEYzNQOXIN2/JPnkMPW/8B3CZoKaEPA==",
+    "tsconfig-paths": {
+      "version": "3.14.1",
+      "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz",
+      "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==",
       "dev": true,
       "requires": {
-        "date-format": "^4.0.3",
-        "debug": "^4.1.1",
-        "fs-extra": "^10.0.0"
+        "@types/json5": "^0.0.29",
+        "json5": "^1.0.1",
+        "minimist": "^1.2.6",
+        "strip-bom": "^3.0.0"
+      },
+      "dependencies": {
+        "json5": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
+          "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
+          "dev": true,
+          "requires": {
+            "minimist": "^1.2.0"
+          }
+        },
+        "strip-bom": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+          "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
+          "dev": true
+        }
       }
     },
-    "string-width": {
-      "version": "4.2.3",
-      "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
-      "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+    "tsconfig-paths-webpack-plugin": {
+      "version": "3.5.2",
+      "resolved": "https://registry.npmjs.org/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-3.5.2.tgz",
+      "integrity": "sha512-EhnfjHbzm5IYI9YPNVIxx1moxMI4bpHD2e0zTXeDNQcwjjRaGepP7IhTHJkyDBG0CAOoxRfe7jCG630Ou+C6Pw==",
+      "dev": true,
       "requires": {
-        "emoji-regex": "^8.0.0",
-        "is-fullwidth-code-point": "^3.0.0",
-        "strip-ansi": "^6.0.1"
+        "chalk": "^4.1.0",
+        "enhanced-resolve": "^5.7.0",
+        "tsconfig-paths": "^3.9.0"
       },
       "dependencies": {
-        "ansi-regex": {
-          "version": "5.0.1",
-          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
-          "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
+        "ansi-styles": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+          "dev": true,
+          "requires": {
+            "color-convert": "^2.0.1"
+          }
+        },
+        "chalk": {
+          "version": "4.1.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+          "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "dev": true,
+          "requires": {
+            "color-name": "~1.1.4"
+          }
         },
-        "emoji-regex": {
-          "version": "8.0.0",
-          "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
-          "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
         },
-        "is-fullwidth-code-point": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
-          "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
         },
-        "strip-ansi": {
-          "version": "6.0.1",
-          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
-          "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+          "dev": true,
           "requires": {
-            "ansi-regex": "^5.0.1"
+            "has-flag": "^4.0.0"
           }
         }
       }
     },
-    "string_decoder": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
-      "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
-      "requires": {
-        "safe-buffer": "~5.1.0"
-      }
+    "tslib": {
+      "version": "2.4.0",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
+      "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
     },
-    "strip-ansi": {
-      "version": "5.2.0",
-      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
-      "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+    "tsutils": {
+      "version": "3.21.0",
+      "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz",
+      "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==",
       "dev": true,
       "requires": {
-        "ansi-regex": "^4.1.0"
+        "tslib": "^1.8.1"
+      },
+      "dependencies": {
+        "tslib": {
+          "version": "1.14.1",
+          "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+          "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+          "dev": true
+        }
       }
     },
-    "strip-eof": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
-      "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=",
-      "dev": true
-    },
-    "strip-json-comments": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
-      "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+    "tty-browserify": {
+      "version": "0.0.0",
+      "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz",
+      "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=",
       "dev": true
     },
-    "style-loader": {
-      "version": "3.2.1",
-      "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.2.1.tgz",
-      "integrity": "sha512-1k9ZosJCRFaRbY6hH49JFlRB0fVSbmnyq1iTPjNxUmGVjBNEmwrrHPenhlp+Lgo51BojHSf6pl2FcqYaN3PfVg==",
+    "type": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
+      "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==",
       "dev": true
     },
-    "stylehacks": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.0.1.tgz",
-      "integrity": "sha512-Es0rVnHIqbWzveU1b24kbw92HsebBepxfcqe5iix7t9j0PQqhs0IxXVXv0pY2Bxa08CgMkzD6OWql7kbGOuEdA==",
+    "type-check": {
+      "version": "0.3.2",
+      "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
+      "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=",
       "dev": true,
       "requires": {
-        "browserslist": "^4.16.0",
-        "postcss-selector-parser": "^6.0.4"
+        "prelude-ls": "~1.1.2"
       }
     },
-    "stylus": {
-      "version": "0.54.8",
-      "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.54.8.tgz",
-      "integrity": "sha512-vr54Or4BZ7pJafo2mpf0ZcwA74rpuYCZbxrHBsH8kbcXOwSfvBFwsRfpGO5OD5fhG5HDCFW737PKaawI7OqEAg==",
+    "type-fest": {
+      "version": "0.21.3",
+      "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
+      "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
+      "dev": true
+    },
+    "type-is": {
+      "version": "1.6.18",
+      "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
+      "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
       "dev": true,
       "requires": {
-        "css-parse": "~2.0.0",
-        "debug": "~3.1.0",
-        "glob": "^7.1.6",
-        "mkdirp": "~1.0.4",
-        "safer-buffer": "^2.1.2",
-        "sax": "~1.2.4",
-        "semver": "^6.3.0",
-        "source-map": "^0.7.3"
-      },
-      "dependencies": {
-        "debug": {
-          "version": "3.1.0",
-          "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
-          "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
-          "dev": true,
-          "requires": {
-            "ms": "2.0.0"
-          }
-        },
-        "ms": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
-          "dev": true
-        },
-        "semver": {
-          "version": "6.3.0",
-          "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
-          "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
-          "dev": true
-        }
+        "media-typer": "0.3.0",
+        "mime-types": "~2.1.24"
       }
     },
-    "stylus-loader": {
-      "version": "6.1.0",
-      "resolved": "https://registry.npmjs.org/stylus-loader/-/stylus-loader-6.1.0.tgz",
-      "integrity": "sha512-qKO34QCsOtSJrXxQQmXsPeaVHh6hMumBAFIoJTcsSr2VzrA6o/CW9HCGR8spCjzJhN8oKQHdj/Ytx0wwXyElkw==",
+    "typedarray": {
+      "version": "0.0.6",
+      "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
+      "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
+      "dev": true
+    },
+    "typedarray-to-buffer": {
+      "version": "3.1.5",
+      "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz",
+      "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==",
       "dev": true,
       "requires": {
-        "fast-glob": "^3.2.5",
-        "klona": "^2.0.4",
-        "normalize-path": "^3.0.0"
+        "is-typedarray": "^1.0.0"
       }
     },
-    "supports-color": {
-      "version": "5.5.0",
-      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
-      "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+    "typescript": {
+      "version": "4.3.5",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz",
+      "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==",
+      "dev": true
+    },
+    "ua-parser-js": {
+      "version": "0.7.31",
+      "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.31.tgz",
+      "integrity": "sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ==",
+      "dev": true
+    },
+    "uglify-js": {
+      "version": "3.15.5",
+      "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.15.5.tgz",
+      "integrity": "sha512-hNM5q5GbBRB5xB+PMqVRcgYe4c8jbyZ1pzZhS6jbq54/4F2gFK869ZheiE5A8/t+W5jtTNpWef/5Q9zk639FNQ==",
+      "dev": true,
+      "optional": true
+    },
+    "unbox-primitive": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz",
+      "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==",
       "dev": true,
       "requires": {
-        "has-flag": "^3.0.0"
+        "call-bind": "^1.0.2",
+        "has-bigints": "^1.0.2",
+        "has-symbols": "^1.0.3",
+        "which-boxed-primitive": "^1.0.2"
       }
     },
-    "svgo": {
-      "version": "2.8.0",
-      "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz",
-      "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==",
+    "unfetch": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/unfetch/-/unfetch-4.2.0.tgz",
+      "integrity": "sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==",
+      "dev": true
+    },
+    "unherit": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/unherit/-/unherit-1.1.3.tgz",
+      "integrity": "sha512-Ft16BJcnapDKp0+J/rqFC3Rrk6Y/Ng4nzsC028k2jdDII/rdZ7Wd3pPT/6+vIIxRagwRc9K0IUX0Ra4fKvw+WQ==",
       "dev": true,
       "requires": {
-        "@trysound/sax": "0.2.0",
-        "commander": "^7.2.0",
-        "css-select": "^4.1.3",
-        "css-tree": "^1.1.3",
-        "csso": "^4.2.0",
-        "picocolors": "^1.0.0",
-        "stable": "^0.1.8"
+        "inherits": "^2.0.0",
+        "xtend": "^4.0.0"
       }
     },
-    "symbol-observable": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz",
-      "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==",
+    "unicode-canonical-property-names-ecmascript": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz",
+      "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==",
       "dev": true
     },
-    "table": {
-      "version": "6.7.3",
-      "resolved": "https://registry.npmjs.org/table/-/table-6.7.3.tgz",
-      "integrity": "sha512-5DkIxeA7XERBqMwJq0aHZOdMadBx4e6eDoFRuyT5VR82J0Ycg2DwM6GfA/EQAhJ+toRTaS1lIdSQCqgrmhPnlw==",
+    "unicode-match-property-ecmascript": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz",
+      "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==",
       "dev": true,
       "requires": {
-        "ajv": "^8.0.1",
-        "lodash.truncate": "^4.4.2",
-        "slice-ansi": "^4.0.0",
-        "string-width": "^4.2.3",
-        "strip-ansi": "^6.0.1"
-      },
-      "dependencies": {
-        "ajv": {
-          "version": "8.8.2",
-          "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.8.2.tgz",
-          "integrity": "sha512-x9VuX+R/jcFj1DHo/fCp99esgGDWiHENrKxaCENuCxpoMCmAt/COCGVDwA7kleEpEzJjDnvh3yGoOuLu0Dtllw==",
-          "dev": true,
-          "requires": {
-            "fast-deep-equal": "^3.1.1",
-            "json-schema-traverse": "^1.0.0",
-            "require-from-string": "^2.0.2",
-            "uri-js": "^4.2.2"
-          }
-        },
-        "ansi-regex": {
-          "version": "5.0.1",
-          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
-          "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
-          "dev": true
-        },
-        "emoji-regex": {
-          "version": "8.0.0",
-          "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
-          "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
-          "dev": true
-        },
-        "is-fullwidth-code-point": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
-          "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
-          "dev": true
-        },
-        "json-schema-traverse": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
-          "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
-          "dev": true
-        },
-        "string-width": {
-          "version": "4.2.3",
-          "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
-          "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
-          "dev": true,
-          "requires": {
-            "emoji-regex": "^8.0.0",
-            "is-fullwidth-code-point": "^3.0.0",
-            "strip-ansi": "^6.0.1"
-          }
-        },
-        "strip-ansi": {
-          "version": "6.0.1",
-          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
-          "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
-          "dev": true,
-          "requires": {
-            "ansi-regex": "^5.0.1"
-          }
-        }
+        "unicode-canonical-property-names-ecmascript": "^2.0.0",
+        "unicode-property-aliases-ecmascript": "^2.0.0"
       }
     },
-    "tapable": {
-      "version": "2.2.1",
-      "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
-      "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
+    "unicode-match-property-value-ecmascript": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz",
+      "integrity": "sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw==",
       "dev": true
     },
-    "tar": {
-      "version": "6.1.11",
-      "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz",
-      "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==",
+    "unicode-properties": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/unicode-properties/-/unicode-properties-1.3.1.tgz",
+      "integrity": "sha512-nIV3Tf3LcUEZttY/2g4ZJtGXhWwSkuLL+rCu0DIAMbjyVPj+8j5gNVz4T/sVbnQybIsd5SFGkPKg/756OY6jlA==",
       "dev": true,
       "requires": {
-        "chownr": "^2.0.0",
-        "fs-minipass": "^2.0.0",
-        "minipass": "^3.0.0",
-        "minizlib": "^2.1.1",
-        "mkdirp": "^1.0.3",
-        "yallist": "^4.0.0"
+        "base64-js": "^1.3.0",
+        "unicode-trie": "^2.0.0"
       }
     },
-    "terser": {
-      "version": "5.7.1",
-      "resolved": "https://registry.npmjs.org/terser/-/terser-5.7.1.tgz",
-      "integrity": "sha512-b3e+d5JbHAe/JSjwsC3Zn55wsBIM7AsHLjKxT31kGCldgbpFePaFo+PiddtO6uwRZWRw7sPXmAN8dTW61xmnSg==",
+    "unicode-property-aliases-ecmascript": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz",
+      "integrity": "sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ==",
+      "dev": true
+    },
+    "unicode-trie": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-2.0.0.tgz",
+      "integrity": "sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==",
       "dev": true,
       "requires": {
-        "commander": "^2.20.0",
-        "source-map": "~0.7.2",
-        "source-map-support": "~0.5.19"
+        "pako": "^0.2.5",
+        "tiny-inflate": "^1.0.0"
       },
       "dependencies": {
-        "commander": {
-          "version": "2.20.3",
-          "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
-          "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+        "pako": {
+          "version": "0.2.9",
+          "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz",
+          "integrity": "sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU=",
           "dev": true
         }
       }
     },
-    "terser-webpack-plugin": {
-      "version": "5.1.4",
-      "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.1.4.tgz",
-      "integrity": "sha512-C2WkFwstHDhVEmsmlCxrXUtVklS+Ir1A7twrYzrDrQQOIMOaVAYykaoo/Aq1K0QRkMoY2hhvDQY1cm4jnIMFwA==",
+    "unified": {
+      "version": "9.2.0",
+      "resolved": "https://registry.npmjs.org/unified/-/unified-9.2.0.tgz",
+      "integrity": "sha512-vx2Z0vY+a3YoTj8+pttM3tiJHCwY5UFbYdiWrwBEbHmK8pvsPj2rtAX2BFfgXen8T39CJWblWRDT4L5WGXtDdg==",
       "dev": true,
       "requires": {
-        "jest-worker": "^27.0.2",
-        "p-limit": "^3.1.0",
-        "schema-utils": "^3.0.0",
-        "serialize-javascript": "^6.0.0",
-        "source-map": "^0.6.1",
-        "terser": "^5.7.0"
+        "bail": "^1.0.0",
+        "extend": "^3.0.0",
+        "is-buffer": "^2.0.0",
+        "is-plain-obj": "^2.0.0",
+        "trough": "^1.0.0",
+        "vfile": "^4.0.0"
       },
       "dependencies": {
-        "p-limit": {
-          "version": "3.1.0",
-          "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
-          "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
-          "dev": true,
-          "requires": {
-            "yocto-queue": "^0.1.0"
-          }
-        },
-        "source-map": {
-          "version": "0.6.1",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+        "is-buffer": {
+          "version": "2.0.5",
+          "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz",
+          "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==",
           "dev": true
         }
       }
     },
-    "text-table": {
-      "version": "0.2.0",
-      "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
-      "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
+    "union-value": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz",
+      "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==",
+      "dev": true,
+      "requires": {
+        "arr-union": "^3.1.0",
+        "get-value": "^2.0.6",
+        "is-extendable": "^0.1.1",
+        "set-value": "^2.0.1"
+      }
+    },
+    "uniq": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz",
+      "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=",
       "dev": true
     },
-    "through": {
-      "version": "2.3.8",
-      "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
-      "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
+    "unique-filename": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz",
+      "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==",
+      "dev": true,
+      "requires": {
+        "unique-slug": "^2.0.0"
+      }
+    },
+    "unique-slug": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz",
+      "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==",
+      "dev": true,
+      "requires": {
+        "imurmurhash": "^0.1.4"
+      }
+    },
+    "unist-builder": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/unist-builder/-/unist-builder-2.0.3.tgz",
+      "integrity": "sha512-f98yt5pnlMWlzP539tPc4grGMsFaQQlP/vM396b00jngsiINumNmsY8rkXjfoi1c6QaM8nQ3vaGDuoKWbe/1Uw==",
       "dev": true
     },
-    "thunky": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz",
-      "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==",
+    "unist-util-generated": {
+      "version": "1.1.6",
+      "resolved": "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-1.1.6.tgz",
+      "integrity": "sha512-cln2Mm1/CZzN5ttGK7vkoGw+RZ8VcUH6BtGbq98DDtRGquAAOXig1mrBQYelOwMXYS8rK+vZDyyojSjp7JX+Lg==",
       "dev": true
     },
-    "timsort": {
-      "version": "0.3.0",
-      "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz",
-      "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=",
+    "unist-util-is": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.1.0.tgz",
+      "integrity": "sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg==",
       "dev": true
     },
-    "tmp": {
-      "version": "0.0.33",
-      "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
-      "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
+    "unist-util-position": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-3.1.0.tgz",
+      "integrity": "sha512-w+PkwCbYSFw8vpgWD0v7zRCl1FpY3fjDSQ3/N/wNd9Ffa4gPi8+4keqt99N3XW6F99t/mUzp2xAhNmfKWp95QA==",
+      "dev": true
+    },
+    "unist-util-remove": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/unist-util-remove/-/unist-util-remove-2.1.0.tgz",
+      "integrity": "sha512-J8NYPyBm4baYLdCbjmf1bhPu45Cr1MWTm77qd9istEkzWpnN6O9tMsEbB2JhNnBCqGENRqEWomQ+He6au0B27Q==",
       "dev": true,
       "requires": {
-        "os-tmpdir": "~1.0.2"
+        "unist-util-is": "^4.0.0"
       }
     },
-    "to-fast-properties": {
+    "unist-util-remove-position": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-2.0.1.tgz",
+      "integrity": "sha512-fDZsLYIe2uT+oGFnuZmy73K6ZxOPG/Qcm+w7jbEjaFcJgbQ6cqjs/eSPzXhsmGpAsWPkqZM9pYjww5QTn3LHMA==",
+      "dev": true,
+      "requires": {
+        "unist-util-visit": "^2.0.0"
+      }
+    },
+    "unist-util-stringify-position": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz",
+      "integrity": "sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==",
+      "dev": true,
+      "requires": {
+        "@types/unist": "^2.0.2"
+      }
+    },
+    "unist-util-visit": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-2.0.3.tgz",
+      "integrity": "sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q==",
+      "dev": true,
+      "requires": {
+        "@types/unist": "^2.0.0",
+        "unist-util-is": "^4.0.0",
+        "unist-util-visit-parents": "^3.0.0"
+      }
+    },
+    "unist-util-visit-parents": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz",
+      "integrity": "sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg==",
+      "dev": true,
+      "requires": {
+        "@types/unist": "^2.0.0",
+        "unist-util-is": "^4.0.0"
+      }
+    },
+    "universalify": {
       "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
-      "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=",
+      "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
+      "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==",
       "dev": true
     },
-    "to-object-path": {
-      "version": "0.3.0",
-      "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz",
-      "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=",
+    "unix-crypt-td-js": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/unix-crypt-td-js/-/unix-crypt-td-js-1.1.4.tgz",
+      "integrity": "sha512-8rMeVYWSIyccIJscb9NdCfZKSRBKYTeVnwmiRYT2ulE3qd1RaDQ0xQDP+rI3ccIWbhu/zuo5cgN8z73belNZgw==",
+      "dev": true
+    },
+    "unpipe": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+      "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=",
+      "dev": true
+    },
+    "unset-value": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz",
+      "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=",
       "dev": true,
       "requires": {
-        "kind-of": "^3.0.2"
+        "has-value": "^0.3.1",
+        "isobject": "^3.0.0"
       },
       "dependencies": {
-        "kind-of": {
-          "version": "3.2.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+        "has-value": {
+          "version": "0.3.1",
+          "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz",
+          "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=",
           "dev": true,
           "requires": {
-            "is-buffer": "^1.1.5"
+            "get-value": "^2.0.3",
+            "has-values": "^0.1.4",
+            "isobject": "^2.0.0"
+          },
+          "dependencies": {
+            "isobject": {
+              "version": "2.1.0",
+              "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
+              "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=",
+              "dev": true,
+              "requires": {
+                "isarray": "1.0.0"
+              }
+            }
           }
+        },
+        "has-values": {
+          "version": "0.1.4",
+          "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz",
+          "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=",
+          "dev": true
         }
       }
     },
-    "to-regex": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz",
-      "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==",
+    "untildify": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/untildify/-/untildify-2.1.0.tgz",
+      "integrity": "sha1-F+soB5h/dpUunASF/DEdBqgmouA=",
       "dev": true,
+      "optional": true,
       "requires": {
-        "define-property": "^2.0.2",
-        "extend-shallow": "^3.0.2",
-        "regex-not": "^1.0.2",
-        "safe-regex": "^1.1.0"
+        "os-homedir": "^1.0.0"
       }
     },
-    "to-regex-range": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
-      "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
-      "dev": true,
+    "upath": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz",
+      "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==",
+      "dev": true
+    },
+    "uri-js": {
+      "version": "4.4.1",
+      "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+      "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
       "requires": {
-        "is-number": "^7.0.0"
+        "punycode": "^2.1.0"
       }
     },
-    "toidentifier": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
-      "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==",
+    "urix": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz",
+      "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=",
       "dev": true
     },
-    "tough-cookie": {
-      "version": "2.5.0",
-      "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
-      "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
+    "url": {
+      "version": "0.11.0",
+      "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz",
+      "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=",
       "dev": true,
       "requires": {
-        "psl": "^1.1.28",
-        "punycode": "^2.1.1"
+        "punycode": "1.3.2",
+        "querystring": "0.2.0"
+      },
+      "dependencies": {
+        "punycode": {
+          "version": "1.3.2",
+          "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
+          "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=",
+          "dev": true
+        }
       }
     },
-    "tree-kill": {
-      "version": "1.2.2",
-      "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
-      "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==",
-      "dev": true
+    "url-loader": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-4.1.1.tgz",
+      "integrity": "sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==",
+      "dev": true,
+      "requires": {
+        "loader-utils": "^2.0.0",
+        "mime-types": "^2.1.27",
+        "schema-utils": "^3.0.0"
+      }
     },
-    "tslib": {
-      "version": "2.3.1",
-      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
-      "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
+    "url-parse": {
+      "version": "1.5.10",
+      "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
+      "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
+      "dev": true,
+      "requires": {
+        "querystringify": "^2.1.1",
+        "requires-port": "^1.0.0"
+      }
     },
-    "tsutils": {
-      "version": "3.21.0",
-      "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz",
-      "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==",
+    "use": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
+      "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==",
+      "dev": true
+    },
+    "util": {
+      "version": "0.11.1",
+      "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz",
+      "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==",
       "dev": true,
       "requires": {
-        "tslib": "^1.8.1"
+        "inherits": "2.0.3"
       },
       "dependencies": {
-        "tslib": {
-          "version": "1.14.1",
-          "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
-          "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+        "inherits": {
+          "version": "2.0.3",
+          "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+          "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
           "dev": true
         }
       }
     },
-    "tunnel-agent": {
-      "version": "0.6.0",
-      "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
-      "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
-      "dev": true,
-      "requires": {
-        "safe-buffer": "^5.0.1"
-      }
-    },
-    "tweetnacl": {
-      "version": "0.14.5",
-      "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
-      "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
-      "dev": true
+    "util-deprecate": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+      "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
     },
-    "type-check": {
-      "version": "0.4.0",
-      "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
-      "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+    "util.promisify": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz",
+      "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==",
       "dev": true,
       "requires": {
-        "prelude-ls": "^1.2.1"
+        "define-properties": "^1.1.2",
+        "object.getownpropertydescriptors": "^2.0.3"
       }
     },
-    "type-fest": {
-      "version": "0.21.3",
-      "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
-      "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
+    "utila": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz",
+      "integrity": "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=",
       "dev": true
     },
-    "type-is": {
-      "version": "1.6.18",
-      "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
-      "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
-      "dev": true,
-      "requires": {
-        "media-typer": "0.3.0",
-        "mime-types": "~2.1.24"
-      }
+    "utils-merge": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
+      "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=",
+      "dev": true
     },
-    "typescript": {
-      "version": "4.3.5",
-      "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz",
-      "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==",
+    "uuid": {
+      "version": "8.3.2",
+      "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
+      "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
       "dev": true
     },
-    "ua-parser-js": {
-      "version": "0.7.31",
-      "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.31.tgz",
-      "integrity": "sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ==",
+    "uuid-browser": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/uuid-browser/-/uuid-browser-3.1.0.tgz",
+      "integrity": "sha1-DwWkCu90+eWVHiDvv0SxGHHlZBA=",
       "dev": true
     },
-    "unicode-canonical-property-names-ecmascript": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz",
-      "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==",
+    "v8-compile-cache": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz",
+      "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==",
       "dev": true
     },
-    "unicode-match-property-ecmascript": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz",
-      "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==",
+    "validate-npm-package-license": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
+      "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
       "dev": true,
       "requires": {
-        "unicode-canonical-property-names-ecmascript": "^2.0.0",
-        "unicode-property-aliases-ecmascript": "^2.0.0"
+        "spdx-correct": "^3.0.0",
+        "spdx-expression-parse": "^3.0.0"
       }
     },
-    "unicode-match-property-value-ecmascript": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz",
-      "integrity": "sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw==",
-      "dev": true
-    },
-    "unicode-property-aliases-ecmascript": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz",
-      "integrity": "sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ==",
-      "dev": true
-    },
-    "union-value": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz",
-      "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==",
+    "validate-npm-package-name": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz",
+      "integrity": "sha1-X6kS2B630MdK/BQN5zF/DKffQ34=",
       "dev": true,
       "requires": {
-        "arr-union": "^3.1.0",
-        "get-value": "^2.0.6",
-        "is-extendable": "^0.1.1",
-        "set-value": "^2.0.1"
+        "builtins": "^1.0.3"
       }
     },
-    "uniq": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz",
-      "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=",
+    "vary": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+      "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=",
       "dev": true
     },
-    "unique-filename": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz",
-      "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==",
+    "vfile": {
+      "version": "4.2.1",
+      "resolved": "https://registry.npmjs.org/vfile/-/vfile-4.2.1.tgz",
+      "integrity": "sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA==",
       "dev": true,
       "requires": {
-        "unique-slug": "^2.0.0"
+        "@types/unist": "^2.0.0",
+        "is-buffer": "^2.0.0",
+        "unist-util-stringify-position": "^2.0.0",
+        "vfile-message": "^2.0.0"
+      },
+      "dependencies": {
+        "is-buffer": {
+          "version": "2.0.5",
+          "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz",
+          "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==",
+          "dev": true
+        }
       }
     },
-    "unique-slug": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz",
-      "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==",
+    "vfile-location": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-3.2.0.tgz",
+      "integrity": "sha512-aLEIZKv/oxuCDZ8lkJGhuhztf/BW4M+iHdCwglA/eWc+vtuRFJj8EtgceYFX4LRjOhCAAiNHsKGssC6onJ+jbA==",
+      "dev": true
+    },
+    "vfile-message": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-2.0.4.tgz",
+      "integrity": "sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==",
       "dev": true,
       "requires": {
-        "imurmurhash": "^0.1.4"
+        "@types/unist": "^2.0.0",
+        "unist-util-stringify-position": "^2.0.0"
       }
     },
-    "universalify": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
-      "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==",
+    "vm-browserify": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz",
+      "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==",
       "dev": true
     },
-    "unpipe": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
-      "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=",
+    "void-elements": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz",
+      "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=",
       "dev": true
     },
-    "unset-value": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz",
-      "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=",
+    "walker": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz",
+      "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==",
       "dev": true,
       "requires": {
-        "has-value": "^0.3.1",
-        "isobject": "^3.0.0"
+        "makeerror": "1.0.12"
+      }
+    },
+    "watchpack": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.3.1.tgz",
+      "integrity": "sha512-x0t0JuydIo8qCNctdDrn1OzH/qDzk2+rdCOC3YzumZ42fiMqmQ7T3xQurykYMhYfHaPHTp4ZxAx2NfUo1K6QaA==",
+      "dev": true,
+      "requires": {
+        "glob-to-regexp": "^0.4.1",
+        "graceful-fs": "^4.1.2"
+      }
+    },
+    "watchpack-chokidar2": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz",
+      "integrity": "sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww==",
+      "dev": true,
+      "optional": true,
+      "requires": {
+        "chokidar": "^2.1.8"
       },
       "dependencies": {
-        "has-value": {
-          "version": "0.3.1",
-          "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz",
-          "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=",
+        "anymatch": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
+          "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "micromatch": "^3.1.4",
+            "normalize-path": "^2.1.1"
+          },
+          "dependencies": {
+            "normalize-path": {
+              "version": "2.1.1",
+              "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
+              "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
+              "dev": true,
+              "optional": true,
+              "requires": {
+                "remove-trailing-separator": "^1.0.1"
+              }
+            }
+          }
+        },
+        "binary-extensions": {
+          "version": "1.13.1",
+          "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz",
+          "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==",
+          "dev": true,
+          "optional": true
+        },
+        "braces": {
+          "version": "2.3.2",
+          "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
+          "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "arr-flatten": "^1.1.0",
+            "array-unique": "^0.3.2",
+            "extend-shallow": "^2.0.1",
+            "fill-range": "^4.0.0",
+            "isobject": "^3.0.1",
+            "repeat-element": "^1.1.2",
+            "snapdragon": "^0.8.1",
+            "snapdragon-node": "^2.0.1",
+            "split-string": "^3.0.2",
+            "to-regex": "^3.0.1"
+          },
+          "dependencies": {
+            "extend-shallow": {
+              "version": "2.0.1",
+              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+              "dev": true,
+              "optional": true,
+              "requires": {
+                "is-extendable": "^0.1.0"
+              }
+            }
+          }
+        },
+        "chokidar": {
+          "version": "2.1.8",
+          "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz",
+          "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "anymatch": "^2.0.0",
+            "async-each": "^1.0.1",
+            "braces": "^2.3.2",
+            "fsevents": "^1.2.7",
+            "glob-parent": "^3.1.0",
+            "inherits": "^2.0.3",
+            "is-binary-path": "^1.0.0",
+            "is-glob": "^4.0.0",
+            "normalize-path": "^3.0.0",
+            "path-is-absolute": "^1.0.0",
+            "readdirp": "^2.2.1",
+            "upath": "^1.1.1"
+          }
+        },
+        "fill-range": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
+          "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "extend-shallow": "^2.0.1",
+            "is-number": "^3.0.0",
+            "repeat-string": "^1.6.1",
+            "to-regex-range": "^2.1.0"
+          },
+          "dependencies": {
+            "extend-shallow": {
+              "version": "2.0.1",
+              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+              "dev": true,
+              "optional": true,
+              "requires": {
+                "is-extendable": "^0.1.0"
+              }
+            }
+          }
+        },
+        "fsevents": {
+          "version": "1.2.13",
+          "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz",
+          "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==",
+          "dev": true,
+          "optional": true
+        },
+        "glob-parent": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
+          "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=",
           "dev": true,
+          "optional": true,
           "requires": {
-            "get-value": "^2.0.3",
-            "has-values": "^0.1.4",
-            "isobject": "^2.0.0"
+            "is-glob": "^3.1.0",
+            "path-dirname": "^1.0.0"
           },
           "dependencies": {
-            "isobject": {
-              "version": "2.1.0",
-              "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
-              "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=",
+            "is-glob": {
+              "version": "3.1.0",
+              "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
+              "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
               "dev": true,
+              "optional": true,
               "requires": {
-                "isarray": "1.0.0"
+                "is-extglob": "^2.1.0"
               }
             }
           }
         },
-        "has-values": {
-          "version": "0.1.4",
-          "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz",
-          "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=",
-          "dev": true
-        }
-      }
-    },
-    "upath": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz",
-      "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==",
-      "dev": true
-    },
-    "uri-js": {
-      "version": "4.4.1",
-      "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
-      "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
-      "requires": {
-        "punycode": "^2.1.0"
-      }
-    },
-    "urix": {
-      "version": "0.1.0",
-      "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz",
-      "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=",
-      "dev": true
-    },
-    "url": {
-      "version": "0.11.0",
-      "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz",
-      "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=",
-      "dev": true,
-      "requires": {
-        "punycode": "1.3.2",
-        "querystring": "0.2.0"
-      },
-      "dependencies": {
-        "punycode": {
-          "version": "1.3.2",
-          "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
-          "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=",
-          "dev": true
-        }
-      }
-    },
-    "url-parse": {
-      "version": "1.5.10",
-      "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
-      "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
-      "dev": true,
-      "requires": {
-        "querystringify": "^2.1.1",
-        "requires-port": "^1.0.0"
-      }
-    },
-    "use": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
-      "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==",
-      "dev": true
-    },
-    "util-deprecate": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
-      "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
-    },
-    "utils-merge": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
-      "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=",
-      "dev": true
-    },
-    "uuid": {
-      "version": "3.4.0",
-      "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
-      "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
-      "dev": true
-    },
-    "v8-compile-cache": {
-      "version": "2.3.0",
-      "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz",
-      "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==",
-      "dev": true
-    },
-    "validate-npm-package-name": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz",
-      "integrity": "sha1-X6kS2B630MdK/BQN5zF/DKffQ34=",
-      "dev": true,
-      "requires": {
-        "builtins": "^1.0.3"
-      }
-    },
-    "vary": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
-      "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=",
-      "dev": true
-    },
-    "verror": {
-      "version": "1.10.0",
-      "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
-      "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
-      "dev": true,
-      "requires": {
-        "assert-plus": "^1.0.0",
-        "core-util-is": "1.0.2",
-        "extsprintf": "^1.2.0"
-      },
-      "dependencies": {
-        "core-util-is": {
-          "version": "1.0.2",
-          "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
-          "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
-          "dev": true
+        "is-binary-path": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz",
+          "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "binary-extensions": "^1.0.0"
+          }
+        },
+        "is-number": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
+          "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "kind-of": "^3.0.2"
+          },
+          "dependencies": {
+            "kind-of": {
+              "version": "3.2.2",
+              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+              "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+              "dev": true,
+              "optional": true,
+              "requires": {
+                "is-buffer": "^1.1.5"
+              }
+            }
+          }
+        },
+        "micromatch": {
+          "version": "3.1.10",
+          "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
+          "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "arr-diff": "^4.0.0",
+            "array-unique": "^0.3.2",
+            "braces": "^2.3.1",
+            "define-property": "^2.0.2",
+            "extend-shallow": "^3.0.2",
+            "extglob": "^2.0.4",
+            "fragment-cache": "^0.2.1",
+            "kind-of": "^6.0.2",
+            "nanomatch": "^1.2.9",
+            "object.pick": "^1.3.0",
+            "regex-not": "^1.0.0",
+            "snapdragon": "^0.8.1",
+            "to-regex": "^3.0.2"
+          }
+        },
+        "readdirp": {
+          "version": "2.2.1",
+          "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz",
+          "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "graceful-fs": "^4.1.11",
+            "micromatch": "^3.1.10",
+            "readable-stream": "^2.0.2"
+          }
+        },
+        "to-regex-range": {
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
+          "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "is-number": "^3.0.0",
+            "repeat-string": "^1.6.1"
+          }
         }
       }
     },
-    "void-elements": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz",
-      "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=",
-      "dev": true
-    },
-    "watchpack": {
-      "version": "2.3.0",
-      "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.3.0.tgz",
-      "integrity": "sha512-MnN0Q1OsvB/GGHETrFeZPQaOelWh/7O+EiFlj8sM9GPjtQkis7k01aAxrg/18kTfoIVcLL+haEVFlXDaSRwKRw==",
-      "dev": true,
-      "requires": {
-        "glob-to-regexp": "^0.4.1",
-        "graceful-fs": "^4.1.2"
-      }
-    },
     "wbuf": {
       "version": "1.7.3",
       "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz",
@@ -12127,6 +24910,24 @@
         "defaults": "^1.0.3"
       }
     },
+    "web-namespaces": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-1.1.4.tgz",
+      "integrity": "sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw==",
+      "dev": true
+    },
+    "web-streams-polyfill": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz",
+      "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==",
+      "dev": true
+    },
+    "webidl-conversions": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+      "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=",
+      "dev": true
+    },
     "webpack": {
       "version": "5.50.0",
       "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.50.0.tgz",
@@ -12160,9 +24961,9 @@
       },
       "dependencies": {
         "webpack-sources": {
-          "version": "3.2.2",
-          "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.2.tgz",
-          "integrity": "sha512-cp5qdmHnu5T8wRg2G3vZZHoJPN14aqQ89SyQ11NpGH5zEMDCclt49rzo+MaRazk7/UeILhAI+/sEtcM+7Fr0nw==",
+          "version": "3.2.3",
+          "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz",
+          "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==",
           "dev": true
         }
       }
@@ -12182,12 +24983,12 @@
       }
     },
     "webpack-dev-server": {
-      "version": "3.11.2",
-      "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.11.2.tgz",
-      "integrity": "sha512-A80BkuHRQfCiNtGBS1EMf2ChTUs0x+B3wGDFmOeT4rmJOHhHTCH2naNxIHhmkr0/UillP4U3yeIyv1pNp+QDLQ==",
+      "version": "3.11.3",
+      "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.11.3.tgz",
+      "integrity": "sha512-3x31rjbEQWKMNzacUZRE6wXvUFuGpH7vr0lIEbYpMAG9BOxi0928QU1BBswOAP3kg3H1O4hiS+sq4YyAn6ANnA==",
       "dev": true,
       "requires": {
-        "ansi-html": "0.0.7",
+        "ansi-html-community": "0.0.8",
         "bonjour": "^3.5.0",
         "chokidar": "^2.1.8",
         "compression": "^1.7.4",
@@ -12225,7 +25026,7 @@
         "ansi-regex": {
           "version": "2.1.1",
           "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
-          "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
+          "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
           "dev": true
         },
         "anymatch": {
@@ -12412,12 +25213,12 @@
           "dev": true
         },
         "mkdirp": {
-          "version": "0.5.5",
-          "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
-          "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
+          "version": "0.5.6",
+          "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
+          "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
           "dev": true,
           "requires": {
-            "minimist": "^1.2.5"
+            "minimist": "^1.2.6"
           }
         },
         "readdirp": {
@@ -12436,46 +25237,18 @@
           "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
           "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
           "dev": true,
-          "requires": {
-            "ajv": "^6.1.0",
-            "ajv-errors": "^1.0.0",
-            "ajv-keywords": "^3.1.0"
-          }
-        },
-        "semver": {
-          "version": "6.3.0",
-          "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
-          "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
-          "dev": true
-        },
-        "string-width": {
-          "version": "3.1.0",
-          "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
-          "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
-          "dev": true,
-          "requires": {
-            "emoji-regex": "^7.0.1",
-            "is-fullwidth-code-point": "^2.0.0",
-            "strip-ansi": "^5.1.0"
-          },
-          "dependencies": {
-            "ansi-regex": {
-              "version": "4.1.0",
-              "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
-              "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
-              "dev": true
-            },
-            "strip-ansi": {
-              "version": "5.2.0",
-              "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
-              "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
-              "dev": true,
-              "requires": {
-                "ansi-regex": "^4.1.0"
-              }
-            }
+          "requires": {
+            "ajv": "^6.1.0",
+            "ajv-errors": "^1.0.0",
+            "ajv-keywords": "^3.1.0"
           }
         },
+        "semver": {
+          "version": "6.3.0",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+          "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+          "dev": true
+        },
         "strip-ansi": {
           "version": "3.0.1",
           "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
@@ -12516,34 +25289,32 @@
             "range-parser": "^1.2.1",
             "webpack-log": "^2.0.0"
           }
-        },
-        "yargs": {
-          "version": "13.3.2",
-          "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz",
-          "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==",
-          "dev": true,
-          "requires": {
-            "cliui": "^5.0.0",
-            "find-up": "^3.0.0",
-            "get-caller-file": "^2.0.1",
-            "require-directory": "^2.1.1",
-            "require-main-filename": "^2.0.0",
-            "set-blocking": "^2.0.0",
-            "string-width": "^3.0.0",
-            "which-module": "^2.0.0",
-            "y18n": "^4.0.0",
-            "yargs-parser": "^13.1.2"
-          }
-        },
-        "yargs-parser": {
-          "version": "13.1.2",
-          "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
-          "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
-          "dev": true,
-          "requires": {
-            "camelcase": "^5.0.0",
-            "decamelize": "^1.2.0"
-          }
+        }
+      }
+    },
+    "webpack-filter-warnings-plugin": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/webpack-filter-warnings-plugin/-/webpack-filter-warnings-plugin-1.2.1.tgz",
+      "integrity": "sha512-Ez6ytc9IseDMLPo0qCuNNYzgtUl8NovOqjIq4uAU8LTD4uoa1w1KpZyyzFtLTEMZpkkOkLfL9eN+KGYdk1Qtwg==",
+      "dev": true
+    },
+    "webpack-hot-middleware": {
+      "version": "2.25.1",
+      "resolved": "https://registry.npmjs.org/webpack-hot-middleware/-/webpack-hot-middleware-2.25.1.tgz",
+      "integrity": "sha512-Koh0KyU/RPYwel/khxbsDz9ibDivmUbrRuKSSQvW42KSDdO4w23WI3SkHpSUKHE76LrFnnM/L7JCrpBwu8AXYw==",
+      "dev": true,
+      "requires": {
+        "ansi-html-community": "0.0.8",
+        "html-entities": "^2.1.0",
+        "querystring": "^0.2.0",
+        "strip-ansi": "^6.0.0"
+      },
+      "dependencies": {
+        "html-entities": {
+          "version": "2.3.3",
+          "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz",
+          "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==",
+          "dev": true
         }
       }
     },
@@ -12562,6 +25333,12 @@
           "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz",
           "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==",
           "dev": true
+        },
+        "uuid": {
+          "version": "3.4.0",
+          "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
+          "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
+          "dev": true
         }
       }
     },
@@ -12602,6 +25379,26 @@
         "webpack-sources": "^1.3.0"
       }
     },
+    "webpack-virtual-modules": {
+      "version": "0.2.2",
+      "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.2.2.tgz",
+      "integrity": "sha512-kDUmfm3BZrei0y+1NTHJInejzxfhtU8eDj2M7OKb2IWrPFAeO1SOH2KuQ68MSZu9IGEHcxbkKKR1v18FrUSOmA==",
+      "dev": true,
+      "requires": {
+        "debug": "^3.0.0"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "3.2.7",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+          "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+          "dev": true,
+          "requires": {
+            "ms": "^2.1.1"
+          }
+        }
+      }
+    },
     "websocket-driver": {
       "version": "0.7.4",
       "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz",
@@ -12619,6 +25416,16 @@
       "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==",
       "dev": true
     },
+    "whatwg-url": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+      "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=",
+      "dev": true,
+      "requires": {
+        "tr46": "~0.0.3",
+        "webidl-conversions": "^3.0.0"
+      }
+    },
     "which": {
       "version": "1.3.1",
       "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
@@ -12628,6 +25435,19 @@
         "isexe": "^2.0.0"
       }
     },
+    "which-boxed-primitive": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz",
+      "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==",
+      "dev": true,
+      "requires": {
+        "is-bigint": "^1.0.1",
+        "is-boolean-object": "^1.1.0",
+        "is-number-object": "^1.0.4",
+        "is-string": "^1.0.5",
+        "is-symbol": "^1.0.3"
+      }
+    },
     "which-module": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
@@ -12643,18 +25463,144 @@
         "string-width": "^1.0.2 || 2 || 3 || 4"
       }
     },
+    "widest-line": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz",
+      "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==",
+      "dev": true,
+      "requires": {
+        "string-width": "^4.0.0"
+      }
+    },
     "wildcard": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz",
       "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==",
       "dev": true
     },
+    "windows-release": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-4.0.0.tgz",
+      "integrity": "sha512-OxmV4wzDKB1x7AZaZgXMVsdJ1qER1ed83ZrTYd5Bwq2HfJVg3DJS8nqlAG4sMoJ7mu8cuRmLEYyU13BKwctRAg==",
+      "dev": true,
+      "requires": {
+        "execa": "^4.0.2"
+      },
+      "dependencies": {
+        "cross-spawn": {
+          "version": "7.0.3",
+          "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
+          "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+          "dev": true,
+          "requires": {
+            "path-key": "^3.1.0",
+            "shebang-command": "^2.0.0",
+            "which": "^2.0.1"
+          }
+        },
+        "execa": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz",
+          "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==",
+          "dev": true,
+          "requires": {
+            "cross-spawn": "^7.0.0",
+            "get-stream": "^5.0.0",
+            "human-signals": "^1.1.1",
+            "is-stream": "^2.0.0",
+            "merge-stream": "^2.0.0",
+            "npm-run-path": "^4.0.0",
+            "onetime": "^5.1.0",
+            "signal-exit": "^3.0.2",
+            "strip-final-newline": "^2.0.0"
+          }
+        },
+        "get-stream": {
+          "version": "5.2.0",
+          "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
+          "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
+          "dev": true,
+          "requires": {
+            "pump": "^3.0.0"
+          }
+        },
+        "is-stream": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
+          "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
+          "dev": true
+        },
+        "npm-run-path": {
+          "version": "4.0.1",
+          "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
+          "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
+          "dev": true,
+          "requires": {
+            "path-key": "^3.0.0"
+          }
+        },
+        "path-key": {
+          "version": "3.1.1",
+          "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+          "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+          "dev": true
+        },
+        "shebang-command": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+          "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+          "dev": true,
+          "requires": {
+            "shebang-regex": "^3.0.0"
+          }
+        },
+        "shebang-regex": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+          "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+          "dev": true
+        },
+        "which": {
+          "version": "2.0.2",
+          "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+          "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+          "dev": true,
+          "requires": {
+            "isexe": "^2.0.0"
+          }
+        }
+      }
+    },
     "word-wrap": {
       "version": "1.2.3",
       "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
       "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
       "dev": true
     },
+    "wordwrap": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
+      "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=",
+      "dev": true
+    },
+    "worker-farm": {
+      "version": "1.7.0",
+      "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz",
+      "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==",
+      "dev": true,
+      "requires": {
+        "errno": "~0.1.7"
+      }
+    },
+    "worker-rpc": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/worker-rpc/-/worker-rpc-0.1.1.tgz",
+      "integrity": "sha512-P1WjMrUB3qgJNI9jfmpZ/htmBEjFh//6l/5y8SD9hg1Ef5zTTVVoRjTrTEzPrNBQvmhMxkoTsjOXN10GWU7aCg==",
+      "dev": true,
+      "requires": {
+        "microevent.ts": "~0.1.1"
+      }
+    },
     "wrap-ansi": {
       "version": "5.1.0",
       "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
@@ -12666,6 +25612,24 @@
         "strip-ansi": "^5.0.0"
       },
       "dependencies": {
+        "ansi-regex": {
+          "version": "4.1.1",
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
+          "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==",
+          "dev": true
+        },
+        "emoji-regex": {
+          "version": "7.0.3",
+          "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
+          "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
+          "dev": true
+        },
+        "is-fullwidth-code-point": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+          "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+          "dev": true
+        },
         "string-width": {
           "version": "3.1.0",
           "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
@@ -12676,6 +25640,15 @@
             "is-fullwidth-code-point": "^2.0.0",
             "strip-ansi": "^5.1.0"
           }
+        },
+        "strip-ansi": {
+          "version": "5.2.0",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+          "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+          "dev": true,
+          "requires": {
+            "ansi-regex": "^4.1.0"
+          }
         }
       }
     },
@@ -12685,6 +25658,18 @@
       "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
       "dev": true
     },
+    "write-file-atomic": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz",
+      "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==",
+      "dev": true,
+      "requires": {
+        "imurmurhash": "^0.1.4",
+        "is-typedarray": "^1.0.0",
+        "signal-exit": "^3.0.2",
+        "typedarray-to-buffer": "^3.1.5"
+      }
+    },
     "ws": {
       "version": "6.2.2",
       "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz",
@@ -12694,6 +25679,30 @@
         "async-limiter": "~1.0.0"
       }
     },
+    "x-default-browser": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/x-default-browser/-/x-default-browser-0.4.0.tgz",
+      "integrity": "sha1-cM8NqF2nwKtcsPFaiX8jIqa91IE=",
+      "dev": true,
+      "requires": {
+        "default-browser-id": "^1.0.4"
+      }
+    },
+    "xmldoc": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/xmldoc/-/xmldoc-1.1.2.tgz",
+      "integrity": "sha512-ruPC/fyPNck2BD1dpz0AZZyrEwMOrWTO5lDdIXS91rs3wtm4j+T8Rp2o+zoOYkkAxJTZRPOSnOGei1egoRmKMQ==",
+      "dev": true,
+      "requires": {
+        "sax": "^1.2.1"
+      }
+    },
+    "xtend": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
+      "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
+      "dev": true
+    },
     "y18n": {
       "version": "4.0.3",
       "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
@@ -12713,84 +25722,106 @@
       "dev": true
     },
     "yargs": {
-      "version": "17.3.1",
-      "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.3.1.tgz",
-      "integrity": "sha512-WUANQeVgjLbNsEmGk20f+nlHgOqzRFpiGWVaBrYGYIGANIIu3lWjoyi0fNlFmJkvfhCZ6BXINe7/W2O2bV4iaA==",
+      "version": "13.3.2",
+      "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz",
+      "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==",
+      "dev": true,
       "requires": {
-        "cliui": "^7.0.2",
-        "escalade": "^3.1.1",
-        "get-caller-file": "^2.0.5",
+        "cliui": "^5.0.0",
+        "find-up": "^3.0.0",
+        "get-caller-file": "^2.0.1",
         "require-directory": "^2.1.1",
-        "string-width": "^4.2.3",
-        "y18n": "^5.0.5",
-        "yargs-parser": "^21.0.0"
+        "require-main-filename": "^2.0.0",
+        "set-blocking": "^2.0.0",
+        "string-width": "^3.0.0",
+        "which-module": "^2.0.0",
+        "y18n": "^4.0.0",
+        "yargs-parser": "^13.1.2"
       },
       "dependencies": {
         "ansi-regex": {
-          "version": "5.0.1",
-          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
-          "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
+          "version": "4.1.1",
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
+          "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==",
+          "dev": true
         },
-        "ansi-styles": {
-          "version": "4.3.0",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
-          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+        "emoji-regex": {
+          "version": "7.0.3",
+          "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
+          "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
+          "dev": true
+        },
+        "find-up": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
+          "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+          "dev": true,
           "requires": {
-            "color-convert": "^2.0.1"
+            "locate-path": "^3.0.0"
           }
         },
-        "cliui": {
-          "version": "7.0.4",
-          "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
-          "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
+        "is-fullwidth-code-point": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+          "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+          "dev": true
+        },
+        "locate-path": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
+          "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+          "dev": true,
           "requires": {
-            "string-width": "^4.2.0",
-            "strip-ansi": "^6.0.0",
-            "wrap-ansi": "^7.0.0"
+            "p-locate": "^3.0.0",
+            "path-exists": "^3.0.0"
           }
         },
-        "color-convert": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
-          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+        "p-locate": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
+          "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+          "dev": true,
           "requires": {
-            "color-name": "~1.1.4"
+            "p-limit": "^2.0.0"
           }
         },
-        "color-name": {
-          "version": "1.1.4",
-          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
-          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+        "path-exists": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+          "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
+          "dev": true
         },
-        "strip-ansi": {
-          "version": "6.0.1",
-          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
-          "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+        "string-width": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+          "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+          "dev": true,
           "requires": {
-            "ansi-regex": "^5.0.1"
+            "emoji-regex": "^7.0.1",
+            "is-fullwidth-code-point": "^2.0.0",
+            "strip-ansi": "^5.1.0"
           }
         },
-        "wrap-ansi": {
-          "version": "7.0.0",
-          "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
-          "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+        "strip-ansi": {
+          "version": "5.2.0",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+          "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+          "dev": true,
           "requires": {
-            "ansi-styles": "^4.0.0",
-            "string-width": "^4.1.0",
-            "strip-ansi": "^6.0.0"
+            "ansi-regex": "^4.1.0"
           }
-        },
-        "y18n": {
-          "version": "5.0.8",
-          "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
-          "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="
         }
       }
     },
     "yargs-parser": {
-      "version": "21.0.0",
-      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.0.tgz",
-      "integrity": "sha512-z9kApYUOCwoeZ78rfRYYWdiU/iNL6mwwYlkkZfJoyMR1xps+NEBX5X7XmRpxkZHhXJ6+Ey00IwKxBBSW9FIjyA=="
+      "version": "13.1.2",
+      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
+      "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
+      "dev": true,
+      "requires": {
+        "camelcase": "^5.0.0",
+        "decamelize": "^1.2.0"
+      }
     },
     "yocto-queue": {
       "version": "0.1.0",
@@ -12799,12 +25830,18 @@
       "dev": true
     },
     "zone.js": {
-      "version": "0.11.4",
-      "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.11.4.tgz",
-      "integrity": "sha512-DDh2Ab+A/B+9mJyajPjHFPWfYU1H+pdun4wnnk0OcQTNjem1XQSZ2CDW+rfZEUDjv5M19SBqAkjZi0x5wuB5Qw==",
+      "version": "0.11.5",
+      "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.11.5.tgz",
+      "integrity": "sha512-D1/7VxEuQ7xk6z/kAROe4SUbd9CzxY4zOwVGnGHerd/SgLIVU5f4esDzQUsOCeArn933BZfWMKydH7l7dPEp0g==",
       "requires": {
-        "tslib": "^2.0.0"
+        "tslib": "^2.3.0"
       }
+    },
+    "zwitch": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-1.0.5.tgz",
+      "integrity": "sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw==",
+      "dev": true
     }
   }
 }
diff --git a/package.json b/package.json
index bbeff84ba0061cf5596dc00a1af1b78d4c9c9b44..8045a92ddfdc2c4b3ee43a83e590b11bf7cdf35a 100644
--- a/package.json
+++ b/package.json
@@ -1,12 +1,8 @@
 {
   "name": "interactive-viewer",
-  "version": "2.6.10",
-  "description": "Siibra Explorer. Integrating KG query, dataset previews & more. Based on humanbrainproject/nehuba & google/neuroglancer. Built with angular",
+  "version": "2.7.0",
+  "description": "siibra-explorer - explore brain atlases. Based on humanbrainproject/nehuba & google/neuroglancer. Built with angular",
   "scripts": {
-    "build-aot": "ng build && node ./third_party/matomo/processMatomo.js",
-    "dev-server-aot": "ng serve",
-    "e2e": "echo NYI && exit 1",
-    "wd": "webdriver-manager",
     "lint": "eslint src --ext .ts",
     "eslint": "eslint",
     "ng": "ng",
@@ -14,7 +10,12 @@
     "build": "ng build",
     "watch": "ng build --watch --configuration development",
     "test": "ng test",
-    "test-ci": "ng test --progress false --watch false --browsers=ChromeHeadless"
+    "test-ci": "ng test --progress false --watch false --browsers=ChromeHeadless",
+    "sapi-schema": "npx openapi-typescript@5.1.1 http://localhost:5000/v1_0/openapi.json --output ./src/atlasComponents/sapi/schema.ts && eslint ./src/atlasComponents/sapi/schema.ts --no-ignore --fix",
+    "api-schema": "node src/plugin/generateTypes.js",
+    "docs:json": "compodoc -p ./tsconfig.json -e json -d .",
+    "storybook": "npm run docs:json && start-storybook -p 6006",
+    "build-storybook": "npm run docs:json && build-storybook"
   },
   "keywords": [],
   "author": "FZJ-INM1-BDA <inm1-bda@fz-juelich.de>",
@@ -23,18 +24,31 @@
     "@angular-devkit/build-angular": "^12.2.13",
     "@angular/cli": "^12.2.13",
     "@angular/compiler-cli": "^12.2.13",
+    "@babel/core": "^7.17.5",
+    "@compodoc/compodoc": "^1.1.19",
+    "@storybook/addon-actions": "^6.4.22",
+    "@storybook/addon-essentials": "^6.4.22",
+    "@storybook/addon-interactions": "^6.4.22",
+    "@storybook/addon-links": "^6.4.22",
+    "@storybook/angular": "^6.4.22",
+    "@storybook/builder-webpack5": "^6.4.22",
+    "@storybook/manager-webpack5": "^6.4.22",
+    "@storybook/testing-library": "0.0.11",
     "@types/jasmine": "~3.8.0",
     "@types/node": "^12.11.1",
     "@typescript-eslint/eslint-plugin": "^4.29.2",
     "@typescript-eslint/parser": "^4.29.2",
+    "babel-loader": "^8.2.3",
     "eslint": "^7.32.0",
+    "eslint-plugin-storybook": "^0.5.11",
     "jasmine-core": "~3.8.0",
     "jasmine-marbles": "^0.8.3",
-    "karma": "^6.3.16",
+    "karma": "^6.3.17",
     "karma-chrome-launcher": "~3.1.0",
     "karma-coverage": "~2.0.3",
     "karma-jasmine": "~4.0.0",
     "karma-jasmine-html-reporter": "~1.7.0",
+    "storybook-dark-mode": "^1.0.9",
     "typescript": "~4.3.5"
   },
   "dependencies": {
@@ -53,7 +67,6 @@
     "acorn": "^8.4.1",
     "export-nehuba": "0.0.12",
     "file-loader": "^6.2.0",
-    "hbp-connectivity-component": "^0.5.2",
     "jszip": "^3.6.0",
     "postcss": "^8.3.6",
     "raw-loader": "^4.0.2",
diff --git a/src/api/index.ts b/src/api/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..51c31a21157e2a010baa88039032c69d0d01ec2f
--- /dev/null
+++ b/src/api/index.ts
@@ -0,0 +1,10 @@
+export {
+  JRPCRequest,
+  JRPCResp,
+  JRPCSuccessResp,
+  JRPCErrorResp,
+} from "./jsonrpc"
+
+export {
+  ApiService,
+} from "./service"
diff --git a/src/api/jsonrpc.ts b/src/api/jsonrpc.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3c79f42a0132f6c89d92ec55f29872fd1826fcf9
--- /dev/null
+++ b/src/api/jsonrpc.ts
@@ -0,0 +1,111 @@
+type JRPCBase = { jsonrpc: "2.0" }
+
+export type JRPCRequest<Method, T> = {
+  method: Method // does NOT start with rpc.
+  params?: T
+  id?: string // if absent, notification, does not require response
+} & JRPCBase
+
+export type JRPCSuccessResp<T> = {
+  result: T
+  id?: string
+} & JRPCBase
+
+export type JRPCErrorResp<T> = {
+  error: {
+    /**
+     * 
+     * -32700	Parse error	Invalid JSON was received by the server.An error occurred on the server while parsing the JSON text.
+     * -32600	Invalid Request	The JSON sent is not a valid Request object.
+     * -32601	Method not found	The method does not exist / is not available.
+     * -32602	Invalid params	Invalid method parameter(s).
+     * -32603	Internal error	Internal JSON-RPC error.
+     * -32000 to -32099	Server error	Reserved for implementation-defined server-errors.
+     */
+    code: number
+    message: string
+    data?: T
+  }
+} & JRPCBase
+
+export type JRPCResp<T, E> = JRPCSuccessResp<T> | JRPCErrorResp<E>
+
+export interface ListenerChannel {
+  notify: (payload: JRPCRequest<unknown, unknown>) => void
+  registerLeaveCb: (cb: () => void) => void
+}
+
+export type BroadcastChannel<
+  Protocols extends Record<string, unknown>,
+> = {
+  state: Protocols
+  listeners: ListenerChannel[]
+  emit: (event: keyof Protocols, payload: Protocols[keyof Protocols]) => void
+  addListener: (listener: ListenerChannel) => void
+}
+
+export function createBroadcastingJsonRpcChannel<
+  NameSpace extends string,
+  Protocols extends Record<keyof Protocols, unknown>
+>(namespace: NameSpace, defaultState: Protocols): BroadcastChannel<Protocols>{
+  return {
+    state: defaultState,
+    listeners: [],
+    emit(event: keyof Protocols, value: Protocols[keyof Protocols]) {
+      const ev = `${namespace}${event as string}`
+      this.state[event] = value
+      const payload: Omit<JRPCRequest<string, Protocols[keyof Protocols]>, 'id'> = {
+        jsonrpc: '2.0',
+        method: ev,
+        params: this.state[event]
+      }
+      for (const listener of (this.listeners as ListenerChannel[])) {
+        listener.notify(payload)
+      }
+    },
+    addListener(listener: ListenerChannel){
+      if (this.listeners.indexOf(listener) < 0) {
+        this.listeners.push(listener)
+      }
+      listener.registerLeaveCb(() => {
+        this.listeners = this.listeners.filter(l => l !== listener)
+      })
+      for (const key in this.state) {
+        const payload: Omit<JRPCRequest<string, Protocols[keyof Protocols]>,'id'> = {
+          jsonrpc: '2.0',
+          method: `${namespace}.${key}`,
+          params: this.state[key]
+        }
+        listener.notify(payload)
+      }
+    }
+  }
+}
+
+type BoothProtocol = Record<string, {
+  request: unknown
+  response: unknown
+}>
+
+export class BoothVisitor<T extends BoothProtocol>{
+  constructor(private booth: Booth<T>){
+
+  }
+  request(event: JRPCRequest<keyof T, T[keyof T]['request']>) {
+    return this.booth.responder.onRequest(event)
+  }
+}
+
+export interface BoothResponder<RespParam extends BoothProtocol>{
+  onRequest: (event: JRPCRequest<keyof RespParam, RespParam[keyof RespParam]['request']>) => Promise<void | JRPCResp<RespParam[keyof RespParam]['response'], string>>
+}
+
+export class Booth<T extends BoothProtocol>{
+  constructor(
+    public responder: BoothResponder<T>
+  ){
+  }
+  handshake() {
+    return new BoothVisitor<T>(this)
+  }
+}
diff --git a/src/api/service.ts b/src/api/service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..67269b96039a13ff4f246fea991a7b8adecdba8a
--- /dev/null
+++ b/src/api/service.ts
@@ -0,0 +1,542 @@
+import { Inject, Injectable, Optional } from "@angular/core";
+import { select, Store } from "@ngrx/store";
+import { Subject } from "rxjs";
+import { distinctUntilChanged, filter, map, take } from "rxjs/operators";
+import { SAPI, SapiAtlasModel, SapiParcellationModel, SapiRegionModel, SapiSpaceModel, OpenMINDSCoordinatePoint } from "src/atlasComponents/sapi";
+import { SxplrCoordinatePointExtension } from "src/atlasComponents/sapi/type";
+import { MainState, atlasSelection, userInteraction, annotation, atlasAppearance } from "src/state"
+import { ClickInterceptor, CLICK_INTERCEPTOR_INJECTOR } from "src/util";
+import { CANCELLABLE_DIALOG, CANCELLABLE_DIALOG_OPTS } from "src/util/interfaces";
+import { Booth, BoothResponder, createBroadcastingJsonRpcChannel, JRPCRequest, JRPCResp } from "./jsonrpc"
+
+export type NAMESPACE_TYPE = "sxplr"
+export const namespace: NAMESPACE_TYPE = "sxplr"
+const nameSpaceRegex = new RegExp(`^${namespace}`)
+
+type AddableLayer = atlasAppearance.NgLayerCustomLayer
+
+type AtId = {
+  "@id": string
+}
+
+type RequestUserTypes = {
+  region: SapiRegionModel
+  point: OpenMINDSCoordinatePoint
+  confirm: void
+  input: string
+}
+
+type RequestUser<T extends keyof RequestUserTypes> = {
+  type: T
+  message: string
+  promise: Promise<RequestUserTypes[T]>
+  id: string
+  rs: (arg: RequestUserTypes[T]) => void
+  rj: (reason: string) => void
+}
+
+export type ApiBoothEvents = {
+  getAllAtlases: {
+    request: null
+    response: SapiAtlasModel[]
+  }
+  getSupportedTemplates: {
+    request: null
+    response: SapiSpaceModel[]
+  }
+  getSupportedParcellations: {
+    request: null
+    response: SapiParcellationModel[]
+  }
+
+  selectAtlas: {
+    request: AtId
+    response: 'OK'
+  }
+  selectParcellation: {
+    request: AtId
+    response: 'OK'
+  }
+  selectTemplate: {
+    request: AtId
+    response: 'OK'
+  }
+
+  navigateTo: {
+    request: MainState['[state.atlasSelection]']['navigation'] & { animate?: boolean }
+    response: 'OK'
+  }
+
+  getUserToSelectARoi: {
+    request: {
+      type: 'region' | 'point'
+      message: string
+    }
+    response: SapiRegionModel | OpenMINDSCoordinatePoint
+  }
+  
+  addAnnotations: {
+    request: {
+      annotations: SxplrCoordinatePointExtension[]
+    }
+    response: 'OK'
+  }
+
+  rmAnnotations: {
+    request: {
+      annotations: AtId[]
+    }
+    response: 'OK'
+  }
+
+  loadLayers: {
+    request: {
+      layers: AddableLayer[]
+    }
+    response: 'OK'
+  }
+
+  updateLayers: {
+    request: {
+      layers: AddableLayer[]
+    }
+    response: 'OK'
+  }
+
+  removeLayers: {
+    request: {
+      layers: {id: string}[]
+    }
+    response: 'OK'
+  }
+
+  exit: {
+    request: {
+      requests: JRPCRequest<keyof ApiBoothEvents, ApiBoothEvents[keyof ApiBoothEvents]['request']>[]
+    }
+    response: 'OK'
+  }
+
+  cancelRequest: {
+    request: {
+      id: string
+    }
+    response: 'OK'
+  }
+}
+
+export type HeartbeatEvents = {
+  init: {
+    request: null
+    response: {
+      name: string
+    }
+  }
+}
+
+export type BroadCastingApiEvents = {
+  atlasSelected: SapiAtlasModel
+  templateSelected: SapiSpaceModel
+  parcellationSelected: SapiParcellationModel
+  allRegions: SapiRegionModel[]
+  regionsSelected: SapiRegionModel[]
+}
+
+const broadCastDefault: BroadCastingApiEvents = {
+  atlasSelected: null,
+  templateSelected: null,
+  parcellationSelected: null,
+  allRegions: [],
+  regionsSelected: [],
+}
+
+@Injectable({
+  providedIn: 'root'
+})
+
+export class ApiService implements BoothResponder<ApiBoothEvents>{
+
+  public broadcastCh = createBroadcastingJsonRpcChannel<`${NAMESPACE_TYPE}.on`, BroadCastingApiEvents>(`${namespace}.on`, broadCastDefault)
+  public booth = new Booth<ApiBoothEvents>(this)
+
+  private requestUserQueue: RequestUser<keyof RequestUserTypes>[] = []
+  private requestUser$ = new Subject<RequestUser<keyof RequestUserTypes>>()
+  private fulfillUserRequest(error: string, result: RequestUserTypes[keyof RequestUserTypes]){
+    const {
+      rs, rj
+    } = this.requestUserQueue.pop()
+    if (!!error) {
+      rj(error)
+    } else {
+      rs(result)
+    }
+    if (this.dismissDialog) {
+      this.dismissDialog()
+      this.dismissDialog = null
+    }
+    if (this.requestUserQueue.length > 0) {
+      this.requestUser$.next(this.requestUserQueue[0])
+    }
+  }
+  private dismissDialog: () => void
+  private onMouseClick(): boolean {
+    if (this.requestUserQueue.length === 0) return true
+    
+    const { type } = this.requestUserQueue[0]
+
+    if (type === "region") {
+      let moRegion: SapiRegionModel
+      this.store.pipe(
+        select(userInteraction.selectors.mousingOverRegions),
+        filter(val => val.length > 0),
+        map(val => val[0]),
+        take(1)
+      ).subscribe(region => moRegion = region)
+      if (!!moRegion) {
+        this.fulfillUserRequest(null, moRegion)
+        return false
+      }
+    }
+
+    if (type === "point") {
+      let point: OpenMINDSCoordinatePoint
+      this.store.pipe(
+        select(userInteraction.selectors.mousingOverPosition),
+        take(1)
+      ).subscribe(p => point = p)
+      if (!!point) {
+        this.fulfillUserRequest(null, point)
+        return false
+      }
+    }
+    return true
+  }
+
+  private onDestoryCb: (() => void)[] = []
+  constructor(
+    private store: Store,
+    private sapi: SAPI,
+    @Optional() @Inject(CANCELLABLE_DIALOG) openCancellableDialog: (message: string, options: CANCELLABLE_DIALOG_OPTS) => () => void,
+    @Optional() @Inject(CLICK_INTERCEPTOR_INJECTOR) clickInterceptor: ClickInterceptor,
+  ){
+
+    if (clickInterceptor) {
+      const { register, deregister } = clickInterceptor
+      const onMouseClick = this.onMouseClick.bind(this)
+      register(onMouseClick)
+      this.onDestoryCb.push(() => deregister(onMouseClick))
+    }
+
+    if (openCancellableDialog) {
+
+      const requestUsersSub = this.requestUser$.pipe(
+        distinctUntilChanged((o, n) => o?.promise === n?.promise)
+      ).subscribe(item => {
+        if (this.dismissDialog) this.dismissDialog()
+        if (!item) return
+        this.dismissDialog = openCancellableDialog(item.message, {
+          userCancelCallback: () => {
+            this.fulfillUserRequest(`user Cancelled`, null)
+            this.dismissDialog = null
+          }
+        })
+      })
+      this.onDestoryCb.push(() => requestUsersSub.unsubscribe())
+    }
+
+    this.store.pipe(
+      select(atlasSelection.selectors.selectedAtlas)
+    ).subscribe(atlas => {
+      this.broadcastCh.emit('atlasSelected', atlas)
+    })
+    this.store.pipe(
+      select(atlasSelection.selectors.selectedParcellation)
+    ).subscribe(parcellation => {
+      this.broadcastCh.emit('parcellationSelected', parcellation)
+    })
+    this.store.pipe(
+      select(atlasSelection.selectors.selectedTemplate)
+    ).subscribe(template => {
+      this.broadcastCh.emit('templateSelected', template)
+    })
+    this.store.pipe(
+      select(atlasSelection.selectors.selectedRegions)
+    ).subscribe(regions => {
+      this.broadcastCh.emit('regionsSelected', regions)
+    })
+    this.store.pipe(
+      select(atlasSelection.selectors.selectedParcAllRegions)
+    ).subscribe(regions => {
+      this.broadcastCh.emit('allRegions', regions)
+    })
+  }
+  async onRequest(event: JRPCRequest<keyof ApiBoothEvents, unknown>): Promise<void | JRPCResp<ApiBoothEvents[keyof ApiBoothEvents]['response'], string>> {
+    /**
+     * if id is not present, then it's a no-op
+     */
+    if (!event.id) {
+      return
+    }
+    if (!nameSpaceRegex.test(event.method)) return
+
+    const method = event.method.replace(nameSpaceRegex, '').replace(/^\./, '')
+    switch (method) {
+    case 'getAllAtlases': {
+      if (!event.id) return
+      const atlases = await this.sapi.atlases$.pipe(
+        take(1)
+      ).toPromise()
+      return {
+        id: event.id,
+        result: atlases,
+        jsonrpc: '2.0'
+      }
+    }
+    case 'getSupportedParcellations': {
+      if (!event.id) return
+      const parcs = await this.store.pipe(
+        atlasSelection.fromRootStore.allAvailParcs(this.sapi),
+        take(1)
+      ).toPromise()
+      return {
+        id: event.id,
+        jsonrpc: '2.0',
+        result: parcs
+      }
+    }
+    case 'getSupportedTemplates': {
+      if (!event.id) return
+      const spaces = await this.store.pipe(
+        atlasSelection.fromRootStore.allAvailSpaces(this.sapi),
+        take(1)
+      ).toPromise()
+      return {
+        id: event.id,
+        jsonrpc: '2.0',
+        result: spaces
+      }
+    }
+    case 'selectAtlas': {
+      const atlases = await this.sapi.atlases$.pipe(
+        take(1)
+      ).toPromise()
+      const id = event.params as ApiBoothEvents['selectAtlas']['request']
+      const atlas = atlases.find(atlas => atlas["@id"] === id?.["@id"])
+      if (!atlas) {
+        if (!!event.id) {
+          return {
+            id: event.id,
+            jsonrpc: '2.0',
+            error: {
+              code: -32602,
+              message:`atlas id ${id?.["@id"]} not found`
+            }
+          }
+        }
+        return
+      }
+      this.store.dispatch(
+        atlasSelection.actions.selectAtlas({ atlas })
+      )
+      if (!!event.id) {
+        return {
+          jsonrpc: '2.0',
+          id: event.id,
+          result: null
+        }
+      }
+      break
+    }
+    case 'selectParcellation': {
+      if (!!event.id) {
+        return {
+          jsonrpc: '2.0',
+          id: event.id,
+          error: {
+            code: -32601,
+            message: `NYI`
+          }
+        }
+      }
+      break
+    }
+    case 'selectTemplate': {
+      if (!!event.id) {
+        return {
+          jsonrpc: '2.0',
+          id: event.id,
+          error: {
+            code: -32601,
+            message: `NYI`
+          }
+        }
+      }
+      break
+    }
+    case 'navigateTo': {
+      const { animate, ...navigation } = event.params as ApiBoothEvents['navigateTo']['request']
+      this.store.dispatch(
+        atlasSelection.actions.navigateTo({
+          navigation,
+          animation: !!animate
+        })
+      )
+      if (!!event.id) {
+        const timeoutDuration = !!animate
+          ? 500
+          : 0
+        await new Promise(rs => setTimeout(rs, timeoutDuration))
+        return {
+          id: event.id,
+          jsonrpc: '2.0',
+          result: null
+        }
+      }
+      break
+    }
+    case 'getUserToSelectARoi': {
+      const { params, id } = event as JRPCRequest<'getUserToSelectARoi', ApiBoothEvents['getUserToSelectARoi']['request']>
+      const { type, message } = params
+      if (!params || (type !== "region" && type !== "point")) {
+        return {
+          id: event.id,
+          jsonrpc: '2.0',
+          error: {
+            code: -32602,
+            message: `type must be either region or point!`
+          }
+        }
+      }
+      let rs, rj
+      const promise = new Promise<RequestUserTypes['region'] | RequestUserTypes['point']>((_rs, _rj) => {
+        rs = _rs
+        rj = _rj
+      })
+      this.requestUserQueue.push({
+        message,
+        promise,
+        id,
+        type: type as 'region' | 'point',
+        rj,
+        rs
+      })
+      this.requestUser$.next(
+        this.requestUserQueue[0]
+      )
+      return promise.then(val => {
+        return {
+          id,
+          jsonrpc: '2.0',
+          result: val
+        }
+      })
+    }
+    case 'addAnnotations': {
+      const { annotations } = event.params as ApiBoothEvents['addAnnotations']['request']
+      const ann = annotations as (annotation.Annotation<'openminds'>)[]
+      this.store.dispatch(
+        annotation.actions.addAnnotations({
+          annotations: ann
+        })
+      )
+      if (event.id) {
+        return {
+          jsonrpc: '2.0',
+          id: event.id,
+          result: 'OK'
+        }
+      }
+      break
+    }
+    case 'rmAnnotations': {
+      const { annotations } = event.params as ApiBoothEvents['rmAnnotations']['request']
+      this.store.dispatch(
+        annotation.actions.rmAnnotations({
+          annotations
+        })
+      )
+      if (event.id){
+        return {
+          jsonrpc: '2.0',
+          id: event.id,
+          result: 'OK'
+        }
+      }
+      break
+    }
+    case 'loadLayers':
+    case 'updateLayers': {
+      const { layers } = event.params as ApiBoothEvents['loadLayers']['request'] | ApiBoothEvents['updateLayers']['request']
+      for (const layer of layers) {
+        this.store.dispatch(
+          atlasAppearance.actions.addCustomLayer({
+            customLayer: layer
+          })
+        )
+      }
+      break
+    }
+    case 'removeLayers': {
+      const { layers } = event.params as ApiBoothEvents['removeLayers']['request']
+      for (const layer of layers) {
+        this.store.dispatch(
+          atlasAppearance.actions.removeCustomLayer(layer)
+        )
+      }
+      break
+    }
+    case 'exit': {
+      const { requests } = event.params as ApiBoothEvents['exit']['request']
+      for (const req of requests) {
+        await this.onRequest(req)
+      }
+      break
+    }
+    case 'cancelRequest': {
+      const { id } = event.params as ApiBoothEvents['cancelRequest']['request']
+      const idx = this.requestUserQueue.findIndex(q => q.id === id)
+      if (idx < 0) {
+        if (!!event.id) {
+          return {
+            jsonrpc: '2.0',
+            id: event.id,
+            error: {
+              code: -1,
+              message: `cancelRequest failed, request with id ${id} does not exist, or has already been resolved.`
+            }
+          }
+        }
+        return
+      }
+      const req = this.requestUserQueue.splice(idx, 1)
+      req[0].rj(`client cancelled`)
+
+      this.requestUser$.next(
+        this.requestUserQueue[0]
+      )
+
+      if (!!event.id) {
+        return {
+          jsonrpc: '2.0',
+          id: event.id,
+          result: null
+        }
+      }
+      break
+    }
+    default: {
+      const message = `Method ${event.method} not found.`
+      if (!!event.id) {
+        return {
+          jsonrpc: '2.0',
+          id: event.id,
+          error: {
+            code: -32601,
+            message
+          }
+        }
+      }
+    }
+    }
+  }
+}
diff --git a/src/assets/images/atlas-selection/firbe-long.png b/src/assets/images/atlas-selection/fibre-long.png
similarity index 100%
rename from src/assets/images/atlas-selection/firbe-long.png
rename to src/assets/images/atlas-selection/fibre-long.png
diff --git a/src/assets/images/atlas-selection/firbe-short.png b/src/assets/images/atlas-selection/fibre-short.png
similarity index 100%
rename from src/assets/images/atlas-selection/firbe-short.png
rename to src/assets/images/atlas-selection/fibre-short.png
diff --git a/src/atlasComponents/annotations/annotation.service.ts b/src/atlasComponents/annotations/annotation.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fd67601ee087cf32ddd230d7e42b3ca9fdaea421
--- /dev/null
+++ b/src/atlasComponents/annotations/annotation.service.ts
@@ -0,0 +1,204 @@
+import { BehaviorSubject, Observable } from "rxjs";
+import { distinctUntilChanged } from "rxjs/operators";
+import { getUuid } from "src/util/fn";
+
+export type TNgAnnotationEv = {
+  pickedAnnotationId: string
+  pickedOffset: number
+}
+
+/**
+ * axis aligned bounding box
+ */
+export type TNgAnnotationAABBox = {
+  type: 'aabbox'
+  pointA: [number, number, number]
+  pointB: [number, number, number]
+  id: string
+  description?: string
+}
+
+export type TNgAnnotationLine = {
+  type: 'line'
+  pointA: [number, number, number]
+  pointB: [number, number, number]
+  id: string
+  description?: string
+}
+
+export type TNgAnnotationPoint = {
+  type: 'point'
+  point: [number, number, number]
+  id: string
+  description?: string
+}
+
+export type AnnotationSpec = TNgAnnotationLine | TNgAnnotationPoint | TNgAnnotationAABBox
+type _AnnotationSpec = Omit<AnnotationSpec, 'type'> & { type: number }
+type AnnotationRef = Record<string, unknown>
+
+interface NgAnnotationLayer {
+  layer: {
+    localAnnotations: {
+      references: {
+        get(id: string): AnnotationRef
+        delete(id: string): void
+      }
+      update(ref: AnnotationRef, spec: _AnnotationSpec): void
+      add(spec: _AnnotationSpec): void
+      delete(spec: AnnotationRef):void
+      annotationMap: Map<string, _AnnotationSpec>
+    }
+    registerDisposer(fn: () => void): void
+  }
+  setVisible(flag: boolean): void
+}
+
+export class AnnotationLayer {
+  static Map = new Map<string, AnnotationLayer>()
+  static Get(name: string, color: string){
+    if (AnnotationLayer.Map.has(name)) return AnnotationLayer.Map.get(name)
+    const layer = new AnnotationLayer(name, color)
+    AnnotationLayer.Map.set(name, layer)
+    return layer
+  }
+
+  private _onHover = new BehaviorSubject<{ id: string, offset: number }>(null)
+  public onHover: Observable<{ id: string, offset: number }> = this._onHover.asObservable().pipe(
+    distinctUntilChanged((o, n) => o?.id === n?.id)
+  )
+  private onDestroyCb: (() => void)[] = []
+  private nglayer: NgAnnotationLayer
+  private idset = new Set<string>()
+  constructor(
+    private name: string = getUuid(),
+    private color="#ffffff"
+  ){
+    const layerSpec = this.viewer.layerSpecification.getLayer(
+      this.name,
+      {
+        type: "annotation",
+        "annotationColor": this.color,
+        "annotations": [],
+        name: this.name,
+        transform: [
+          [1, 0, 0, 0],
+          [0, 1, 0, 0],
+          [0, 0, 1, 0],
+          [0, 0, 0, 1],
+        ]
+      }
+    )
+    this.nglayer = this.viewer.layerManager.addManagedLayer(layerSpec)
+    const mouseState = this.viewer.mouseState
+    const res: () => void = mouseState.changed.add(() => {
+      const payload = mouseState.active
+      && !!mouseState.pickedAnnotationId
+      && this.idset.has(mouseState.pickedAnnotationId)
+        ? {
+          id: mouseState.pickedAnnotationId,
+          offset: mouseState.pickedOffset
+        }
+        : null
+      this._onHover.next(payload)
+    })
+    this.onDestroyCb.push(res)
+    
+    this.nglayer.layer.registerDisposer(() => {
+      this.dispose()
+    })
+  }
+  setVisible(flag: boolean){
+    this.nglayer && this.nglayer.setVisible(flag)
+  }
+  dispose() {
+    this.nglayer = null
+    AnnotationLayer.Map.delete(this.name)
+    this._onHover.complete()
+    while(this.onDestroyCb.length > 0) this.onDestroyCb.pop()()
+    try {
+      this.viewer.layerManager.removeManagedLayer(this.nglayer)
+    // eslint-disable-next-line no-empty
+    } catch (e) {
+
+    }
+  }
+
+  addAnnotation(spec: AnnotationSpec){
+    if (!this.nglayer) {
+      throw new Error(`layer has already been disposed`)
+    }
+    const localAnnotations = this.nglayer.layer.localAnnotations
+    this.idset.add(spec.id)
+    const annSpec = this.parseNgSpecType(spec)
+    localAnnotations.add(
+      annSpec
+    )
+  }
+  removeAnnotation(spec: { id: string }) {
+    if (!this.nglayer) return
+    const { localAnnotations } = this.nglayer.layer
+    this.idset.delete(spec.id)
+    const ref = localAnnotations.references.get(spec.id)
+    if (ref) {
+      localAnnotations.delete(ref)
+      localAnnotations.references.delete(spec.id)
+    }
+  }
+  updateAnnotation(spec: AnnotationSpec) {
+    const localAnnotations = this.nglayer?.layer?.localAnnotations
+    if (!localAnnotations) return
+    const ref = localAnnotations.references.get(spec.id)
+    const _spec = this.parseNgSpecType(spec)
+    if (ref) {
+      localAnnotations.update(
+        ref,
+        _spec
+      )
+    } else {
+      this.idset.add(_spec.id)
+      localAnnotations.add(_spec)
+    }
+  }
+
+  private get viewer() {
+    if ((window as any).viewer) return (window as any).viewer
+    throw new Error(`window.viewer not defined`)
+  }
+
+  private parseNgSpecType(spec: AnnotationSpec): _AnnotationSpec{
+    const voxelSize = this.viewer.navigationState.voxelSize.toJSON()
+    const sanitizePoint = (p: [number, number, number]) => p.map((v, idx) => v / voxelSize[idx]) as [number, number, number]
+    const needSanitizePosition = voxelSize[0] !== 1 || voxelSize[1] !== 1 || voxelSize[2] !== 1
+    const overwrite: Partial<_AnnotationSpec> = {}
+    switch (spec.type) {
+    case "point": {
+      overwrite['type'] = 0
+      break
+    }
+    case "line": {
+      overwrite['type'] = 1
+      break
+    }
+    case "aabbox": {
+      overwrite['type'] = 2
+      break
+    }
+    default: throw new Error(`overwrite type lookup failed for ${(spec as any).type}`)
+    }
+
+    /**
+     * The unit of annotation(s) depends on voxel size. If it is 1,1,1 then it would be in um, but often it is not.
+     * If not sanitized, the annotation can be miles off.
+     */
+    if (needSanitizePosition) {
+      for (const key of ['point', 'pointA', 'pointB'] ) {
+        if (!!spec[key]) overwrite[key] = sanitizePoint(spec[key])
+      }
+    }
+    return {
+      ...spec,
+      ...overwrite,
+    } as _AnnotationSpec
+  }
+}
diff --git a/src/atlasComponents/annotations/index.ts b/src/atlasComponents/annotations/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..da271a6363a9f8e34165466841eba3dda31135f8
--- /dev/null
+++ b/src/atlasComponents/annotations/index.ts
@@ -0,0 +1 @@
+export { TNgAnnotationAABBox, AnnotationLayer, TNgAnnotationPoint, TNgAnnotationLine } from "./annotation.service"
\ No newline at end of file
diff --git a/src/atlasComponents/parcellationRegion/regionSimple/regionSimple.style.css b/src/atlasComponents/annotations/module.ts
similarity index 100%
rename from src/atlasComponents/parcellationRegion/regionSimple/regionSimple.style.css
rename to src/atlasComponents/annotations/module.ts
diff --git a/src/atlasComponents/connectivity/connectivityBrowser/connectivityBrowser.component.ts b/src/atlasComponents/connectivity/connectivityBrowser/connectivityBrowser.component.ts
deleted file mode 100644
index 638863a0893fc8a064fc1e7482b24b56bf956a39..0000000000000000000000000000000000000000
--- a/src/atlasComponents/connectivity/connectivityBrowser/connectivityBrowser.component.ts
+++ /dev/null
@@ -1,450 +0,0 @@
-import {
-  AfterViewInit, ChangeDetectorRef,
-  Component,
-  ElementRef,
-  EventEmitter,
-  OnDestroy,
-  Output,
-  ViewChild,
-  Input,
-  OnInit, Inject,
-} from "@angular/core";
-import {select, Store} from "@ngrx/store";
-import {fromEvent, Observable, Subscription, Subject, combineLatest} from "rxjs";
-import {distinctUntilChanged, filter, map} from "rxjs/operators";
-
-import { viewerStateNavigateToRegion, viewerStateSetSelectedRegions } from "src/services/state/viewerState.store.helper";
-import { ngViewerSelectorClearViewEntries, ngViewerActionClearView } from "src/services/state/ngViewerState.store.helper";
-import {
-  viewerStateAllRegionsFlattenedRegionSelector,
-  viewerStateOverwrittenColorMapSelector
-} from "src/services/state/viewerState/selectors";
-import {HttpClient} from "@angular/common/http";
-import {BS_ENDPOINT} from "src/util/constants";
-import {getIdFromKgIdObj} from "common/util";
-import {OVERWRITE_SHOW_DATASET_DIALOG_TOKEN} from "src/util/interfaces";
-
-
-const CONNECTIVITY_NAME_PLATE = 'Connectivity'
-
-@Component({
-  selector: 'connectivity-browser',
-  templateUrl: './connectivityBrowser.template.html',
-  providers: [
-    {
-      provide: OVERWRITE_SHOW_DATASET_DIALOG_TOKEN,
-      useValue: null
-    }
-  ]
-})
-export class ConnectivityBrowserComponent implements OnInit, AfterViewInit, OnDestroy {
-
-    private setColorMap$: Subject<boolean> = new Subject()
-
-    /**
-     * accordion expansion should only toggle the clearviewqueue state
-     * which should be the single source of truth
-     * setcolormaps$ is set by the presence/absence of clearviewqueue[CONNECTIVITY_NAME_PLATE]
-     */
-    private _isFirstUpdate = true
-
-    public connectivityUrl: string
-
-    private accordionIsExpanded = false
-
-    @Input()
-    set accordionExpanded(flag: boolean) {
-      /**
-         * ignore first update
-         */
-      if (this._isFirstUpdate) {
-        this._isFirstUpdate = false
-        return
-      }
-      this.accordionIsExpanded = flag
-      this.store$.dispatch(
-        ngViewerActionClearView({
-          payload: {
-            [CONNECTIVITY_NAME_PLATE]: flag && !this.noDataReceived
-          }
-        })
-      )
-      this.store$.dispatch({
-        type: 'SET_OVERWRITTEN_COLOR_MAP',
-        payload: flag? CONNECTIVITY_NAME_PLATE : false,
-      })
-
-      if (flag) {
-        this.addNewColorMap()
-      } else {
-        this.restoreDefaultColormap()
-      }
-    }
-
-    @Output()
-    connectivityDataReceived = new EventEmitter<any>()
-
-    @Output()
-    setOpenState: EventEmitter<boolean> = new EventEmitter()
-
-    @Output()
-    connectivityLoadUrl: EventEmitter<string> = new EventEmitter()
-
-    @Output() connectivityNumberReceived: EventEmitter<string> = new EventEmitter()
-
-    @Input()
-    set region(val) {
-      const newRegionName = val && val.name
-
-      if (!val) {
-        this.store$.dispatch({
-          type: 'SET_OVERWRITTEN_COLOR_MAP',
-          payload: false,
-        })
-        return
-      }
-
-      if (newRegionName !== this.regionName && this.defaultColorMap) {
-        this.restoreDefaultColormap()
-      }
-
-      if (val.status
-          && !val.name.includes('left hemisphere')
-          && !val.name.includes('right hemisphere')) {
-        this.regionHemisphere = val.status
-      }
-
-      this.regionName = newRegionName
-      this.regionId = val.id? val.id.kg? getIdFromKgIdObj(val.id.kg) : val.id : null
-      this.atlasId = val.context.atlas['@id']
-      this.parcellationId = val.context.parcellation['@id']
-
-      if(this.selectedDataset) {
-        this.setConnectivityUrl()
-        this.setProfileLoadUrl()
-      }
-      // TODO may not be necessary
-      this.changeDetectionRef.detectChanges()
-    }
-    public atlasId: any
-    public parcellationId: any
-    public regionId: string
-    public regionName: string
-    public regionHemisphere: string = null
-    public datasetList: any[] = []
-    public selectedDataset: any
-    public selectedDatasetName: any
-    public selectedDatasetDescription: string = ''
-    public selectedDatasetKgId: string = ''
-    public selectedDatasetKgSchema: string = ''
-    public connectedAreas = []
-
-    private selectedParcellationFlatRegions$ = this.store$.pipe(
-      select(viewerStateAllRegionsFlattenedRegionSelector)
-    )
-    public overwrittenColorMap$: Observable<any>
-
-    private subscriptions: Subscription[] = []
-    public expandMenuIndex = -1
-    public allRegions = []
-    public defaultColorMap: Map<string, Map<number, { red: number, green: number, blue: number }>>
-
-    public noDataReceived = false
-
-    @ViewChild('connectivityComponent', {read: ElementRef}) public connectivityComponentElement: ElementRef<any>
-    @ViewChild('fullConnectivityGrid') public fullConnectivityGridElement: ElementRef<any>
-
-    constructor(
-        private store$: Store<any>,
-        private changeDetectionRef: ChangeDetectorRef,
-        private httpClient: HttpClient,
-        @Inject(BS_ENDPOINT) private siibraApiUrl: string,
-    ) {
-
-      this.overwrittenColorMap$ = this.store$.pipe(
-        select(viewerStateOverwrittenColorMapSelector),
-        distinctUntilChanged()
-      )
-    }
-
-    public loadUrl: string
-    public fullConnectivityLoadUrl: string
-
-    ngOnInit(): void {
-      this.setConnectivityUrl()
-
-      this.httpClient.get<[]>(this.connectivityUrl).subscribe(res => {
-        this.datasetList = res
-        this.selectedDataset = this.datasetList[0]?.['@id']
-        this.selectedDatasetName = this.datasetList[0]?.['src_name']
-        this.selectedDatasetDescription = this.datasetList[0]?.['src_info']
-        // this.selectedDatasetKgId = this.datasetList[0]?.kgId || null
-        // this.selectedDatasetKgSchema = this.datasetList[0]?.kgSchema || null
-
-        this.changeDataset()
-      })
-    }
-
-    public ngAfterViewInit(): void {
-      this.subscriptions.push(
-        this.store$.pipe(
-          select(viewerStateOverwrittenColorMapSelector),
-        ).subscribe(value => {
-          if (this.accordionIsExpanded) {
-            this.setColorMap$.next(!!value)
-          }
-        })
-      )
-
-      /**
-       * Listen to of clear view entries
-       * can come from within the component (when connectivity is not available for the dataset)
-       * --> do not collapse
-       * or outside (user clicks x in chip)
-       * --> collapse
-       */
-      this.subscriptions.push(
-        this.store$.pipe(
-          select(ngViewerSelectorClearViewEntries),
-          map(arr => arr.filter(v => v === CONNECTIVITY_NAME_PLATE)),
-          filter(arr => arr.length ===0),
-          distinctUntilChanged()
-        ).subscribe(() => {
-          if (!this.noDataReceived) {
-            this.setOpenState.emit(false)
-          }
-        })
-      )
-
-
-      this.subscriptions.push(this.overwrittenColorMap$.subscribe(ocm => {
-        if (this.accordionIsExpanded && !ocm) {
-          this.setOpenState.emit(false)
-        }
-      }))
-
-      this.subscriptions.push(
-        this.selectedParcellationFlatRegions$.subscribe(flattenedRegions => {
-          this.defaultColorMap = null
-          this.allRegions = flattenedRegions
-        }),
-      )
-
-      /**
-         * setting/restoring colormap
-         */
-      this.subscriptions.push(
-        combineLatest(
-          this.setColorMap$.pipe(
-            distinctUntilChanged()
-          ),
-          fromEvent(this.connectivityComponentElement?.nativeElement, 'connectivityDataReceived').pipe(
-            map((e: CustomEvent) => {
-              if (e.detail !== 'No data') {
-                this.connectivityNumberReceived.emit(e.detail.length)
-              }
-              return e.detail
-            })
-          )
-        ).subscribe(([flag, connectedAreas]) => {
-          if (connectedAreas === 'No data') {
-            this.noDataReceived = true
-            return this.clearViewer()
-          } else {
-            this.store$.dispatch(
-              ngViewerActionClearView({
-                payload: {
-                  [CONNECTIVITY_NAME_PLATE]: true
-                }
-              })
-            )
-            this.noDataReceived = false
-            this.connectivityNumberReceived.emit(connectedAreas.length)
-            this.connectedAreas = connectedAreas
-
-            if (flag) {
-              this.addNewColorMap()
-              this.store$.dispatch({
-                type: 'SET_OVERWRITTEN_COLOR_MAP',
-                payload: 'connectivity',
-              })
-            } else {
-              this.restoreDefaultColormap()
-
-              this.store$.dispatch({type: 'SET_OVERWRITTEN_COLOR_MAP', payload: null})
-
-            }
-          }
-        })
-      )
-
-      this.subscriptions.push(
-        fromEvent(this.connectivityComponentElement?.nativeElement, 'customToolEvent', {capture: true})
-          .subscribe((e: CustomEvent) => {
-            if (e.detail.name === 'export csv') {
-              // ToDo Fix in future to use component
-              const a = document.querySelector('hbp-connectivity-matrix-row');
-              (a as any).downloadCSV()
-            }
-          }),
-        fromEvent(this.connectivityComponentElement?.nativeElement, 'connectedRegionClicked', {capture: true})
-          .subscribe((e: CustomEvent) => {
-            this.navigateToRegion(e.detail.name)
-          }),
-      )
-    }
-
-    public ngOnDestroy(): void {
-      this.connectivityNumberReceived.emit(null)
-      this.store$.dispatch(
-        ngViewerActionClearView({
-          payload: {
-            [CONNECTIVITY_NAME_PLATE]: false
-          }
-        })
-      )
-      this.restoreDefaultColormap()
-      this.subscriptions.forEach(s => s.unsubscribe())
-    }
-
-    private setConnectivityUrl() {
-      this.connectivityUrl = `${this.siibraApiUrl}/atlases/${encodeURIComponent(this.atlasId)}/parcellations/${encodeURIComponent(this.parcellationId)}/regions/${encodeURIComponent(this.regionName)}/features/ConnectivityProfile`
-    }
-
-    private setProfileLoadUrl() {
-      const url = `${this.connectivityUrl}/${encodeURIComponent(this.selectedDataset)}`
-      this.connectivityLoadUrl.emit(url)
-      this.loadUrl = url
-    }
-
-    clearViewer() {
-      this.store$.dispatch(
-        ngViewerActionClearView({
-          payload: {
-            [CONNECTIVITY_NAME_PLATE]: false
-          }
-        })
-      )
-      this.connectedAreas = []
-      this.connectivityNumberReceived.emit('0')
-
-      return this.restoreDefaultColormap()
-    }
-
-    // ToDo Affect on component
-    changeDataset(event = null) {
-      if (event) {
-        this.selectedDataset = event.value
-        const foundDataset = this.datasetList.find(d => d['@id'] === this.selectedDataset)
-        this.selectedDatasetName = foundDataset?.['src_name']
-        this.selectedDatasetDescription = foundDataset?.['src_info']
-        // this.selectedDatasetKgId = foundDataset?.kgId || null
-        // this.selectedDatasetKgSchema = foundDataset?.kgSchema || null
-      }
-      if (this.datasetList.length && this.selectedDataset) {
-        this.setProfileLoadUrl()
-
-        this.fullConnectivityLoadUrl = `${this.siibraApiUrl}/atlases/${encodeURIComponent(this.atlasId)}/parcellations/${encodeURIComponent(this.parcellationId)}/features/ConnectivityMatrix/${encodeURIComponent(this.selectedDatasetName)}`
-      }
-    }
-
-    navigateToRegion(region) {
-      this.store$.dispatch(
-        viewerStateNavigateToRegion({
-          payload: {region: this.getRegionWithName(region)}
-        })
-      )
-    }
-
-    selectRegion(region) {
-      this.store$.dispatch(
-        viewerStateSetSelectedRegions({
-          selectRegions: [ region ]
-        })
-      )
-    }
-
-    getRegionWithName(region) {
-      return this.allRegions.find(ar => {
-        if (this.regionHemisphere) {
-          let regionName = region
-          let regionStatus = null
-          if (regionName.includes('left hemisphere')) {
-            regionStatus = 'left hemisphere'
-            regionName = regionName.replace(' - left hemisphere', '');
-          } else if (regionName.includes('right hemisphere')) {
-            regionStatus = 'right hemisphere'
-            regionName = regionName.replace(' - right hemisphere', '');
-          }
-          return ar.name === regionName && ar.status === regionStatus
-        }
-
-        return ar.name === region
-      })
-    }
-
-    public restoreDefaultColormap() {
-      if (!this.defaultColorMap) return
-      getWindow().interactiveViewer.viewerHandle.applyLayersColourMap(this.defaultColorMap)
-    }
-
-    public addNewColorMap() {
-      if (!this.defaultColorMap) {
-        this.defaultColorMap = getWindow().interactiveViewer.viewerHandle.getLayersSegmentColourMap()
-      }
-
-      const existingMap: Map<string, Map<number, { red: number, green: number, blue: number }>> = (getWindow().interactiveViewer.viewerHandle.getLayersSegmentColourMap())
-      const colorMap = new Map(existingMap)
-
-      this.allRegions.forEach(r => {
-        if (r.ngId) {
-          colorMap.get(r.ngId).set(r.labelIndex, {red: 255, green: 255, blue: 255})
-        }
-      })
-
-      this.connectedAreas.forEach(area => {
-        const areaAsRegion = this.allRegions
-          .filter(r => {
-
-            if (this.regionHemisphere) {
-              let regionName = area.name
-              let regionStatus = null
-              if (regionName.includes('left hemisphere')) {
-                regionStatus = 'left hemisphere'
-                regionName = regionName.replace(' - left hemisphere', '');
-              } else if (regionName.includes('right hemisphere')) {
-                regionStatus = 'right hemisphere'
-                regionName = regionName.replace(' - right hemisphere', '');
-              }
-              return r.name === regionName && r.status === regionStatus
-            }
-
-            return r.name === area.name
-          })
-          .map(r => r)
-
-        if (areaAsRegion && areaAsRegion.length && areaAsRegion[0].ngId) {
-          colorMap.get(areaAsRegion[0].ngId).set(areaAsRegion[0].labelIndex, {
-            red: area.color.r,
-            green: area.color.g,
-            blue: area.color.b
-          })
-        }
-      })
-      getWindow().interactiveViewer.viewerHandle.applyLayersColourMap(colorMap)
-    }
-
-    exportConnectivityProfile() {
-      const a = document.querySelector('hbp-connectivity-matrix-row');
-      (a as any).downloadCSV()
-    }
-
-    public exportFullConnectivity() {
-      this.fullConnectivityGridElement?.nativeElement['downloadCSV']()
-    }
-
-}
-
-function getWindow(): any {
-  return window
-}
diff --git a/src/atlasComponents/connectivity/connectivityBrowser/connectivityBrowser.template.html b/src/atlasComponents/connectivity/connectivityBrowser/connectivityBrowser.template.html
deleted file mode 100644
index 9dfdc054e09c6eba8ad1318a37cca2db792a8ef2..0000000000000000000000000000000000000000
--- a/src/atlasComponents/connectivity/connectivityBrowser/connectivityBrowser.template.html
+++ /dev/null
@@ -1,97 +0,0 @@
-<div class="w-100 h-100 d-block d-flex flex-column pb-2">
-    <hbp-connectivity-matrix-row
-            (connectivityDataReceived)="connectivityDataReceived.emit($event)"
-            #connectivityComponent
-            *ngIf="regionName"
-            [region]="regionName + (regionHemisphere? ' - ' + regionHemisphere : '')"
-            theme="dark"
-            [loadurl]="loadUrl"
-            show-export="true"
-            show-source="false"
-            show-title="false"
-            show-toolbar="false"
-            show-description="false"
-            show-dataset-name="false"
-            custom-dataset-selector="true"
-            tools_showlog="true"
-            [tools_custom]='[{"name": "exportslot", "type": "slot"}]'
-            hide-export-view="true">
-
-        <div slot="dataset">
-            <div *ngIf="datasetList.length && selectedDataset"  class=" flex-grow-0 flex-shrink-0 d-flex flex-row flex-nowrap">
-                    <mat-form-field class="flex-grow-1 flex-shrink-1 w-0">
-                        <mat-label>
-                            Dataset
-                        </mat-label>
-
-                        <mat-select
-                                panelClass="no-max-width"
-                                [value]="selectedDataset"
-                                (selectionChange)="changeDataset($event)">
-                            <mat-option
-                                    *ngFor="let dataset of datasetList"
-                                    [value]="dataset['@id']">
-                                {{ dataset['src_name'] }}
-                            </mat-option>
-                        </mat-select>
-                    </mat-form-field>
-                <ng-container *ngIf="selectedDataset && (selectedDatasetDescription || selectedDatasetKgId)" >
-                    <button class="flex-grow-0 flex-shrink-0"
-                            mat-icon-button
-                            iav-dataset-show-dataset-dialog
-                            [iav-dataset-show-dataset-dialog-name]="selectedDatasetName"
-                            [iav-dataset-show-dataset-dialog-description]="selectedDatasetDescription"
-                            [iav-dataset-show-dataset-dialog-kgid]="selectedDatasetKgId? selectedDatasetKgId : null"
-                            [iav-dataset-show-dataset-dialog-kgschema]="selectedDatasetKgSchema? selectedDatasetKgSchema : null"
-                            >
-                        <i class="fas fa-info"></i>
-                    </button>
-                    <button class="flex-grow-0 flex-shrink-0"
-                            mat-icon-button
-                            (click)="exportFullConnectivity()"
-                            matTooltip="Export full connectivity profile">
-                        <i class="fas fa-download"></i>
-                    </button>
-                </ng-container>
-                </div>
-            <div>
-                Connectivity Profile
-            </div>
-        </div>
-
-        <div slot="connectedRegionMenu">
-            <div class="d-flex flex-column p-0 m-0" *ngIf="expandMenuIndex >= 0">
-                <mat-card-subtitle class="pt-2 pr-2 pl-2 pb-0">
-                    <div class="d-flex justify-content-between align-items-center">
-                      {{connectedAreas[expandMenuIndex].name}}
-                      <small class="d-flex align-items-center">
-                          <button mat-icon-button matTooltip="Navigate"
-                                  (click)="navigateToRegion(connectedAreas[expandMenuIndex].name)">
-                              <i class="fas fa-map-marked-alt"></i>
-                          </button>
-                          <button mat-icon-button matTooltip="Explore"
-                                  (click)="selectRegion(getRegionWithName(connectedAreas[expandMenuIndex].name))">
-                              <i class="fas fa-search-location"></i>
-                          </button>
-                      </small>
-                    </div>
-                </mat-card-subtitle>
-                <mat-divider></mat-divider>
-            </div>
-        </div>
-        <div slot="exportslot">
-            <button mat-icon-button
-                    [disabled]="noDataReceived"
-                    (click)="exportConnectivityProfile()"
-                    matTooltip="Export connectivity profile">
-                <i class="fas fa-download mb-2"></i>
-            </button>
-        </div>
-    </hbp-connectivity-matrix-row>
-    <full-connectivity-grid #fullConnectivityGrid
-                            [loadurl]="fullConnectivityLoadUrl"
-                            [name]="selectedDataset"
-                            [description]="selectedDatasetDescription"
-                            only-export="true">
-    </full-connectivity-grid>
-</div>
diff --git a/src/atlasComponents/connectivity/hasConnectivity.directive.ts b/src/atlasComponents/connectivity/hasConnectivity.directive.ts
deleted file mode 100644
index 8879de8c093488b90acd5fb81d57820f341da36e..0000000000000000000000000000000000000000
--- a/src/atlasComponents/connectivity/hasConnectivity.directive.ts
+++ /dev/null
@@ -1,62 +0,0 @@
-import {Directive, Inject, Input, OnDestroy, OnInit} from "@angular/core";
-import {of, Subscription} from "rxjs";
-import {switchMap} from "rxjs/operators";
-import {BS_ENDPOINT} from "src/util/constants";
-import {HttpClient} from "@angular/common/http";
-
-@Directive({
-  selector: '[has-connectivity]',
-  exportAs: 'hasConnectivityDirective'
-})
-
-export class HasConnectivity implements OnInit, OnDestroy {
-
-    private subscriptions: Subscription[] = []
-
-    @Input() region: any
-
-    public hasConnectivity = false
-    public connectivityNumber = 0
-
-    constructor(@Inject(BS_ENDPOINT) private siibraApiUrl: string,
-                private httpClient: HttpClient) {}
-
-    ngOnInit() {
-      this.checkConnectivity(this.region[0])
-    }
-
-    checkConnectivity(region) {
-      if (!region.context) {
-        this.hasConnectivity = false
-        return
-      }
-      const {atlas, parcellation, template} = region.context
-      if (region.name) {
-        const connectivityUrl = `${this.siibraApiUrl}/atlases/${encodeURIComponent(atlas['@id'])}/parcellations/${encodeURIComponent(parcellation['@id'])}/regions/${encodeURIComponent(region.name)}/features/ConnectivityProfile`
-
-        this.subscriptions.push(
-          this.httpClient.get<[]>(connectivityUrl).pipe(switchMap((res: any[]) => {
-            if (res && res.length) {
-              this.hasConnectivity = true
-              const url = `${connectivityUrl}/${encodeURIComponent(res[0]['@id'])}`
-              return this.httpClient.get(url)
-            } else {
-              this.hasConnectivity = false
-              this.connectivityNumber = 0
-            }
-            return of(null)
-          })).subscribe(res => {
-
-            if (res && res['__profile']) {
-              this.connectivityNumber = res['__profile'].filter(p => p > 0).length
-            }
-          })
-        )
-      }
-    }
-
-    ngOnDestroy(){
-      while (this.subscriptions.length > 0) this.subscriptions.pop().unsubscribe()
-    }
-
-}
diff --git a/src/atlasComponents/connectivity/module.ts b/src/atlasComponents/connectivity/module.ts
deleted file mode 100644
index a43e558c21da18e5998646bf63578f6ea00d8c28..0000000000000000000000000000000000000000
--- a/src/atlasComponents/connectivity/module.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import { CommonModule } from "@angular/common";
-import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from "@angular/core";
-import { AngularMaterialModule } from "src/sharedModules";
-import { ConnectivityBrowserComponent } from "./connectivityBrowser/connectivityBrowser.component";
-import {HasConnectivity} from "src/atlasComponents/connectivity/hasConnectivity.directive";
-import {KgDatasetModule} from "src/atlasComponents/regionalFeatures/bsFeatures/kgDataset";
-
-@NgModule({
-  imports: [
-    CommonModule,
-    KgDatasetModule,
-    AngularMaterialModule
-  ],
-  declarations: [
-    ConnectivityBrowserComponent,
-    HasConnectivity
-  ],
-  exports: [
-    ConnectivityBrowserComponent,
-    HasConnectivity
-  ],
-  schemas: [
-    CUSTOM_ELEMENTS_SCHEMA,
-  ],
-})
-
-export class AtlasCmptConnModule{}
diff --git a/src/atlasComponents/parcellation/index.ts b/src/atlasComponents/parcellation/index.ts
deleted file mode 100644
index f49438e683dafb818113eb571433a364854eb24b..0000000000000000000000000000000000000000
--- a/src/atlasComponents/parcellation/index.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-export { GetParcPreviewUrlPipe } from "./getParcPreviewUrl.pipe";
-export { FilterNameBySearch } from "./regionHierachy/filterNameBySearch.pipe";
-export { AtlasCmpParcellationModule } from "./module";
-export { RegionHierarchy } from "./regionHierachy/regionHierarchy.component";
-export { RegionTextSearchAutocomplete } from "./regionSearch/regionSearch.component";
diff --git a/src/atlasComponents/parcellation/module.ts b/src/atlasComponents/parcellation/module.ts
deleted file mode 100644
index c3e101c3a1d4bf3b2a6384723d79414e59a76429..0000000000000000000000000000000000000000
--- a/src/atlasComponents/parcellation/module.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-import { CommonModule } from "@angular/common";
-import { NgModule } from "@angular/core";
-import { AngularMaterialModule } from "src/sharedModules";
-import { ParcellationRegionModule } from "src/atlasComponents/parcellationRegion";
-import { RegionHierarchy } from "./regionHierachy/regionHierarchy.component";
-import { RegionTextSearchAutocomplete } from "./regionSearch/regionSearch.component";
-import { FilterNameBySearch } from "./regionHierachy/filterNameBySearch.pipe";
-import { UtilModule } from "src/util";
-import { FormsModule, ReactiveFormsModule } from "@angular/forms";
-import { ComponentsModule } from "src/components";
-import { GetParcPreviewUrlPipe } from "./getParcPreviewUrl.pipe";
-
-@NgModule({
-  imports: [
-    CommonModule,
-    UtilModule,
-    FormsModule,
-    ReactiveFormsModule,
-    AngularMaterialModule,
-    ParcellationRegionModule,
-    ComponentsModule,
-  ],
-  declarations: [
-    RegionHierarchy,
-    RegionTextSearchAutocomplete,
-
-    FilterNameBySearch,
-    GetParcPreviewUrlPipe,
-  ],
-  exports: [
-    RegionHierarchy,
-    RegionTextSearchAutocomplete,
-    FilterNameBySearch,
-    GetParcPreviewUrlPipe,
-  ]
-})
-export class AtlasCmpParcellationModule{}
\ No newline at end of file
diff --git a/src/atlasComponents/parcellation/regionHierachy/regionHierarchy.component.ts b/src/atlasComponents/parcellation/regionHierachy/regionHierarchy.component.ts
deleted file mode 100644
index 715f000757552e41ee4e67ace06e9de56006e69e..0000000000000000000000000000000000000000
--- a/src/atlasComponents/parcellation/regionHierachy/regionHierarchy.component.ts
+++ /dev/null
@@ -1,238 +0,0 @@
-import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, HostListener, Input, OnInit, Output, ViewChild } from "@angular/core";
-import { fromEvent, Subject, Subscription } from "rxjs";
-import { buffer, debounceTime } from "rxjs/operators";
-import { FilterNameBySearch } from "./filterNameBySearch.pipe";
-import { serialiseParcellationRegion } from "common/util"
-
-const insertHighlight: (name: string, searchTerm: string) => string = (name: string, searchTerm: string = '') => {
-  const regex = new RegExp(searchTerm, 'gi')
-  return searchTerm === '' ?
-    name :
-    name.replace(regex, (s) => `<span class = "highlight">${s}</span>`)
-}
-
-const getDisplayTreeNode: (searchTerm: string, selectedRegions: any[]) => (item: any) => string = (searchTerm: string = '', selectedRegions: any[] = []) => ({ ngId, name, status, labelIndex }) =>  {
-  return !!labelIndex
-    && !!ngId
-    && selectedRegions.findIndex(re =>
-      serialiseParcellationRegion({ labelIndex: re.labelIndex, ngId: re.ngId }) === serialiseParcellationRegion({ ngId, labelIndex }),
-    ) >= 0
-    ? `<span class="cursor-default regionSelected">${insertHighlight(name, searchTerm)}</span>` + (status ? ` <span class="text-muted">(${insertHighlight(status, searchTerm)})</span>` : ``)
-    : `<span class="cursor-default regionNotSelected">${insertHighlight(name, searchTerm)}</span>` + (status ? ` <span class="text-muted">(${insertHighlight(status, searchTerm)})</span>` : ``)
-}
-
-const getFilterTreeBySearch = (pipe: FilterNameBySearch, searchTerm: string) =>
-  (node: any) => {
-    const searchFields = [
-      node.name,
-      node.status,
-      ...(node.relatedAreas ? node.relatedAreas : [])
-    ]
-    return pipe.transform(searchFields, searchTerm)
-  }
-
-@Component({
-  selector: 'region-hierarchy',
-  templateUrl: './regionHierarchy.template.html',
-  styleUrls: [
-    './regionHierarchy.style.css',
-  ],
-  changeDetection: ChangeDetectionStrategy.OnPush,
-})
-
-export class RegionHierarchy implements OnInit, AfterViewInit {
-
-  @Input()
-  public useMobileUI: boolean = false
-
-  @Input()
-  public selectedRegions: any[] = []
-
-  @Input()
-  public parcellationSelected: any
-
-  private _showRegionTree: boolean = false
-
-  @Output()
-  private showRegionFlagChanged: EventEmitter<boolean> = new EventEmitter()
-
-  @Output()
-  private singleClickRegion: EventEmitter<any> = new EventEmitter()
-
-  @Output()
-  private doubleClickRegion: EventEmitter<any> = new EventEmitter()
-
-  @Output()
-  private clearAllRegions: EventEmitter<MouseEvent> = new EventEmitter()
-
-  public searchTerm: string = ''
-  private subscriptions: Subscription[] = []
-
-  @ViewChild('searchTermInput', {read: ElementRef})
-  private searchTermInput: ElementRef
-
-  public placeHolderText: string = `Start by selecting a template and a parcellation.`
-
-  /**
-   * set the height to max, bound by max-height
-   */
-  public numTotalRenderedRegions: number = 999
-  public windowHeight: number
-
-  @HostListener('document:click', ['$event'])
-  public closeRegion(event: MouseEvent) {
-    const contains = this.el.nativeElement.contains(event.target)
-    this.showRegionTree = contains
-    if (!this.showRegionTree) {
-      this.searchTerm = ''
-      this.numTotalRenderedRegions = 999
-    }
-  }
-
-  @HostListener('window:resize', ['$event'])
-  public onResize(event) {
-    this.windowHeight = event.target.innerHeight;
-  }
-
-  get regionsLabelIndexMap() {
-    return null
-  }
-
-  constructor(
-    private cdr: ChangeDetectorRef,
-    private el: ElementRef,
-  ) {
-    this.windowHeight = window.innerHeight;
-  }
-
-  public ngOnChanges() {
-    if (this.parcellationSelected) {
-      this.placeHolderText = `Search region in ${this.parcellationSelected.name}`
-      this.aggregatedRegionTree = {
-        name: this.parcellationSelected.name,
-        children: this.parcellationSelected.regions,
-      }
-    }
-    this.displayTreeNode = getDisplayTreeNode(this.searchTerm, this.selectedRegions)
-    this.filterTreeBySearch = getFilterTreeBySearch(this.filterNameBySearchPipe, this.searchTerm)
-  }
-
-  public clearRegions(event: MouseEvent) {
-    this.clearAllRegions.emit(event)
-  }
-
-  get showRegionTree() {
-    return this._showRegionTree
-  }
-
-  set showRegionTree(flag: boolean) {
-    this._showRegionTree = flag
-    this.showRegionFlagChanged.emit(this._showRegionTree)
-  }
-
-  public ngOnInit() {
-    this.displayTreeNode = getDisplayTreeNode(this.searchTerm, this.selectedRegions)
-    this.filterTreeBySearch = getFilterTreeBySearch(this.filterNameBySearchPipe, this.searchTerm)
-
-    this.subscriptions.push(
-      this.handleRegionTreeClickSubject.pipe(
-        buffer(
-          this.handleRegionTreeClickSubject.pipe(
-            debounceTime(200),
-          ),
-        ),
-      ).subscribe(arr => arr.length > 1 ? this.doubleClick(arr[0]) : this.singleClick(arr[0])),
-    )
-  }
-
-  public ngAfterViewInit() {
-    this.subscriptions.push(
-      fromEvent(this.searchTermInput.nativeElement, 'input').pipe(
-        debounceTime(200),
-      ).subscribe(ev => {
-        this.changeSearchTerm(ev)
-      }),
-    )
-  }
-
-  public escape(event: KeyboardEvent) {
-    this.showRegionTree = false
-    this.searchTerm = '';
-    (event.target as HTMLInputElement).blur()
-
-  }
-
-  public handleTotalRenderedListChanged(changeEvent: {previous: number, current: number}) {
-    const { current } = changeEvent
-    this.numTotalRenderedRegions = current
-  }
-
-  public regionHierarchyHeight() {
-    return({
-      'height' : (this.numTotalRenderedRegions * 15 + 60).toString() + 'px',
-      'max-height': (this.windowHeight - 100) + 'px',
-    })
-  }
-
-  /* NB need to bind two way data binding like this. Or else, on searchInput blur, the flat tree will be rebuilt,
-    resulting in first click to be ignored */
-
-  public changeSearchTerm(event: any) {
-    if (event.target.value === this.searchTerm) { return }
-    this.searchTerm = event.target.value
-    this.ngOnChanges()
-    this.cdr.markForCheck()
-  }
-
-  private handleRegionTreeClickSubject: Subject<any> = new Subject()
-
-  public handleClickRegion(obj: any) {
-    const {event} = obj
-    /**
-     * TODO figure out why @closeRegion gets triggered, but also, contains returns false
-     */
-    if (event) {
-      event.stopPropagation()
-    }
-    this.handleRegionTreeClickSubject.next(obj)
-  }
-
-  /* single click selects/deselects region(s) */
-  private singleClick(obj: any) {
-    if (!obj) { return }
-    const { inputItem : region } = obj
-    if (!region) { return }
-    this.singleClickRegion.emit(region)
-  }
-
-  /* double click navigate to the interested area */
-  private doubleClick(obj: any) {
-    if (!obj) {
-      return
-    }
-    const { inputItem : region } = obj
-    if (!region) {
-      return
-    }
-    this.doubleClickRegion.emit(region)
-  }
-
-  public displayTreeNode: (item: any) => string
-
-  private filterNameBySearchPipe = new FilterNameBySearch()
-  public filterTreeBySearch: (node: any) => boolean
-
-  public aggregatedRegionTree: any
-
-  public gotoRegion(region: any) {
-    this.doubleClickRegion.emit(region)
-  }
-
-  public deselectRegion(region: any) {
-    this.singleClickRegion.emit(region)
-  }
-}
-
-export function trackRegionBy(index: number, region: any) {
-  return region.labelIndex || region.id
-}
diff --git a/src/atlasComponents/parcellation/regionHierachy/regionHierarchy.style.css b/src/atlasComponents/parcellation/regionHierachy/regionHierarchy.style.css
deleted file mode 100644
index 480bb3e62be1e9d1014bccb4fca0c84340ef0401..0000000000000000000000000000000000000000
--- a/src/atlasComponents/parcellation/regionHierachy/regionHierarchy.style.css
+++ /dev/null
@@ -1,64 +0,0 @@
-
-div[treeContainer]
-{
-  padding:1em;
-  z-index: 3;
-
-  height:20em;
-  overflow-y:auto;
-  overflow-x:hidden;
-
-  /* color:white;
-  background-color:rgba(12,12,12,0.8); */
-}
-
-.flex-basis-33-pc
-{
-  flex-basis: 33%;
-}
-
-.flex-basis-auto
-{
-  flex-basis: auto;
-}
-
-input[type="text"]
-{
-  border:none;
-}
-
-
-.regionSearch
-{
-  width:20em;
-}
-
-.tree-body
-{
-  flex: 1 1 auto;
-}
-
-:host
-{
-  display: flex;
-  flex-direction: column;
-}
-
-:host > mat-form-field
-{
-  flex: 0 0 auto;
-}
-
-.horizontal-mode .cdk-viewport-wrapper
-{
-  display: flex;
-  flex-direction: row;
-  flex-wrap: nowrap;
-
-  height:4rem;
-}
-
-.cdk-virtual-scroll-viewport-container.horizontal-mode
-{
-  height: 6rem;
-}
\ No newline at end of file
diff --git a/src/atlasComponents/parcellation/regionHierachy/regionHierarchy.template.html b/src/atlasComponents/parcellation/regionHierachy/regionHierarchy.template.html
deleted file mode 100644
index f78b046b16046e4a0cc50c49a59031eb5811a163..0000000000000000000000000000000000000000
--- a/src/atlasComponents/parcellation/regionHierachy/regionHierarchy.template.html
+++ /dev/null
@@ -1,41 +0,0 @@
-<mat-form-field class="w-100">
-  <input 
-    #searchTermInput
-    matInput
-    (keydown.esc)="escape($event)"
-    (focus)="showRegionTree = true"
-    [value]="searchTerm"
-    type="text" 
-    autocomplete="off"
-    [placeholder]="placeHolderText"/>
-</mat-form-field>
-
-<div
-  [ngClass]="{'flex-column': useMobileUI, 'flex-row': !useMobileUI}"
-  class="d-flex flex-grow-1 flex-shrink-1">
-
-
-  <!-- region tree -->
-  <div class="flex-grow-1 flex-shrink-1 overflow-hidden">
-    <div
-      class="d-flex flex-column h-100"
-      treeContainer
-      hideScrollbarInnerContainer
-      #treeContainer>
-      
-      <div
-        *ngIf="parcellationSelected && parcellationSelected.regions as regions"
-        class="tree-body">
-        <flat-tree-component
-          (treeNodeClick)="handleClickRegion($event)"
-          (totalRenderedListChanged)="handleTotalRenderedListChanged($event)"
-          [inputItem]="aggregatedRegionTree"
-          [renderNode]="displayTreeNode"
-          [searchFilter]="filterTreeBySearch">
-          
-        </flat-tree-component>
-      </div> 
-    </div>
-  </div>
-</div>
-  
\ No newline at end of file
diff --git a/src/atlasComponents/parcellation/regionSearch/regionSearch.component.ts b/src/atlasComponents/parcellation/regionSearch/regionSearch.component.ts
deleted file mode 100644
index 6ff6301a2f59a77a959774d31299a679c317bed1..0000000000000000000000000000000000000000
--- a/src/atlasComponents/parcellation/regionSearch/regionSearch.component.ts
+++ /dev/null
@@ -1,251 +0,0 @@
-import { ChangeDetectionStrategy, Component, ElementRef, EventEmitter, Input, Output, TemplateRef, ViewChild } from "@angular/core";
-import { FormControl } from "@angular/forms";
-import { select, Store } from "@ngrx/store";
-import { combineLatest, Observable, Subject, merge } from "rxjs";
-import { debounceTime, distinctUntilChanged, filter, map, shareReplay, startWith, take, tap, withLatestFrom } from "rxjs/operators";
-import { VIEWER_STATE_ACTION_TYPES } from "src/services/effect/effect";
-import { CHANGE_NAVIGATION, SELECT_REGIONS } from "src/services/state/viewerState.store";
-import { getMultiNgIdsRegionsLabelIndexMap } from "src/services/stateStore.service";
-import { LoggingService } from "src/logging";
-import { MatDialog } from "@angular/material/dialog";
-import { MatAutocompleteSelectedEvent } from "@angular/material/autocomplete";
-import { PureContantService } from "src/util";
-import { viewerStateToggleRegionSelect, viewerStateNavigateToRegion, viewerStateSetSelectedRegions, viewerStateSetSelectedRegionsWithIds } from "src/services/state/viewerState.store.helper";
-import { ARIA_LABELS, CONST } from 'common/constants'
-import { serialiseParcellationRegion } from "common/util"
-import { actionAddToRegionsSelectionWithIds } from "src/services/state/viewerState/actions";
-
-const filterRegionBasedOnText = searchTerm => region => `${region.name.toLowerCase()}${region.status? ' (' + region.status + ')' : null}`.includes(searchTerm.toLowerCase())
-  || (region.relatedAreas && region.relatedAreas.some(relatedArea => relatedArea.name && relatedArea.name.toLowerCase().includes(searchTerm.toLowerCase())))
-
-const compareFn = (it, item) => it.name === item.name && it.status === item.status
-
-@Component({
-  selector: 'region-text-search-autocomplete',
-  templateUrl: './regionSearch.template.html',
-  styleUrls: [
-    './regionSearch.style.css',
-  ],
-  changeDetection: ChangeDetectionStrategy.OnPush,
-})
-
-export class RegionTextSearchAutocomplete {
-
-  public renderInputText(regionsSelected: any[]){
-    if (!regionsSelected) return ''
-    if (regionsSelected.length === 0) return ''
-    if (regionsSelected.length === 1) return regionsSelected[0].name || ''
-    return CONST.MULTI_REGION_SELECTION
-  }
-
-  public manualRenderList$: Subject<any> = new Subject()
-
-  public compareFn = compareFn
-
-  public CLEAR_SELECTED_REGION = ARIA_LABELS.CLEAR_SELECTED_REGION
-
-  @Input() public ariaLabel: string = ARIA_LABELS.TEXT_INPUT_SEARCH_REGION
-  @Input() public showBadge: boolean = false
-  @Input() public showAutoComplete: boolean = true
-
-  @ViewChild('regionHierarchyDialog', {read: TemplateRef}) public regionHierarchyDialogTemplate: TemplateRef<any>
-
-  public useMobileUI$: Observable<boolean>
-
-  public selectedRegionLabelIndexSet: Set<string> = new Set()
-
-  constructor(
-    private store$: Store<any>,
-    private dialog: MatDialog,
-    private pureConstantService: PureContantService,
-    private log: LoggingService
-  ) {
-
-    this.useMobileUI$ = this.pureConstantService.useTouchUI$
-
-    const viewerState$ = this.store$.pipe(
-      select('viewerState'),
-      shareReplay(1),
-    )
-
-    this.regionsWithLabelIndex$ = viewerState$.pipe(
-      select('parcellationSelected'),
-      distinctUntilChanged(),
-      filter(p => !!p && p.regions),
-      map(parcellationSelected => {
-        try {
-          const returnArray = []
-          const ngIdMap = getMultiNgIdsRegionsLabelIndexMap(parcellationSelected, { ngId: 'root', relatedAreas: [], fullId: null })
-          for (const [ngId, labelIndexMap] of ngIdMap) {
-            for (const [labelIndex, region] of labelIndexMap) {
-              returnArray.push({
-                ...region,
-                ngId,
-                labelIndex,
-                labelIndexId: serialiseParcellationRegion({ ngId, labelIndex }),
-              })
-            }
-          }
-          return returnArray
-        } catch (e) {
-          this.log.warn(`getMultiNgIdsRegionsLabelIndexMap error`, e)
-          return []
-        }
-      }),
-      shareReplay(1),
-    )
-
-    this.regionsSelected$ = viewerState$.pipe(
-      select('regionsSelected'),
-      distinctUntilChanged(),
-      tap(regions => {
-        const arrLabelIndexId = regions.map(({ ngId, labelIndex }) => serialiseParcellationRegion({ ngId, labelIndex }))
-        this.selectedRegionLabelIndexSet = new Set(arrLabelIndexId)
-      }),
-      startWith([]),
-      shareReplay(1),
-    )
-
-    this.autocompleteList$ = combineLatest(
-      merge(
-        this.manualRenderList$.pipe(
-          withLatestFrom(this.regionsSelected$),
-          map(([_, selectedRegions]) => this.renderInputText(selectedRegions)),
-          startWith('')
-        ),
-        this.formControl.valueChanges.pipe(
-          startWith(''),
-          distinctUntilChanged(),
-          debounceTime(200),
-        )
-      ),
-      this.regionsWithLabelIndex$.pipe(
-        startWith([]),
-      ),
-    ).pipe(
-      map(([searchTerm, regionsWithLabelIndex]) => regionsWithLabelIndex.filter(filterRegionBasedOnText(searchTerm))),
-      map(arr => arr.slice(0, 5)),
-    )
-
-    this.parcellationSelected$ = viewerState$.pipe(
-      select('parcellationSelected'),
-      distinctUntilChanged(),
-      shareReplay(1),
-    )
-  }
-
-  public toggleRegionWithId(id: string, removeFlag= false) {
-    if (removeFlag) {
-      this.store$.dispatch({
-        type: VIEWER_STATE_ACTION_TYPES.DESELECT_REGIONS_WITH_ID,
-        deselecRegionIds: [id],
-      })
-    } else {
-      this.store$.dispatch(
-        actionAddToRegionsSelectionWithIds({
-          selectRegionIds : [id],
-        })
-      )
-    }
-  }
-
-  public navigateTo(position) {
-    this.store$.dispatch({
-      type: CHANGE_NAVIGATION,
-      navigation: {
-        position,
-        animation: {},
-      },
-    })
-  }
-
-  public optionSelected(_ev?: MatAutocompleteSelectedEvent) {
-    this.store$.dispatch(
-      viewerStateSetSelectedRegionsWithIds({
-        selectRegionIds: _ev ? [ _ev.option.value ] : []
-      })
-    )
-  }
-
-  private regionsWithLabelIndex$: Observable<any[]>
-  public autocompleteList$: Observable<any[]>
-  public formControl = new FormControl()
-
-  public filterNullFn(item: any){
-    return !!item
-  }
-  public regionsSelected$: Observable<any>
-  public parcellationSelected$: Observable<any>
-
-  @Output()
-  public focusedStateChanged: EventEmitter<boolean> = new EventEmitter()
-
-  private _focused: boolean = false
-  set focused(val: boolean) {
-    this._focused = val
-    this.focusedStateChanged.emit(val)
-  }
-  get focused() {
-    return this._focused
-  }
-
-  public deselectAllRegions(_event: MouseEvent) {
-    this.store$.dispatch({
-      type: SELECT_REGIONS,
-      selectRegions: [],
-    })
-  }
-
-  // TODO handle mobile
-  public handleRegionClick({ mode = null, region = null } = {}) {
-    if (mode === 'single') {
-      this.store$.dispatch(
-        viewerStateToggleRegionSelect({
-          payload: { region }
-        })
-      )
-    }
-    if (mode === 'double') {
-      this.store$.dispatch(
-        viewerStateNavigateToRegion({
-          payload: { region }
-        })
-      )
-    }
-  }
-
-  public showHierarchy(_event: MouseEvent) {
-    // mat-card-content has a max height of 65vh
-    const dialog = this.dialog.open(this.regionHierarchyDialogTemplate, {
-      height: '65vh',
-      panelClass: [
-        'col-10',
-        'col-sm-10',
-        'col-md-8',
-        'col-lg-8',
-        'col-xl-6',
-      ],
-    })
-
-    /**
-     * keep sleight of hand shown while modal is shown
-     *
-     */
-    this.focused = true
-
-    /**
-     * take 1 to avoid memory leak
-     */
-    dialog.afterClosed().pipe(
-      take(1),
-    ).subscribe(() => this.focused = false)
-  }
-
-  public deselectAndSelectRegion(region: any) {
-    this.store$.dispatch(
-      viewerStateSetSelectedRegions({
-        selectRegions: [ region ]
-      })
-    )
-  }
-}
diff --git a/src/atlasComponents/parcellation/regionSearch/regionSearch.style.css b/src/atlasComponents/parcellation/regionSearch/regionSearch.style.css
deleted file mode 100644
index 697cf662c0ed5ca19e9ccce62972112a79905000..0000000000000000000000000000000000000000
--- a/src/atlasComponents/parcellation/regionSearch/regionSearch.style.css
+++ /dev/null
@@ -1,9 +0,0 @@
-region-hierarchy
-{
-  height: 100%;
-}
-
-.regionAutocompleteOption
-{
-  height:38px;
-}
diff --git a/src/atlasComponents/parcellation/regionSearch/regionSearch.template.html b/src/atlasComponents/parcellation/regionSearch/regionSearch.template.html
deleted file mode 100644
index 965cc3c65d508042857b48f489afaf00ee6e2364..0000000000000000000000000000000000000000
--- a/src/atlasComponents/parcellation/regionSearch/regionSearch.template.html
+++ /dev/null
@@ -1,84 +0,0 @@
-<div class="w-100 d-inline-flex flex-row align-items-center">
-
-  <form *ngIf="showAutoComplete" class="d-flex flex-row flex-nowrap flex-grow-1 flex-shrink-1">
-    <mat-form-field class="w-0 flex-grow-1 flex-shrink-1"
-      floatLabel="never">
-      <input
-        placeholder="Search for regions"
-        [value]="renderInputText(regionsSelected$ | async)"
-        #trigger="matAutocompleteTrigger"
-        type="text"
-        (focus)="manualRenderList$.next()"
-        matInput
-        [attr.aria-label]="ariaLabel"
-        [formControl]="formControl"
-        [matAutocomplete]="auto">
-
-      <!-- close region selection -->
-      <button *ngIf="(regionsSelected$ | async)?.length > 0"
-        mat-icon-button
-        [attr.aria-label]="CLEAR_SELECTED_REGION"
-        (click)="optionSelected()"  
-        matSuffix>
-        <i class="fas fa-times"></i>
-      </button>
-
-    </mat-form-field>
-    <mat-autocomplete
-      (opened)="focused = true"
-      (closed)="focused = false"
-      panelWidth="auto"
-      (optionSelected)="optionSelected($event)"
-      autoActiveFirstOption
-      #auto="matAutocomplete">
-      <mat-option
-        class="regionAutocompleteOption"
-        *ngFor="let region of autocompleteList$ | async"
-        [value]="region.labelIndexId">
-
-        <simple-region
-          [region]="region"
-          [isSelected]="regionsSelected$ | async | includes : region : compareFn">
-
-        </simple-region>
-      </mat-option>
-    </mat-autocomplete>
-  </form>
-  <!-- region hierarchy -->
-  <button
-    matBadgeColor="accent"
-    [matBadge]="showBadge && (regionsSelected$ | async).length ? (regionsSelected$ | async).length : null"
-    class="flex-grow-0 flex-shrink-0"
-    (click)="showHierarchy($event)"
-    mat-icon-button
-    color="primary">
-    <i class="fas fa-sitemap"></i>
-  </button>
-</div>
-
-<ng-template #regionHierarchyDialog>
-  <div class="h-100 d-flex flex-column">
-    <mat-dialog-content class="flex-grow-1 flex-shrink-1">
-      <ng-container *ngTemplateOutlet="regionHierarchy">
-      </ng-container>
-    </mat-dialog-content>
-  
-    <mat-dialog-actions class="justify-content-center">
-      <button mat-dialog-close mat-flat-button>
-        close
-      </button>
-    </mat-dialog-actions>
-  </div>
-</ng-template>
-
-<ng-template #regionHierarchy>
-  <region-hierarchy
-    [useMobileUI]="useMobileUI$ | async"
-    [selectedRegions]="regionsSelected$ | async | filterArray : filterNullFn"
-    (singleClickRegion)="handleRegionClick({ mode: 'single', region: $event })"
-    (doubleClickRegion)="handleRegionClick({ mode: 'double', region: $event })"
-    (clearAllRegions)="deselectAllRegions($event)"
-    [parcellationSelected]="parcellationSelected$ | async">
-  
-  </region-hierarchy>
-</ng-template>
diff --git a/src/atlasComponents/parcellationRegion/index.ts b/src/atlasComponents/parcellationRegion/index.ts
deleted file mode 100644
index cced6b8edf56fd9c723124704267e3f5fd11e722..0000000000000000000000000000000000000000
--- a/src/atlasComponents/parcellationRegion/index.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-export {
-  ParcellationRegionModule,
-} from "./module"
-
-export { RegionDirective } from "./region.directive";
-export { RegionMenuComponent } from "./regionMenu/regionMenu.component";
-export { SimpleRegionComponent } from "./regionSimple/regionSimple.component";
-export { RenderViewOriginDatasetLabelPipe } from "./region.base";
\ No newline at end of file
diff --git a/src/atlasComponents/parcellationRegion/module.ts b/src/atlasComponents/parcellationRegion/module.ts
deleted file mode 100644
index 03aceeb2d6353c60a3bdb08a634fbbd87a4199d8..0000000000000000000000000000000000000000
--- a/src/atlasComponents/parcellationRegion/module.ts
+++ /dev/null
@@ -1,48 +0,0 @@
-import { CommonModule } from "@angular/common";
-import { NgModule } from "@angular/core";
-import { ComponentsModule } from "src/components";
-import { AngularMaterialModule } from "src/sharedModules";
-import { UtilModule } from "src/util";
-import { RenderViewOriginDatasetLabelPipe } from "./region.base";
-import { RegionDirective } from "./region.directive";
-import { RegionMenuComponent } from "./regionMenu/regionMenu.component";
-import { SimpleRegionComponent } from "./regionSimple/regionSimple.component";
-import { BSFeatureModule } from "../regionalFeatures/bsFeatures";
-import { RegionAccordionTooltipTextPipe } from "./regionAccordionTooltipText.pipe";
-import { AtlasCmptConnModule } from "../connectivity";
-import { HttpClientModule } from "@angular/common/http";
-import { RegionInOtherTmplPipe } from "./regionInOtherTmpl.pipe";
-import { SiibraExplorerTemplateModule } from "../template";
-import { KgDatasetModule } from "../regionalFeatures/bsFeatures/kgDataset";
-
-@NgModule({
-  imports: [
-    CommonModule,
-    UtilModule,
-    AngularMaterialModule,
-    ComponentsModule,
-    BSFeatureModule,
-    AtlasCmptConnModule,
-    HttpClientModule,
-    SiibraExplorerTemplateModule,
-    KgDatasetModule,
-  ],
-  declarations: [
-    RegionMenuComponent,
-    SimpleRegionComponent,
-
-    RegionDirective,
-    RenderViewOriginDatasetLabelPipe,
-    RegionAccordionTooltipTextPipe,
-    RegionInOtherTmplPipe,
-  ],
-  exports: [
-    RegionMenuComponent,
-    SimpleRegionComponent,
-
-    RegionDirective,
-    RenderViewOriginDatasetLabelPipe,
-  ]
-})
-
-export class ParcellationRegionModule{}
\ No newline at end of file
diff --git a/src/atlasComponents/parcellationRegion/region.base.spec.ts b/src/atlasComponents/parcellationRegion/region.base.spec.ts
deleted file mode 100644
index 3e4085c70e1df483bd89f47e31dda4c3d167f1da..0000000000000000000000000000000000000000
--- a/src/atlasComponents/parcellationRegion/region.base.spec.ts
+++ /dev/null
@@ -1,335 +0,0 @@
-import { TestBed } from '@angular/core/testing'
-import { MockStore, provideMockStore } from '@ngrx/store/testing'
-import { viewerStateSelectTemplateWithId } from 'src/services/state/viewerState/actions'
-import { RegionBase, getRegionParentParcRefSpace } from './region.base'
-import { TSiibraExRegion } from './type'
-
-// eslint-disable-next-line @typescript-eslint/no-var-requires
-const util = require('common/util')
-
-/**
- * regions
- */
-
-const mr0 = {
-  labelIndex: 1,
-  name: 'mr0',
-  availableIn: [{id: 'fzj/mock/rs/v0.0.0/aaa-bbb'}, {id: 'fzj/mock/rs/v0.0.0/bbb-bbb'}, {id: 'fzj/mock/rs/v0.0.0/ccc-bbb'}],
-  id: {
-    kg: {
-      kgSchema: 'fzj/mock/pr',
-      kgId: 'aaa-bbb'
-    }
-  }
-}
-
-enum EnumParcRegVersion{
-  V1_18 = 'V1_18',
-  V2_4 = "V2_4"
-}
-
-describe('> region.base.ts', () => {
-  describe('> RegionBase', () => {
-    let regionBase: RegionBase
-    let mockStore: MockStore
-    beforeEach(() => {
-      TestBed.configureTestingModule({
-        providers: [
-          provideMockStore()
-        ]
-      })
-      mockStore = TestBed.inject(MockStore)
-      mockStore.overrideSelector(getRegionParentParcRefSpace, { template: null, parcellation: null })
-    })
-    describe('> position', () => {
-      beforeEach(() => {
-        regionBase = new RegionBase(mockStore)
-      })
-      it('> does not populate if position property is absent', () => {
-        regionBase.region = {
-          ...mr0
-        } as any
-        expect(regionBase.position).toBeFalsy()
-      })
-
-      describe('> does not populate if position property is malformed', () => {
-        it('> if region is falsy', () => {
-          regionBase.region = null
-          expect(regionBase.position).toBeFalsy()
-        })
-        it('> if props is falsy', () => {
-          regionBase.region = {
-            ...mr0,
-            props: null
-          } as any
-          expect(regionBase.position).toBeFalsy()
-        })
-        it('> if props.components is falsy', () => {
-          regionBase.region = {
-            ...mr0,
-            props: {
-              components: null
-            }
-          } as any
-          expect(regionBase.position).toBeFalsy()
-        })
-        it('> if props.components[0] is falsy', () => {
-          regionBase.region = {
-            ...mr0,
-            props: {
-              components: []
-            }
-          } as any
-          expect(regionBase.position).toBeFalsy()
-        })
-
-        it('> if props.components[0].centroid is falsy', () => {
-
-          regionBase.region = {
-            ...mr0,
-            props: {
-              components: [{
-                centroid: null
-              }]
-            }
-          } as any
-          expect(regionBase.position).toBeFalsy()
-        })
-      })
-
-      it('> populates if position property is array with length 3 and non NaN element', () => {
-        regionBase.region = {
-          ...mr0,
-          props: {
-            components: [{
-              centroid: [1, 2, 3]
-            }]
-          },
-        } as any
-        expect(regionBase.position).toBeTruthy()
-      })
-    })
-
-    describe('> rgb', () => {
-      let strToRgbSpy: jasmine.Spy
-      let mockStore: MockStore
-      beforeEach(() => {
-        strToRgbSpy = spyOn(util, 'strToRgb')
-        mockStore = TestBed.inject(MockStore)
-        mockStore.overrideSelector(getRegionParentParcRefSpace, { template: null, parcellation: null })
-      })
-
-      afterEach(() => {
-        strToRgbSpy.calls.reset()
-      })
-
-      it('> will take region.rgb if exists', () => {
-        const regionBase = new RegionBase(mockStore)
-        regionBase.region = {
-          rgb: [100, 120, 140]
-        } as any
-        expect(
-          regionBase.rgbString
-        ).toEqual(`rgb(100,120,140)`)
-      })
-
-      it('> if rgb not provided, and labelIndex > 65500, set to white', () => {
-
-        const regionBase = new RegionBase(mockStore)
-        regionBase.region = {
-          labelIndex: 65535
-        } as any
-        expect(
-          regionBase.rgbString
-        ).toEqual(`rgb(255,255,255)`)
-      })
-
-      describe('> if rgb not provided, labelIndex < 65500', () => {
-
-        describe('> arguments for strToRgb', () => {
-          it('> if ngId is defined, use ngId', () => {
-
-            const regionBase = new RegionBase(mockStore)
-            regionBase.region = {
-              ngId: 'foo',
-              name: 'bar',
-              labelIndex: 152
-            } as any
-            expect(strToRgbSpy).toHaveBeenCalledWith(`foo152`)
-          })
-          it('> if ngId is not defined, use name', () => {
-
-            const regionBase = new RegionBase(mockStore)
-            regionBase.region = {
-              name: 'bar',
-              labelIndex: 152
-            } as any
-            expect(strToRgbSpy).toHaveBeenCalledWith(`bar152`)
-          })
-        })
-
-        it('> calls strToRgb, and use return value for rgb', () => {
-          const getRandomNum = () => Math.floor(255*Math.random())
-          const arr = [
-            getRandomNum(),
-            getRandomNum(),
-            getRandomNum()
-          ]
-          strToRgbSpy.and.returnValue(arr)
-          const regionBase = new RegionBase(mockStore)
-          regionBase.region = {
-            foo: 'bar'
-          } as any
-          expect(
-            regionBase.rgbString
-          ).toEqual(`rgb(${arr.join(',')})`)
-        })
-
-        it('> if strToRgb returns falsy, uses fallback', () => {
-
-          strToRgbSpy.and.returnValue(null)
-          const regionBase = new RegionBase(mockStore)
-          regionBase.region = {
-            foo: 'bar'
-          } as any
-          expect(
-            regionBase.rgbString
-          ).toEqual(`rgb(255,200,200)`)
-        })
-      })
-    })
-    describe('> changeView', () => {
-      const fakeTmpl = {
-        '@id': 'faketmplid',
-        name: 'fakeTmpl'
-      }
-      const fakeParc = {
-        '@id': 'fakeparcid',
-        name: 'fakeParc'
-      }
-      beforeEach(() => {
-        regionBase = new RegionBase(mockStore)
-      })
-
-      describe('> [tmp] sameRegion to use transform backend', () => {
-        let dispatchSpy: jasmine.Spy
-
-        beforeEach(() => {
-          dispatchSpy = spyOn(mockStore, 'dispatch')
-        })
-        afterEach(() => {
-          dispatchSpy.calls.reset()
-        })
-
-        it('> calls viewerStateSelectTemplateWithId', () => {
-
-          const partialRegion = {
-            context: {
-              parcellation: fakeParc,
-              atlas: {
-                "@id": '',
-                name: '',
-                parcellations: [],
-                templateSpaces: [fakeTmpl]
-              },
-              template: null
-            }
-          } as Partial<TSiibraExRegion>
-          regionBase.region = partialRegion as any
-          regionBase.changeView(fakeTmpl)
-
-          expect(dispatchSpy).toHaveBeenCalledWith(
-            viewerStateSelectTemplateWithId({
-              payload: {
-                '@id': fakeTmpl['@id']
-              },
-              config: {
-                selectParcellation: {
-                  "@id": fakeParc['@id']
-                }
-              }
-            })
-          )
-        })
-      })
-
-      /**
-       * currently, without position metadata, the navigation is broken
-       * fix changeView to fetch position metadata. If cannot, fallback to spatial backend
-       */
-
-      // describe('> if sameRegion has position attribute', () => {
-      //   let dispatchSpy: jasmine.Spy
-
-      //   beforeEach(() => {
-      //     dispatchSpy = spyOn(mockStore, 'dispatch')
-      //   })
-      //   afterEach(() => {
-      //     dispatchSpy.calls.reset()
-      //   })
-      //   it('> malformed position is not an array > do not pass position', () => {
-
-      //     regionBase.changeView({
-      //       template: fakeTmpl,
-      //       parcellation: fakeParc,
-      //       region: {
-      //         position: 'hello wolrd'
-      //       }
-      //     })
-
-      //     expect(dispatchSpy).toHaveBeenCalledWith(
-      //       viewerStateNewViewer({
-      //         selectTemplate: fakeTmpl,
-      //         selectParcellation: fakeParc,
-      //         navigation: {}
-      //       })
-      //     )
-      //   })
-
-      //   it('> malformed position is an array of incorrect size > do not pass position', () => {
-
-      //     regionBase.changeView({
-      //       template: fakeTmpl,
-      //       parcellation: fakeParc,
-      //       region: {
-      //         position: []
-      //       }
-      //     })
-
-      //     expect(dispatchSpy).toHaveBeenCalledWith(
-      //       viewerStateSelectTemplateWithId({
-      //         payload: {
-      //           '@id': fakeTmpl['@id']
-      //         },
-      //         config: {
-      //           selectParcellation: {
-      //             "@id": fakeParc['@id']
-      //           }
-      //         }
-      //       })
-      //     )
-      //   })
-
-      //   it('> correct position > pass position', () => {
-      //     regionBase.changeView({
-      //       template: fakeTmpl,
-      //       parcellation: fakeParc,
-      //       region: {
-      //         position: [1,2,3]
-      //       }
-      //     })
-
-      //     expect(dispatchSpy).toHaveBeenCalledWith(
-      //       viewerStateNewViewer({
-      //         selectTemplate: fakeTmpl,
-      //         selectParcellation: fakeParc,
-      //         navigation: {
-      //           position: [1,2,3]
-      //         }
-      //       })
-      //     )
-      //   })
-      // })
-    })
-  })
-})
diff --git a/src/atlasComponents/parcellationRegion/region.base.ts b/src/atlasComponents/parcellationRegion/region.base.ts
deleted file mode 100644
index 5f4b7d327e4e1432abb128187ec7c738b6a1bd29..0000000000000000000000000000000000000000
--- a/src/atlasComponents/parcellationRegion/region.base.ts
+++ /dev/null
@@ -1,228 +0,0 @@
-import { Directive, EventEmitter, Input, Output, Pipe, PipeTransform } from "@angular/core";
-import { select, Store, createSelector } from "@ngrx/store";
-import { uiStateOpenSidePanel, uiStateExpandSidePanel, uiActionShowSidePanelConnectivity } from 'src/services/state/uiState.store.helper'
-import { map, tap } from "rxjs/operators";
-import { Observable, BehaviorSubject, combineLatest } from "rxjs";
-import { rgbToHsl } from 'common/util'
-import { viewerStateSetConnectivityRegion, viewerStateNavigateToRegion, viewerStateToggleRegionSelect, viewerStateSelectTemplateWithId } from "src/services/state/viewerState.store.helper";
-import { viewerStateGetSelectedAtlas, viewerStateSelectedTemplatePureSelector } from "src/services/state/viewerState/selectors";
-import { strToRgb, verifyPositionArg } from 'common/util'
-import { getPosFromRegion } from "src/util/siibraApiConstants/fn";
-import { TRegionDetail } from "src/util/siibraApiConstants/types";
-import { IHasId } from "src/util/interfaces";
-import { TSiibraExTemplate } from "./type";
-
-@Directive()
-export class RegionBase {
-
-  public rgbString: string
-  public rgbDarkmode: boolean
-
-  private _region: TRegionDetail & {  
-    context?: {
-      atlas: IHasId
-      template: IHasId
-      parcellation: IHasId
-    }
-    ngId?: string
-  }
-
-  private _position: [number, number, number]
-  set position(val){
-    if (verifyPositionArg(val)) {
-      this._position = val
-    } else {
-      this._position = null
-    }
-  }
-
-  get position(){
-    return this._position
-  }
-
-  @Input()
-  set region(val) {
-    this._region = val
-    this.region$.next(this._region)
-    this.hasContext$.next(!!this._region?.context)
-
-    this.position = null
-    // bug the centroid returned is currently nonsense
-    // this.position = val?.props?.centroid_mm
-    if (!this._region) return
-    const pos = getPosFromRegion(val)
-    if (pos) {
-      this.position = pos
-    }
-
-    const rgb = this._region.rgb
-      || (this._region.labelIndex > 65500 && [255, 255, 255])
-      || strToRgb(`${this._region.ngId || this._region.name}${this._region.labelIndex}`)
-      || [255, 200, 200]
-
-    this.rgbString = `rgb(${rgb.join(',')})`
-    const [_h, _s, l] = rgbToHsl(...rgb)
-    this.rgbDarkmode = l < 0.4
-  }
-
-  get region(){
-    return this._region
-  }
-
-  get originDatainfos(){
-    if (!this._region) return []
-    return (this._region._dataset_specs || []).filter(spec => spec['@type'] === 'minds/core/dataset/v1.0.0')
-  }
-
-  public hasContext$: BehaviorSubject<boolean> = new BehaviorSubject(false)
-  public region$: BehaviorSubject<any> = new BehaviorSubject(null)
-
-  @Input()
-  public isSelected: boolean = false
-
-  @Input() public hasConnectivity: boolean
-
-  @Output() public closeRegionMenu: EventEmitter<boolean> = new EventEmitter()
-
-  public regionOriginDatasetLabels$: Observable<{ name: string }[]>
-  public selectedAtlas$: Observable<any> = this.store$.pipe(
-    select(viewerStateGetSelectedAtlas)
-  )
-
-
-  constructor(
-    private store$: Store<any>,
-  ) {
-
-    this.regionOriginDatasetLabels$ = combineLatest([
-      this.store$,
-      this.region$
-    ]).pipe(
-      map(([state, region]) => getRegionParentParcRefSpace(state, { region })),
-      map(({ template }) => (template && template.originalDatasetFormats) || [])
-    )
-  }
-
-  public selectedTemplate$ = this.store$.pipe(
-    select(viewerStateSelectedTemplatePureSelector),
-  )
-
-  public navigateToRegion() {
-    this.closeRegionMenu.emit()
-    const { region } = this
-    this.store$.dispatch(
-      viewerStateNavigateToRegion({ payload: { region } })
-    )
-  }
-
-  public toggleRegionSelected() {
-    this.closeRegionMenu.emit()
-    const { region } = this
-    this.store$.dispatch(
-      viewerStateToggleRegionSelect({ payload: { region } })
-    )
-  }
-
-  public showConnectivity(regionName) {
-    this.closeRegionMenu.emit()
-    // ToDo trigger side panel opening with effect
-    this.store$.dispatch(uiStateOpenSidePanel())
-    this.store$.dispatch(uiStateExpandSidePanel())
-    this.store$.dispatch(uiActionShowSidePanelConnectivity())
-
-    this.store$.dispatch(
-      viewerStateSetConnectivityRegion({ connectivityRegion: regionName })
-    )
-  }
-
-  changeView(template: TSiibraExTemplate) {
-
-    this.closeRegionMenu.emit()
-
-    const {
-      parcellation
-    } = (this.region?.context || {})
-    
-    /**
-     * TODO use createAction in future
-     * for now, not importing const because it breaks tests
-     */
-    this.store$.dispatch(
-      viewerStateSelectTemplateWithId({
-        payload: {
-          '@id': template['@id'] || template['fullId']
-        },
-        config: {
-          selectParcellation: {
-            '@id': parcellation['@id'] || parcellation['fullId']
-          }
-        }
-      })
-    )
-  }
-}
-
-export const getRegionParentParcRefSpace = createSelector(
-  (state: any) => state.viewerState,
-  viewerStateGetSelectedAtlas,
-  (viewerState, selectedAtlas, prop) => {
-    const { region: regionOfInterest } = prop
-    /**
-     * if region is undefined, return null
-     */
-    if (!regionOfInterest || !viewerState.parcellationSelected || !viewerState.templateSelected) {
-      return {
-        template: null,
-        parcellation: null
-      }
-    }
-    /**
-     * first check if the region can be found in the currently selected parcellation
-     */
-    const checkRegions = regions => {
-      for (const region of regions) {
-
-        /**
-         * check ROI to iterating regions
-         */
-        if (region.name === regionOfInterest.name) return true
-
-        if (region && region.children && Array.isArray(region.children)) {
-          const flag = checkRegions(region.children)
-          if (flag) return true
-        }
-      }
-      return false
-    }
-    const regionInParcSelected = checkRegions(viewerState.parcellationSelected.regions)
-
-    if (regionInParcSelected) {
-      const p = selectedAtlas.parcellations.find(p => p['@id'] === viewerState.parcellationSelected['@id'])
-      if (p) {
-        const refSpace = p.availableIn.find(refSpace => refSpace['@id'] === viewerState.templateSelected['@id'])
-        return {
-          template: refSpace,
-          parcellation: p
-        }
-      }
-    }
-
-    return {
-      template: null,
-      parcellation: null
-    }
-  }
-)
-
-@Pipe({
-  name: 'renderViewOriginDatasetlabel'
-})
-
-export class RenderViewOriginDatasetLabelPipe implements PipeTransform{
-  public transform(originDatasetlabels: { name: string }[], index: string|number){
-    if (!!originDatasetlabels && !!originDatasetlabels[index] && !!originDatasetlabels[index].name) {
-      return `${originDatasetlabels[index]['name']}`
-    }
-    return `origin dataset`
-  }
-}
diff --git a/src/atlasComponents/parcellationRegion/region.directive.ts b/src/atlasComponents/parcellationRegion/region.directive.ts
deleted file mode 100644
index fb66743eb6ec06a570b9c91223476d44695d1748..0000000000000000000000000000000000000000
--- a/src/atlasComponents/parcellationRegion/region.directive.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import { Directive } from "@angular/core";
-import { RegionBase } from "./region.base";
-import { Store } from "@ngrx/store";
-
-@Directive({
-  selector: '[iav-region]',
-  exportAs: 'iavRegion'
-})
-
-export class RegionDirective extends RegionBase{
-  constructor(store: Store<any>){
-    super(store)
-  }
-}
diff --git a/src/atlasComponents/parcellationRegion/regionAccordionTooltipText.pipe.ts b/src/atlasComponents/parcellationRegion/regionAccordionTooltipText.pipe.ts
deleted file mode 100644
index c2a92b83de75ee5660c0d59a60e19f561581bf31..0000000000000000000000000000000000000000
--- a/src/atlasComponents/parcellationRegion/regionAccordionTooltipText.pipe.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import { Pipe, PipeTransform } from "@angular/core"
-
-@Pipe({
-  name: 'regionAccordionTooltipTextPipe',
-  pure: true
-})
-
-export class RegionAccordionTooltipTextPipe implements PipeTransform{
-
-  public transform(length: number, type: string): string{
-    switch (type) {
-    case 'regionInOtherTmpl': return `Region available in ${length} other reference space${length > 1 ? 's' : ''}`
-    case 'regionalFeatures': return `${length} regional feature${length > 1 ? 's' : ''} found`
-    case 'connectivity': return `${length} connections found`
-    default: return `${length} items found`
-    }
-  }
-}
diff --git a/src/atlasComponents/parcellationRegion/regionInOtherTmpl.pipe.ts b/src/atlasComponents/parcellationRegion/regionInOtherTmpl.pipe.ts
deleted file mode 100644
index 3a0e81dc8ee756aa29b6112e1ebab77987f33ed5..0000000000000000000000000000000000000000
--- a/src/atlasComponents/parcellationRegion/regionInOtherTmpl.pipe.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import { Pipe, PipeTransform } from "@angular/core";
-import { TSiibraExRegion } from "./type";
-
-@Pipe({
-  name: 'regionInOtherTmpl',
-  pure: true
-})
-
-export class RegionInOtherTmplPipe implements PipeTransform{
-  public transform(region: TSiibraExRegion){
-    const { templateSpaces: allTmpl = [] } = region?.context?.atlas || {}
-    return allTmpl.filter(t => (region?.availableIn || []).find(availTmpl => availTmpl['id'] === t["@id"]))
-  }
-}
diff --git a/src/atlasComponents/parcellationRegion/regionMenu/regionMenu.component.spec.ts b/src/atlasComponents/parcellationRegion/regionMenu/regionMenu.component.spec.ts
deleted file mode 100644
index 2c9d299636b1aab010b6af672210079c28611d87..0000000000000000000000000000000000000000
--- a/src/atlasComponents/parcellationRegion/regionMenu/regionMenu.component.spec.ts
+++ /dev/null
@@ -1,113 +0,0 @@
-import { TestBed, async } from "@angular/core/testing"
-import { RegionMenuComponent } from "./regionMenu.component"
-import { AngularMaterialModule } from "src/sharedModules"
-import { UtilModule } from "src/util/util.module"
-import { CommonModule } from "@angular/common"
-import { provideMockStore } from "@ngrx/store/testing"
-import { Component, Directive, Input } from "@angular/core"
-import { NoopAnimationsModule } from "@angular/platform-browser/animations"
-import { ComponentsModule } from "src/components"
-import { ParcellationRegionModule } from "../module"
-import { BS_ENDPOINT } from "src/util/constants"
-
-const mt0 = {
-  name: 'mt0'
-}
-
-const mt1 = {
-  name: 'mt1'
-}
-
-const mr0 = {
-  name: 'mr0'
-}
-
-const mr1 = {
-  name: 'mr1'
-}
-
-const mp0 = {
-  name: 'mp0'
-}
-
-const mp1 = {
-  name: 'mp1'
-}
-
-const mrm0 = {
-  template: mt0,
-  parcellation: mp0,
-  region: mr0
-}
-
-const mrm1 = {
-  template: mt1,
-  parcellation: mp1,
-  region: mr1
-}
-
-const hemisphereMrms = [ {
-  ...mrm0,
-  hemisphere: 'left hemisphere'
-}, {
-  ...mrm1,
-  hemisphere: 'left hemisphere'
-} ]
-
-const nohemisphereHrms = [mrm0, mrm1]
-
-@Component({
-  selector: 'kg-regional-features-list',
-  template: ''
-})
-
-class DummyKgRegionalFeatureList{}
-
-@Directive({
-  selector: '[kg-regional-features-list-directive]',
-  exportAs: 'kgRegionalFeaturesListDirective'
-})
-
-class DummySingleDatasetDirective{
-  @Input()
-  region: string
-
-}
-
-describe('> regionMenu.component.ts', () => {
-  describe('> RegionMenuComponent', () => {
-    beforeEach(async(() => {
-
-      TestBed.configureTestingModule({
-        imports: [
-          UtilModule,
-          AngularMaterialModule,
-          CommonModule,
-          NoopAnimationsModule,
-          ComponentsModule,
-          ParcellationRegionModule,
-        ],
-        declarations: [
-          /**
-           * Used by regionMenu.template.html to show region preview
-           */
-          DummySingleDatasetDirective,
-          DummyKgRegionalFeatureList,
-        ],
-        providers: [
-          provideMockStore({ initialState: {} }),
-          {
-            provide: BS_ENDPOINT,
-            useValue: 'http://example.dev/1_0'
-          }
-        ]
-      }).compileComponents()
-      
-    }))
-
-    it('> init just fine', () => {
-      const fixture = TestBed.createComponent(RegionMenuComponent)
-      expect(fixture).toBeTruthy()
-    })
-  })
-})
\ No newline at end of file
diff --git a/src/atlasComponents/parcellationRegion/regionMenu/regionMenu.component.ts b/src/atlasComponents/parcellationRegion/regionMenu/regionMenu.component.ts
deleted file mode 100644
index f6456eba6b90135ae35ea96b110834093b46a4f8..0000000000000000000000000000000000000000
--- a/src/atlasComponents/parcellationRegion/regionMenu/regionMenu.component.ts
+++ /dev/null
@@ -1,87 +0,0 @@
-import { Component, OnDestroy } from "@angular/core";
-import { Store } from "@ngrx/store";
-import { merge, Observable, Subject, Subscription } from "rxjs";
-import { RegionBase } from '../region.base'
-import { CONST, ARIA_LABELS } from 'common/constants'
-import { ComponentStore } from "src/viewerModule/componentStore";
-import { distinctUntilChanged, mapTo } from "rxjs/operators";
-
-@Component({
-  selector: 'region-menu',
-  templateUrl: './regionMenu.template.html',
-  styleUrls: ['./regionMenu.style.css'],
-  providers: [ ComponentStore ]
-})
-export class RegionMenuComponent extends RegionBase implements OnDestroy {
-
-  public CONST = CONST
-  public ARIA_LABELS = ARIA_LABELS
-  private subscriptions: Subscription[] = []
-
-  public activePanelTitles$: Observable<string[]>
-  private activePanelTitles: string[] = []
-
-  public intentToChgTmpl$ = new Subject()
-  public lockOtherTmpl$ = merge(
-    this.selectedTemplate$.pipe(
-      mapTo(false)
-    ),
-    this.intentToChgTmpl$.pipe(
-      mapTo(true)
-    )
-  ).pipe(
-    distinctUntilChanged()
-  )
-
-  constructor(
-    store$: Store<any>,
-    private viewerCmpLocalUiStore: ComponentStore<{ activePanelsTitle: string[] }>,
-  ) {
-    super(store$)
-    this.viewerCmpLocalUiStore.setState({
-      activePanelsTitle: []
-    })
-
-    this.activePanelTitles$ = this.viewerCmpLocalUiStore.select(
-      state => state.activePanelsTitle
-    ) as Observable<string[]>
-
-    this.subscriptions.push(
-      this.activePanelTitles$.subscribe(
-        (activePanelTitles: string[]) => this.activePanelTitles = activePanelTitles
-      )
-    )
-  }
-
-  ngOnDestroy(): void {
-    this.subscriptions.forEach(s => s.unsubscribe())
-  }
-
-  handleExpansionPanelClosedEv(title: string){
-    this.viewerCmpLocalUiStore.setState({
-      activePanelsTitle: this.activePanelTitles.filter(n => n !== title)
-    })
-  }
-  handleExpansionPanelAfterExpandEv(title: string){
-    if (this.activePanelTitles.includes(title)) return
-    this.viewerCmpLocalUiStore.setState({
-      activePanelsTitle: [
-        ...this.activePanelTitles,
-        title
-      ]
-    })
-  }
-
-  public busyFlag = false
-  private busyMap = new Map<string, boolean>()
-  handleBusySignal(namespace: string, flag: boolean) {
-    this.busyMap.set(namespace, flag)
-    for (const [_key, val] of this.busyMap.entries()) {
-      if (val) {
-        this.busyFlag = true
-        return
-      }
-    }
-    this.busyFlag = false
-  }
-}
diff --git a/src/atlasComponents/parcellationRegion/regionMenu/regionMenu.style.css b/src/atlasComponents/parcellationRegion/regionMenu/regionMenu.style.css
deleted file mode 100644
index 386e688cd8e1adc6489f109fbadf71148c4d5ccc..0000000000000000000000000000000000000000
--- a/src/atlasComponents/parcellationRegion/regionMenu/regionMenu.style.css
+++ /dev/null
@@ -1,54 +0,0 @@
-mat-icon
-{
-  transform: scale(0.75);
-}
-
-.action-list
-{
-  margin-left: -16px;
-  margin-right: -16px;
-}
-
-.action-list mat-icon
-{
-  padding-left: 0px;
-}
-
-.region-name
-{
-  display: inherit;
-  font-size: 95%;
-  line-height: normal;
-}
-
-:host-context([darktheme="true"]) .loading-overlay
-{
-    background-color: rgba(10, 10, 10, 0.8);
-}
-
-.loading-overlay
-{
-    background-color: rgba(250, 250, 250, 0.8);
-}
-
-.loading-overlay
-{
-    position: absolute;
-    width: 100%;
-    height: 100%;
-    top: 0;
-    left: 0;
-    font-size: 200%;
-
-    display: grid;
-    grid-template-columns: auto;
-    grid-template-rows: 1fr auto 1fr;
-    grid-template-columns: 1fr auto 1fr;
-    grid-template-areas: "." "vertical-center" ".";
-}
-
-.loading-overlay > .spinner
-{
-    grid-column: 2;
-    grid-row: 2;
-}
diff --git a/src/atlasComponents/parcellationRegion/regionMenu/regionMenu.template.html b/src/atlasComponents/parcellationRegion/regionMenu/regionMenu.template.html
deleted file mode 100644
index 55539c2c1993146b799f90acc9555d8456c1c1ae..0000000000000000000000000000000000000000
--- a/src/atlasComponents/parcellationRegion/regionMenu/regionMenu.template.html
+++ /dev/null
@@ -1,239 +0,0 @@
-<ng-template [ngIf]="region">
-
-<mat-card class="mat-elevation-z4">
-  <!-- rgbDarkmode must be checked for strict equality to true/false 
-  as if rgb is undefined, rgbDarkmode will be null/undefined
-  which is falsy -->
-  <div class="sidenav-cover-header-container"
-    [ngClass]="{'darktheme': rgbDarkmode === true, 'lighttheme': rgbDarkmode === false}"
-    [style.backgroundColor]="rgbString">
-    <mat-card-title>
-      <div class="position-relative region-name iv-custom-comp text">
-        {{ region.name }}
-      </div>
-    </mat-card-title>
-
-    <!-- subtitle on what it is -->
-    <mat-card-subtitle class="d-inline-flex align-items-center flex-wrap">
-      <mat-icon fontSet="fas" fontIcon="fa-brain"></mat-icon>
-      <span>
-        Brain region
-      </span>
-
-      <!-- origin datas format -->
-      
-      <mat-divider vertical="true" class="ml-2 h-2rem"></mat-divider>
-
-      <!-- position -->
-      <button mat-icon-button *ngIf="position"
-        (click)="navigateToRegion()"
-        [matTooltip]="ARIA_LABELS.GO_TO_REGION_CENTROID + ': ' + (position | nmToMm | addUnitAndJoin : 'mm')">
-        <mat-icon fontSet="fas" fontIcon="fa-map-marked-alt">
-        </mat-icon>
-      </button>
-
-      <!-- explore doi -->
-      <ng-template let-infos [ngIf]="originDatainfos">
-        <ng-container *ngFor="let info of infos">
-          <a *ngFor="let url of info.urls"
-            [href]="url.doi | doiParserPipe"
-            [matTooltip]="ARIA_LABELS.EXPLORE_DATASET_IN_KG"
-            target="_blank"
-            mat-icon-button>
-            <i class="fas fa-external-link-alt"></i>
-          </a>
-        </ng-container>
-      </ng-template>
-
-    </mat-card-subtitle>
-
-  </div>
-</mat-card>
-
-<mat-accordion class="d-block mt-2">
-
-  <!-- description -->
-  <ng-template [ngIf]="(originDatainfos || []).length > 0">
-    <ng-container *ngFor="let originData of originDatainfos">
-      <ng-template #descTmpl>
-        <markdown-dom [markdown]="originData.description"
-          class="text-muted text-sm">
-        </markdown-dom>
-      </ng-template>
-
-      <ng-container *ngTemplateOutlet="ngMatAccordionTmpl; context: {
-        title: 'Description',
-        iconClass: 'fas fa-info',
-        iavNgIf: true,
-        content: descTmpl
-      }">
-
-      </ng-container>
-    </ng-container>
-  </ng-template>
-
-
-  <!-- Explore in other template -->
-
-  <!-- Disabling explore in other templates for now -->
-  <!-- rework before reenable
-  - identify the open todos (bigbrain hemisphere split, navigation to center coordinates in surface viewer, …) until the functionality is working
-  - put it in the development board -->
-  <ng-template [ngIf]="false">
-
-  <ng-template [ngIf]="region$ | async | regionInOtherTmpl" let-otherTmpls>
-    <ng-template #exploreInOtherTmpl>
-      <mat-grid-list cols="3"
-        rowHeight="2:3"
-        gutterSize="16"
-        class="position-relative">
-        <mat-grid-tile *ngFor="let otherTmpl of otherTmpls">
-          
-          <div [hidden]
-              iav-dataset-show-dataset-dialog
-              [iav-dataset-show-dataset-dialog-name]="otherTmpl.originDatainfos[0]?.name"
-              [iav-dataset-show-dataset-dialog-description]="otherTmpl.originDatainfos[0]?.description"
-              [iav-dataset-show-dataset-dialog-urls]="otherTmpl.originDatainfos[0]?.urls"
-              [iav-dataset-show-dataset-dialog-ignore-overwrite]="true"
-              #kgInfo="iavDatasetShowDatasetDialog">
-          </div>
-          <tile-cmp [tile-image-src]="otherTmpl | getTemplatePreviewUrl"
-              class="cursor-pointer pe-all"
-              tile-image-alt="Preview of this tile"
-              [tile-text]="otherTmpl.displayName || otherTmpl.name"
-              [tile-show-info]="otherTmpl.originDatainfos?.length > 0"
-              [tile-image-darktheme]="otherTmpl | templateIsDarkTheme"
-              [tile-selected]="(selectedTemplate$ | async | getProperty : '@id') === otherTmpl['@id']"
-              (tile-on-click)="(tileCmp.selected || changeView(otherTmpl)); (tileCmp.selected || intentToChgTmpl$.next(true))"
-              (tile-on-info-click)="kgInfo && kgInfo.onClick()"
-              #tileCmp="tileCmp">
-          </tile-cmp>
-        </mat-grid-tile>
-      </mat-grid-list>
-
-      <div *ngIf="lockOtherTmpl$ | async" class="loading-overlay">
-        <spinner-cmp class="spinner"></spinner-cmp>
-      </div>
-
-    </ng-template>
-
-    <ng-container *ngTemplateOutlet="ngMatAccordionTmpl; context: {
-      title: 'Explore in other templates',
-      desc: otherTmpls.length,
-      iconClass: 'fas fa-brain',
-      iconTooltip: otherTmpls.length | regionAccordionTooltipTextPipe : 'regionInOtherTmpl',
-      iavNgIf: otherTmpls.length > 0,
-      content: exploreInOtherTmpl
-    }">
-    </ng-container>
-  </ng-template>
-  </ng-template>
-
-
-  <!-- kg regional features list -->
-  <ng-template #kgRegionalFeatureList>
-    <regional-feature-wrapper [region]="region$ | async">
-    </regional-feature-wrapper>
-  </ng-template>
-
-  <ng-container *ngTemplateOutlet="ngMatAccordionTmpl; context: {
-    title: CONST.REGIONAL_FEATURES,
-    iconClass: 'fas fa-database',
-    content: kgRegionalFeatureList,
-    desc: '',
-    iconTooltip: 'Regional Features',
-    iavNgIf: hasContext$ | async
-  }">
-  </ng-container>
-
-  <!-- Connectivity -->
-  
-  <ng-template #connectivityContentTmpl let-expansionPanel="expansionPanel">
-    <mat-card-content class="flex-grow-1 flex-shrink-1 w-100">
-      <connectivity-browser class="pe-all flex-shrink-1"
-        [region]="region"
-        (setOpenState)="expansionPanel.expanded = $event"
-        [accordionExpanded]="expansionPanel.expanded"
-        (connectivityNumberReceived)="hasConnectivityDirective.connectivityNumber = $event">
-      </connectivity-browser>
-    </mat-card-content>
-  </ng-template>
-
-  <ng-container *ngTemplateOutlet="ngMatAccordionTmpl; context: {
-    title: 'Connectivity',
-    desc: hasConnectivityDirective.connectivityNumber,
-    iconClass: 'fas fa-braille',
-    iconTooltip: hasConnectivityDirective.connectivityNumber | regionAccordionTooltipTextPipe : 'connectivity',
-    iavNgIf: hasConnectivityDirective.hasConnectivity,
-    content: connectivityContentTmpl
-  }">
-  </ng-container>
-
-  <div has-connectivity
-    [region]="[region]"
-    #hasConnectivityDirective="hasConnectivityDirective">
-  </div>
-</mat-accordion>
-
-<div *ngIf="busyFlag" class="mt-2 d-flex justify-content-center">
-  <spinner-cmp></spinner-cmp>
-</div>
-
-<!-- expansion tmpl -->
-<ng-template #ngMatAccordionTmpl
-  let-title="title"
-  let-desc="desc"
-  let-iconClass="iconClass"
-  let-iconTooltip="iconTooltip"
-  let-iavNgIf="iavNgIf"
-  let-content="content">
-  <mat-expansion-panel
-    [expanded]="activePanelTitles$ | async | includes : title"
-    [attr.data-opened]="expansionPanel.expanded"
-    [attr.data-mat-expansion-title]="title"
-    (closed)="handleExpansionPanelClosedEv(title)"
-    (afterExpand)="handleExpansionPanelAfterExpandEv(title)"
-    hideToggle
-    *ngIf="iavNgIf"
-    #expansionPanel="matExpansionPanel">
-
-    <mat-expansion-panel-header>
-
-      <!-- title -->
-      <mat-panel-title>
-        {{ title }}
-      </mat-panel-title>
-
-      <!-- desc + icon -->
-      <mat-panel-description class="d-flex align-items-center justify-content-end"
-        [matTooltip]="iconTooltip">
-        <span class="mr-3">{{ desc }}</span>
-        <span class="accordion-icon d-inline-flex justify-content-center">
-          <i [class]="iconClass"></i>
-        </span>
-      </mat-panel-description>
-
-    </mat-expansion-panel-header>
-
-    <!-- content -->
-    <ng-template matExpansionPanelContent>
-      <ng-container *ngTemplateOutlet="content; context: {
-        expansionPanel: expansionPanel
-      }">
-      </ng-container>
-    </ng-template>
-  </mat-expansion-panel>
-</ng-template>
-</ng-template>
-
-
-<ng-template [ngIf]="!region">
-  <mat-card class="mat-elevation-z4">
-    <h1 class="mat-h1 sidenav-cover-header-container">
-      <spinner-cmp class="d-inline-block"></spinner-cmp>
-      <span class="text-muted">
-        Loading region
-      </span>
-    </h1>
-  </mat-card>
-</ng-template>
\ No newline at end of file
diff --git a/src/atlasComponents/parcellationRegion/regionSimple/regionSimple.component.ts b/src/atlasComponents/parcellationRegion/regionSimple/regionSimple.component.ts
deleted file mode 100644
index 76859b9e5341053c37c14c3a57115cc1a6b5f190..0000000000000000000000000000000000000000
--- a/src/atlasComponents/parcellationRegion/regionSimple/regionSimple.component.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-import { Component } from '@angular/core'
-
-import { Store } from '@ngrx/store'
-import { IavRootStoreInterface } from 'src/services/stateStore.service'
-import { RegionBase } from '../region.base'
-
-@Component({
-  selector: 'simple-region',
-  templateUrl: './regionSimple.template.html',
-  styleUrls: [
-    './regionSimple.style.css',
-  ],
-})
-
-export class SimpleRegionComponent extends RegionBase {
-  constructor(
-    store$: Store<IavRootStoreInterface>,
-  ) {
-    super(store$)
-  }
-}
diff --git a/src/atlasComponents/parcellationRegion/regionSimple/regionSimple.template.html b/src/atlasComponents/parcellationRegion/regionSimple/regionSimple.template.html
deleted file mode 100644
index b724ab6f27d2ece74413959934d913d7b48888f5..0000000000000000000000000000000000000000
--- a/src/atlasComponents/parcellationRegion/regionSimple/regionSimple.template.html
+++ /dev/null
@@ -1,26 +0,0 @@
-<div class="d-flex flex-row">
-
-  <small class="text-truncate flex-shrink-1 flex-grow-1">
-    {{ region.name }}
-  </small>
-
-  <div class="flex-grow-0 flex-shrink-0 d-flex flex-row">
-
-    <!-- if has position defined -->
-    <button *ngIf="position"
-      iav-stop="click"
-      (click)="navigateToRegion()"
-      mat-icon-button>
-      <i class="fas fa-map-marked-alt"></i>
-    </button>
-
-    <!-- region selected  -->
-    <button mat-icon-button
-      [color]="isSelected ? 'primary' : 'basic'">
-      <i class="far"
-        [ngClass]="{'fa-check-square': isSelected, 'fa-square': !isSelected}">
-      </i>
-    </button>
-  </div>
-
-</div>
\ No newline at end of file
diff --git a/src/atlasComponents/parcellationRegion/type.ts b/src/atlasComponents/parcellationRegion/type.ts
deleted file mode 100644
index e5c40a75fc1c63e88c92e4e388c56603feef1625..0000000000000000000000000000000000000000
--- a/src/atlasComponents/parcellationRegion/type.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-import { IHasId } from "src/util/interfaces";
-import { TRegionSummary } from "src/util/siibraApiConstants/types";
-
-type TAny = {
-  [key: string]: any
-}
-
-export type TSiibraExTemplate = IHasId & TAny
-export type TSiibraExParcelation = IHasId & TAny
-
-export type TSiibraExAtlas = {
-  name: string
-  '@id': string
-  parcellations: TSiibraExParcelation[]
-  templateSpaces: TSiibraExTemplate[]
-}
-
-export type TSiibraExRegion = TRegionSummary & {
-  context: {
-    atlas: TSiibraExAtlas
-    template: TSiibraExTemplate
-    parcellation: TSiibraExParcelation
-  }
-}
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/bsRegionInputBase.ts b/src/atlasComponents/regionalFeatures/bsFeatures/bsRegionInputBase.ts
deleted file mode 100644
index 85d2e6d005bca99c7d003f148bc6a93fe7e2acb7..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/bsFeatures/bsRegionInputBase.ts
+++ /dev/null
@@ -1,47 +0,0 @@
-import { BehaviorSubject, throwError } from "rxjs";
-import { map, switchMap } from "rxjs/operators";
-import { TRegion, IBSSummaryResponse, IBSDetailResponse } from "./type";
-import { BsFeatureService, TFeatureCmpInput } from "./service";
-import { flattenReducer } from 'common/util'
-import { Directive, Input } from "@angular/core";
-
-@Directive()
-export class BsRegionInputBase{
-
-  protected region$ = new BehaviorSubject<TRegion>(null)
-  private _region: TRegion
-
-  @Input()
-  set region(val: TRegion) {
-    this._region = val
-    this.region$.next(val)
-  }
-
-  get region() {
-    return this._region
-  }
-
-  constructor(
-    private svc: BsFeatureService,
-    data?: TFeatureCmpInput
-  ){
-    if (data) {
-      this.region = data.region
-    }
-  }
-
-  protected featuresList$ = this.region$.pipe(
-    switchMap(region => this.svc.listFeatures(region)),
-    map(result => result.features.map(obj => Object.keys(obj).reduce(flattenReducer, [])))
-  )
-
-  protected getFeatureInstancesList<T extends keyof IBSSummaryResponse>(feature: T){
-    if (!this._region) return throwError('#getFeatureInstancesList region needs to be defined')
-    return this.svc.getFeatures<T>(feature, this._region)
-  }
-
-  protected getFeatureInstance<T extends keyof IBSDetailResponse>(feature: T, id: string) {
-    if (!this._region) return throwError('#getFeatureInstance region needs to be defined')
-    return this.svc.getFeature<T>(feature, this._region, id)
-  }
-}
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/constants.ts b/src/atlasComponents/regionalFeatures/bsFeatures/constants.ts
deleted file mode 100644
index 3746ac94431b1b414a26a5e5fb1d6aa3b46ab0a4..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/bsFeatures/constants.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import { InjectionToken } from "@angular/core";
-import { Observable } from "rxjs";
-export { BS_ENDPOINT } from 'src/util/constants'
-
-export const BS_DARKTHEME = new InjectionToken<Observable<boolean>>('BS_DARKTHEME')
-export const REGISTERED_FEATURE_INJECT_DATA = new InjectionToken('REGISTERED_FEATURE_INJECT_DATA')
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/genericInfo/genericInfoCmp/genericInfo.component.ts b/src/atlasComponents/regionalFeatures/bsFeatures/genericInfo/genericInfoCmp/genericInfo.component.ts
deleted file mode 100644
index ca0d7c57c1c17a4fe011b05d05bd5d1cea13f6f5..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/bsFeatures/genericInfo/genericInfoCmp/genericInfo.component.ts
+++ /dev/null
@@ -1,138 +0,0 @@
-import { AfterViewInit, ChangeDetectorRef, Component, Inject, Input, OnChanges, OnDestroy, Optional, TemplateRef, ViewChild, ViewContainerRef, ViewRef } from "@angular/core";
-import { BsRegionInputBase } from "../../bsRegionInputBase";
-import { KG_REGIONAL_FEATURE_KEY, TBSDetail, UNDER_REVIEW } from "../../kgRegionalFeature/type";
-import { ARIA_LABELS, CONST } from 'common/constants'
-import { TBSSummary } from "../../kgDataset";
-import { BsFeatureService } from "../../service";
-import { MAT_DIALOG_DATA } from "@angular/material/dialog";
-import { TDatainfosDetail } from "src/util/siibraApiConstants/types";
-import { TRegion } from "../../type";
-
-/**
- * this component is specifically used to render side panel ebrains dataset view
- */
-
-export type TInjectableData = TDatainfosDetail & {
-  dataType?: string
-  view?: ViewRef | TemplateRef<any>
-  region?: TRegion
-  summary?: TBSSummary
-  isGdprProtected?: boolean
-}
-
-@Component({
-  selector: 'generic-info-cmp',
-  templateUrl: './genericInfo.template.html',
-  styleUrls: [
-    './genericInfo.style.css'
-  ]
-})
-
-export class GenericInfoCmp extends BsRegionInputBase implements OnChanges, AfterViewInit, OnDestroy {
-
-  public ARIA_LABELS = ARIA_LABELS
-  public CONST = CONST
-
-  @Input()
-  public summary: TBSSummary
-
-  @Input()
-  public detail: TBSDetail
-
-  public loadingFlag = false
-  public error = null
-
-  public nameFallback = `[This dataset cannot be fetched right now]`
-  public isGdprProtected = false
-
-  public descriptionFallback = `[This dataset cannot be fetched right now]`
-  public useClassicUi = false
-  public dataType = 'ebrains regional dataset'
-
-  public description: string
-  public name: string
-  public urls: {
-    cite: string
-    doi: string
-  }[]
-
-  public doiUrls: {
-    cite: string
-    doi: string
-  }[]
-
-  template: TemplateRef<any>
-  viewref: ViewRef
-
-  @ViewChild('insertViewTarget', { read: ViewContainerRef })
-  insertedViewVCR: ViewContainerRef
-
-  constructor(
-    svc: BsFeatureService,
-    private cdr: ChangeDetectorRef,
-    @Optional() @Inject(MAT_DIALOG_DATA) data: TInjectableData
-  ){
-    super(svc)
-    if (data) {
-      const { dataType, description, name, urls, useClassicUi, view, region, summary, isGdprProtected } = data
-      this.description = description
-      this.name = name
-      this.urls = urls || []
-      this.doiUrls = this.urls.filter(d => !!d.doi)
-      this.useClassicUi = useClassicUi
-      if (dataType) this.dataType = dataType
-      if (typeof isGdprProtected !== 'undefined') this.isGdprProtected = isGdprProtected
-
-      if (!!view) {
-        if (view instanceof TemplateRef){
-          this.template = view
-        } else {
-          this.viewref = view
-        }
-      }
-
-      if (region && summary) {
-        this.region = region
-        this.summary = summary
-        this.ngOnChanges()
-      }
-    }
-  }
-
-  ngOnDestroy(){
-    this.insertedViewVCR.clear()
-  }
-
-  ngAfterViewInit(){
-    if (this.insertedViewVCR && this.viewref) {
-      this.insertedViewVCR.insert(this.viewref)
-    }
-  }
-
-  ngOnChanges(){
-    if (!this.region) return
-    if (!this.summary) return
-    if (!!this.detail) return
-    this.loadingFlag = true
-    this.getFeatureInstance(KG_REGIONAL_FEATURE_KEY, this.summary['@id']).subscribe(
-      detail => {
-        this.detail = detail
-
-        this.name = this.detail.src_name
-        this.description = this.detail.__detail?.description
-        this.urls = this.detail.__detail.kgReference.map(url => {
-          return { cite: null, doi: url }
-        })
-        
-        this.isGdprProtected = detail.__detail.embargoStatus && detail.__detail.embargoStatus.some(status => status["@id"] === UNDER_REVIEW["@id"])
-      },
-      err => {
-        this.error = err.toString()
-      },
-      () => {
-        this.loadingFlag = false
-        this.cdr.markForCheck()
-      }
-    )
-  }
-}
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/genericInfo/genericInfoCmp/genericInfo.style.css b/src/atlasComponents/regionalFeatures/bsFeatures/genericInfo/genericInfoCmp/genericInfo.style.css
deleted file mode 100644
index 3e722e3e9997fb043174c54f56a641035c3f1b6d..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/bsFeatures/genericInfo/genericInfoCmp/genericInfo.style.css
+++ /dev/null
@@ -1,5 +0,0 @@
-.desc-container
-{
-  max-height: 35vh;
-  overflow: auto;
-}
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/genericInfo/genericInfoCmp/genericInfo.template.html b/src/atlasComponents/regionalFeatures/bsFeatures/genericInfo/genericInfoCmp/genericInfo.template.html
deleted file mode 100644
index be13818c97aec683681de104eba5f82ea9d0e6d8..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/bsFeatures/genericInfo/genericInfoCmp/genericInfo.template.html
+++ /dev/null
@@ -1,126 +0,0 @@
-
-<!-- classic UI -->
-<ng-template [ngIf]="useClassicUi" [ngIfElse]="modernUi">
-  <mat-card-subtitle>
-    <ng-container *ngTemplateOutlet="nameTmpl">
-    </ng-container>
-  </mat-card-subtitle>
-
-
-  <!-- desc -->
-  <small>
-    <ng-container *ngTemplateOutlet="descTmpl"></ng-container>
-  </small>
-
-  <ng-container *ngTemplateOutlet="insertedView">
-  </ng-container>
-
-  <!-- footer -->
-  <mat-card-actions iav-media-query #iavMediaQuery="iavMediaQuery">
-    <ng-container *ngTemplateOutlet="actionBtnsTmpl; context: {
-      $implicit: (iavMediaQuery.mediaBreakPoint$ | async) > 1
-    }" >
-    </ng-container>
-  </mat-card-actions>
-
-  <ng-template #actionBtnsTmpl let-useSmallIcon>
-    <!-- explore -->
-    <ng-container>
-
-      <a *ngFor="let kgRef of (doiUrls || [])"
-        [href]="kgRef.doi | doiParserPipe"
-        class="color-inherit"
-        [matTooltip]="ARIA_LABELS.EXPLORE_DATASET_IN_KG"
-        target="_blank">
-        <iav-dynamic-mat-button
-          [iav-dynamic-mat-button-style]="useSmallIcon ? 'mat-icon-button' : 'mat-raised-button'"
-          iav-dynamic-mat-button-color="primary">
-
-          <span *ngIf="!useSmallIcon">
-            Explore
-          </span>
-          <i class="fas fa-external-link-alt"></i>
-        </iav-dynamic-mat-button>
-      </a>
-    </ng-container>
-  </ng-template>
-</ng-template>
-
-<!-- modern UI -->
-<ng-template #modernUi>
-  <!-- header -->
-  <mat-card class="mat-elevation-z4">
-
-    <div class="sidenav-cover-header-container bg-50-grey-20">
-      <mat-card-title>
-        <ng-content select="[region-of-interest]"></ng-content>
-        <ng-container *ngTemplateOutlet="nameTmpl">
-        </ng-container>
-      </mat-card-title>
-
-      <mat-card-subtitle class="d-inline-flex align-items-center">
-        <mat-icon fontSet="fas" fontIcon="fa-database"></mat-icon>
-        <span>
-          {{ dataType }}
-        </span>
-
-        <button *ngIf="isGdprProtected"
-          [matTooltip]="CONST.GDPR_TEXT"
-          mat-icon-button color="warn">
-          <i class="fas fa-exclamation-triangle"></i>
-        </button>
-
-        <mat-divider [vertical]="true" class="ml-2 h-2rem"></mat-divider>
-
-        <!-- explore btn -->
-        <a *ngFor="let kgRef of (urls || [])"
-          [href]="kgRef.doi | doiParserPipe"
-          class="color-inherit"
-          mat-icon-button
-          [matTooltip]="ARIA_LABELS.EXPLORE_DATASET_IN_KG"
-          target="_blank">
-          <i class="fas fa-external-link-alt"></i>
-        </a>
-        
-      </mat-card-subtitle>
-    </div>
-
-  </mat-card>
-
-  <!-- description -->
-  <div class="text-muted d-block mat-body m-4 desc-container"
-    *ngIf="!loadingFlag">
-    <ng-container *ngTemplateOutlet="descTmpl">
-    </ng-container>
-  </div>
-
-  <div>
-    <ng-container *ngTemplateOutlet="insertedView">
-    </ng-container>
-  </div>
-</ng-template>
-
-<ng-template #nameTmpl>
-  <span *ngIf="!loadingFlag; else isLoadingTmpl">
-    {{ name || nameFallback }}
-  </span>
-</ng-template>
-
-<!-- desc -->
-<ng-template #descTmpl>
-  <markdown-dom 
-    [markdown]="description || descriptionFallback">
-  </markdown-dom>
-</ng-template>
-
-<!-- inserted view -->
-<ng-template #insertedView>
-  <ng-template #insertViewTarget>
-
-  </ng-template>
-</ng-template>
-
-<!-- is loading tmpl -->
-<ng-template #isLoadingTmpl>
-  <spinner-cmp></spinner-cmp>
-</ng-template>
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/genericInfo/index.ts b/src/atlasComponents/regionalFeatures/bsFeatures/genericInfo/index.ts
deleted file mode 100644
index d38b8440733fc665a74c75e3d9fa574ec90dfe26..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/bsFeatures/genericInfo/index.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-export { GenericInfoModule } from './module'
-export { GenericInfoCmp, TInjectableData } from './genericInfoCmp/genericInfo.component'
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/genericInfo/module.ts b/src/atlasComponents/regionalFeatures/bsFeatures/genericInfo/module.ts
deleted file mode 100644
index 7f1391e10a378a65c95fd3c8261a5d35509106bf..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/bsFeatures/genericInfo/module.ts
+++ /dev/null
@@ -1,65 +0,0 @@
-import { CommonModule } from "@angular/common";
-import { Component, Inject, NgModule, Optional } from "@angular/core";
-import { MAT_DIALOG_DATA } from "@angular/material/dialog";
-import { ComponentsModule } from "src/components";
-import { AngularMaterialModule } from "src/sharedModules";
-import { UtilModule } from "src/util";
-import { IAV_DATASET_SHOW_DATASET_DIALOG_CMP } from "../kgDataset/showDataset/showDataset.directive";
-import { GenericInfoCmp } from "./genericInfoCmp/genericInfo.component";
-
-@Component({
-  selector: 'show-ds-dialog-cmp',
-  template: `
-<ng-template [ngIf]="useClassicUi" [ngIfElse]="modernUiTmpl">
-  <generic-info-cmp></generic-info-cmp>
-</ng-template>
-
-<ng-template #modernUiTmpl>
-
-  <mat-dialog-content class="m-0 p-0">
-    <generic-info-cmp></generic-info-cmp>
-  </mat-dialog-content>
-
-  <mat-dialog-actions align="center">
-    <button mat-button mat-dialog-close>
-      Close
-    </button>
-  </mat-dialog-actions>
-
-</ng-template>
-`
-})
-
-export class ShowDsDialogCmp{
-  public useClassicUi = false
-  constructor(
-    @Optional() @Inject(MAT_DIALOG_DATA) data: any
-  ){
-    this.useClassicUi = data.useClassicUi
-  }
-}
-
-@NgModule({
-  imports: [
-    CommonModule,
-    AngularMaterialModule,
-    UtilModule,
-    ComponentsModule,
-  ],
-  declarations: [
-    GenericInfoCmp,
-    ShowDsDialogCmp,
-  ],
-  exports: [
-    GenericInfoCmp,
-  ],
-
-  providers: [
-    {
-      provide: IAV_DATASET_SHOW_DATASET_DIALOG_CMP,
-      useValue: ShowDsDialogCmp
-    }
-  ]
-})
-
-export class GenericInfoModule{}
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/ieeg/ieegCmp/ieeg.component.ts b/src/atlasComponents/regionalFeatures/bsFeatures/ieeg/ieegCmp/ieeg.component.ts
deleted file mode 100644
index 1694e857d446519312a46d43254edd66323627ee..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/bsFeatures/ieeg/ieegCmp/ieeg.component.ts
+++ /dev/null
@@ -1,151 +0,0 @@
-import { Component, Inject, OnDestroy, Optional } from "@angular/core";
-import { Store } from "@ngrx/store";
-import { BehaviorSubject, forkJoin, merge, Observable, of, Subscription } from "rxjs";
-import { catchError, mapTo, switchMap } from "rxjs/operators";
-import { viewerStateAddUserLandmarks, viewerStateChangeNavigation, viewreStateRemoveUserLandmarks } from "src/services/state/viewerState/actions";
-import { BsRegionInputBase } from "../../bsRegionInputBase";
-import { REGISTERED_FEATURE_INJECT_DATA } from "../../constants";
-import { BsFeatureService, TFeatureCmpInput } from "../../service";
-import { SIIBRA_FEATURE_KEY, TContactPoint, TElectrode, TBSIeegSessionDetail } from '../type'
-import { ARIA_LABELS, CONST } from 'common/constants'
-
-@Component({
-  selector: 'bs-feature-ieeg.cmp',
-  templateUrl: './ieeg.template.html',
-  styleUrls: [
-    './ieeg.style.css'
-  ]
-})
-
-export class BsFeatureIEEGCmp extends BsRegionInputBase implements OnDestroy{
-
-  public ARIA_LABELS = ARIA_LABELS
-  public CONST = CONST
-
-  private featureId: string
-
-  private results: TBSIeegSessionDetail[] = []
-  constructor(
-    private store: Store<any>,
-    svc: BsFeatureService,
-    @Optional() @Inject(REGISTERED_FEATURE_INJECT_DATA) data: TFeatureCmpInput,
-  ){
-    super(svc, data)
-    if (data.featureId) this.featureId = data.featureId
-    this.subs.push(
-      this.results$.subscribe(results => {
-        this.results = results
-        this.loadLandmarks()
-      })
-    )
-  }
-
-  public results$: Observable<TBSIeegSessionDetail[]>  = this.region$.pipe(
-    switchMap(() => this.getFeatureInstancesList(SIIBRA_FEATURE_KEY).pipe(
-      switchMap(arr => forkJoin(arr.filter(it => {
-        if (!this.featureId) return true
-        return it['@id'] === this.featureId
-      }).map(it => this.getFeatureInstance(SIIBRA_FEATURE_KEY, it["@id"])))),
-      catchError(() => of([]))
-    )),
-  )
-
-  public busy$ = this.region$.pipe(
-    switchMap(() => merge(
-      of(true),
-      this.results$.pipe(
-        mapTo(false)
-      )
-    )),
-  )
-  
-  private subs: Subscription[] = []
-  ngOnDestroy(){
-    this.unloadLandmarks()
-    while(this.subs.length) this.subs.pop().unsubscribe()
-  }
-  private openElectrodeSet = new Set<TElectrode>() 
-  public openElectrode$ = new BehaviorSubject<TElectrode[]>([])
-  handleDatumExpansion(electrode: TElectrode, state: boolean) {
-    if (state) this.openElectrodeSet.add(electrode)
-    else this.openElectrodeSet.delete(electrode)
-    this.openElectrode$.next(Array.from(this.openElectrodeSet))
-    this.loadLandmarks()
-  }
-
-  private loadedLms: {
-    '@id': string
-    id: string
-    name: string
-    position: [number, number, number]
-    color: [number, number, number]
-    showInSliceView: boolean
-  }[] = []
-
-  private unloadLandmarks(){
-    /**
-     * unload all the landmarks first
-     */
-    this.store.dispatch(
-      viewreStateRemoveUserLandmarks({
-        payload: {
-          landmarkIds: this.loadedLms.map(l => l['@id'])
-        }
-      })
-    )
-  }
-
-  private loadLandmarks(){
-    this.unloadLandmarks()
-    this.loadedLms = []
-
-    const lms = [] as {
-      '@id': string
-      id: string
-      name: string
-      position: [number, number, number]
-      color: [number, number, number]
-      showInSliceView: boolean
-    }[]
-
-    for (const detail of this.results) {
-      const subjectKey = detail.sub_id
-      for (const electrodeId in detail.electrodes){
-        const electrode = detail.electrodes[electrodeId]
-        if (!electrode.inRoi) continue
-        for (const cpKey in electrode.contact_points) {
-          const cp = electrode.contact_points[cpKey]
-          const id=`${detail.name}:${subjectKey}#${cpKey}`
-          lms.push({
-            "@id": id,
-            id: id,
-            name: id,
-            position: cp.location,
-            color: cp.inRoi ? [255, 100, 100]: [255, 255, 255],
-            showInSliceView: this.openElectrodeSet.has(electrode)
-          })
-        }
-      }
-    }
-    this.loadedLms = lms
-
-    this.store.dispatch(
-      viewerStateAddUserLandmarks({
-        landmarks: lms
-      })
-    )
-  }
-
-  handleContactPtClk(cp: TContactPoint) {
-    const { location } = cp
-    this.store.dispatch(
-      viewerStateChangeNavigation({
-        navigation: {
-          position: location.map(v => v * 1e6),
-          positionReal: true,
-          animation: {}
-        },
-      })
-    )
-  }
-}
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/ieeg/ieegCmp/ieeg.template.html b/src/atlasComponents/regionalFeatures/bsFeatures/ieeg/ieegCmp/ieeg.template.html
deleted file mode 100644
index 2622d200acae890dc47352ef6f0859ccc7125369..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/bsFeatures/ieeg/ieegCmp/ieeg.template.html
+++ /dev/null
@@ -1,67 +0,0 @@
-<ng-template [ngIf]="busy$ | async" [ngIfElse]="contenttmpl">
-  <spinner-cmp></spinner-cmp>
-</ng-template>
-
-<ng-template #contenttmpl>
-  <ng-container *ngFor="let result of results$ | async">
-    <ng-container *ngFor="let electrodeKeyVal of result | getProperty : 'electrodes' | keyvalue">
-      <ng-template [ngIf]="electrodeKeyVal.value.inRoi">
-        <ng-container *ngTemplateOutlet="electrodeTmpl; context: { $implicit: electrodeKeyVal.value }">
-        </ng-container>
-      </ng-template>
-
-    </ng-container>
-  </ng-container>
-</ng-template>
-
-<!-- template for electrode -->
-<ng-template #electrodeTmpl let-electrode>
-
-  <mat-expansion-panel
-    [expanded]="openElectrode$ | async | includes : electrode"
-    (opened)="handleDatumExpansion(electrode, true)"
-    (closed)="handleDatumExpansion(electrode, false)"
-    togglePosition="before">
-    <mat-expansion-panel-header>
-      <mat-panel-title>
-        Electrode
-      </mat-panel-title>
-      <mat-panel-description class="text-nowrap">
-        {{ electrode.electrode_id }}
-      </mat-panel-description>
-    </mat-expansion-panel-header>
-
-
-    <!-- <label for="task-list" class="d-block mat-h4 mt-4 text-muted">
-      Tasks
-    </label>
-    <section class="d-flex align-items-center mt-1">
-      <section id="task-list" class="flex-grow-1 flex-shrink-1 overflow-x-auto">
-        <div role="list">
-          <mat-chip *ngFor="let task of datum['tasks']" class="ml-1">
-            {{ task }}
-          </mat-chip>
-        </div>
-      </section>
-    </section> -->
-
-    <mat-divider></mat-divider>
-
-    <label for="contact-points-list" class="d-block mat-h4 mt-4 text-muted">
-      Contact Points
-    </label>
-    <section class="d-flex align-items-center mt-1">
-      <section id="contact-points-list" class="flex-grow-1 flex-shrink-1 overflow-x-auto">
-        <div role="list">
-          <mat-chip *ngFor="let cp_kv of electrode.contact_points | keyvalue"
-            [matTooltip]="cp_kv['value']['location']"
-            (click)="handleContactPtClk(cp_kv['value'])"
-            class="ml-1">
-            {{ cp_kv['key'] }}
-          </mat-chip>
-        </div>
-      </section>
-    </section>
-
-  </mat-expansion-panel>
-</ng-template>
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/ieeg/ieegCtrl.directive.ts b/src/atlasComponents/regionalFeatures/bsFeatures/ieeg/ieegCtrl.directive.ts
deleted file mode 100644
index e7e79ef4a97103ec38993712490cd0ebbb6ebb1b..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/bsFeatures/ieeg/ieegCtrl.directive.ts
+++ /dev/null
@@ -1,42 +0,0 @@
-import { Directive, Inject, OnDestroy, Optional } from "@angular/core";
-import { merge, Observable, of, Subscription } from "rxjs";
-import { catchError, mapTo, switchMap } from "rxjs/operators";
-import { BsRegionInputBase } from "../bsRegionInputBase";
-import { REGISTERED_FEATURE_INJECT_DATA } from "../constants";
-import { BsFeatureService, TFeatureCmpInput } from "../service";
-import { IBSSummaryResponse, IRegionalFeatureReadyDirective } from "../type";
-import { SIIBRA_FEATURE_KEY } from './type'
-
-@Directive({
-  selector: '[bs-features-ieeg-directive]',
-  exportAs: 'bsFeatureIeegDirective'
-})
-
-export class BsFeatureIEEGDirective extends BsRegionInputBase implements IRegionalFeatureReadyDirective, OnDestroy{
-
-  public results$: Observable<IBSSummaryResponse['IEEG_Session'][]>  = this.region$.pipe(
-    switchMap(() => this.getFeatureInstancesList(SIIBRA_FEATURE_KEY).pipe(
-      catchError(() => of([]))
-    )),
-  )
-  public busy$ = this.region$.pipe(
-    switchMap(() => merge(
-      of(true),
-      this.results$.pipe(
-        mapTo(false)
-      )
-    ))
-  )
-
-  constructor(
-    svc: BsFeatureService,
-    @Optional() @Inject(REGISTERED_FEATURE_INJECT_DATA) data: TFeatureCmpInput,
-  ){
-    super(svc, data)
-  }
-
-  private sub: Subscription[] = []
-  ngOnDestroy(){
-    while(this.sub.length) this.sub.pop().unsubscribe()
-  }
-}
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/ieeg/index.ts b/src/atlasComponents/regionalFeatures/bsFeatures/ieeg/index.ts
deleted file mode 100644
index e4e8322a07b0f6a2a25ce0b8522e5d2550b2f09b..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/bsFeatures/ieeg/index.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-export {
-  BsFeatureIEEGModule
-} from './module'
-
-export {
-  IEEG_FEATURE_NAME,
-  SIIBRA_FEATURE_KEY,
-} from './type'
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/ieeg/module.ts b/src/atlasComponents/regionalFeatures/bsFeatures/ieeg/module.ts
deleted file mode 100644
index 50ea68c3c313509efacea5473ad554e181bddc40..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/bsFeatures/ieeg/module.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-import { CommonModule } from "@angular/common";
-import { NgModule } from "@angular/core";
-import { ComponentsModule } from "src/components";
-import { AngularMaterialModule } from "src/sharedModules";
-import { UtilModule } from "src/util";
-import { BsFeatureService } from "../service";
-import { BsFeatureIEEGCmp } from "./ieegCmp/ieeg.component";
-import { BsFeatureIEEGDirective } from "./ieegCtrl.directive";
-import { IEEG_FEATURE_NAME } from "./type";
-
-@NgModule({
-  imports: [
-    CommonModule,
-    ComponentsModule,
-    UtilModule,
-    AngularMaterialModule,
-  ],
-  declarations: [
-    BsFeatureIEEGCmp,
-    BsFeatureIEEGDirective
-  ]
-})
-
-export class BsFeatureIEEGModule{
-  constructor(svc: BsFeatureService){
-    svc.registerFeature({
-      name: IEEG_FEATURE_NAME,
-      icon: 'fas fa-info',
-      View: BsFeatureIEEGCmp,
-      Ctrl: BsFeatureIEEGDirective
-    })
-  }
-}
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/ieeg/type.ts b/src/atlasComponents/regionalFeatures/bsFeatures/ieeg/type.ts
deleted file mode 100644
index 8300f694dde812df13206c56c403bd06ebb59be7..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/bsFeatures/ieeg/type.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-export type TBSSummary = {
-  '@id': string
-  name: string
-  description: string
-}
-
-export type TContactPoint = {
-  id: string
-  location: [number, number, number]
-  inRoi?: boolean
-}
-
-export type TElectrode = {
-  electrode_id: string
-  subject_id: string
-  contact_points: {
-    [key: string]: TContactPoint
-  }
-  inRoi?: boolean
-}
-
-export type TBSIeegSessionSummary = {
-  '@id': string
-  name: string
-  description: string
-  origin_datainfos: {
-    urls: {
-      doi: string
-    }[]
-  }[]
-}
-
-type TDetail = {
-  sub_id: string
-  electrodes: {
-    [key: string]: TElectrode
-  }
-  inRoi?: boolean
-}
-
-export type TBSIeegSessionDetail = TBSIeegSessionSummary & TDetail
-
-export const SIIBRA_FEATURE_KEY = 'IEEG_Session'
-export const _SIIBRA_FEATURE_KEY = 'IEEG_Electrode'
-export const IEEG_FEATURE_NAME = 'iEEG recordings'
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/index.ts b/src/atlasComponents/regionalFeatures/bsFeatures/index.ts
deleted file mode 100644
index e571d3c4272b3c27af2343832de0f8d8aabd2399..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/bsFeatures/index.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-export { BSFeatureModule } from './module'
-export { BS_ENDPOINT, BS_DARKTHEME } from './constants'
-export { TRegion } from './type'
-// nb do not export BsRegionInputBase from here
-// will result in cyclic imports
\ No newline at end of file
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/getTrailingHex.pipe.ts b/src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/getTrailingHex.pipe.ts
deleted file mode 100644
index 5314267e7e1f31b8226ab6eea1ac890277781a68..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/getTrailingHex.pipe.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import { Pipe, PipeTransform } from "@angular/core";
-
-@Pipe({
-  name: 'getTrailingHex',
-  pure: true
-})
-
-export class GetTrailingHexPipe implements PipeTransform{
-  public transform(input: string) {
-    const match = /[0-9a-f-]+$/.exec(input)
-    return match && match[0]
-  }
-}
\ No newline at end of file
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/index.ts b/src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/index.ts
deleted file mode 100644
index 337cdf646dd4827cc0f0a9a14f6637d0f4cb129e..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/index.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-export { KgDatasetModule } from './module'
-export { TCountedDataModality, TBSDetail, TBSSummary } from './type'
\ No newline at end of file
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/modalityPicker/modalityPicker.component.spec.ts b/src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/modalityPicker/modalityPicker.component.spec.ts
deleted file mode 100644
index 9618701aaa39492065095230cc37d7b524d3fbec..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/modalityPicker/modalityPicker.component.spec.ts
+++ /dev/null
@@ -1,38 +0,0 @@
-import { TCountedDataModality } from "../type"
-import { SortModalityAlphabeticallyPipe } from "./modalityPicker.component"
-
-describe('> modalityPicker.component.ts', () => {
-  describe('> ModalityPicker', () => {
-    // TODO
-  })
-
-  describe('> SortModalityAlphabeticallyPipe', () => {
-
-    const mods: TCountedDataModality[] = [{
-      name: 'bbb',
-      occurance: 0,
-      visible: false
-    }, {
-      name: 'AAA',
-      occurance: 1,
-      visible: false
-    }, {
-      name: '007',
-      occurance: 17,
-      visible: false
-    }]
-    const beforeInput = [...mods]
-    const pipe = new SortModalityAlphabeticallyPipe()
-
-    const output = pipe.transform(mods)
-
-    it('> does not mutate', () => {
-      expect(mods).toEqual(beforeInput)
-    })
-    it('> should sort modalities as expected', () => {
-      expect(output).toEqual([
-        mods[2], mods[1], mods[0]
-      ])
-    })
-  })
-})
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/modalityPicker/modalityPicker.component.ts b/src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/modalityPicker/modalityPicker.component.ts
deleted file mode 100644
index c00a636533018ec616110d4f366d967144254afa..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/modalityPicker/modalityPicker.component.ts
+++ /dev/null
@@ -1,81 +0,0 @@
-import { Component, EventEmitter, Input, OnChanges, Output, Pipe, PipeTransform } from "@angular/core";
-import { TCountedDataModality } from "../type";
-import { ARIA_LABELS } from 'common/constants'
-
-
-@Component({
-  selector: 'modality-picker',
-  templateUrl: './modalityPicker.template.html',
-  styleUrls: [
-    './modalityPicker.style.css',
-  ],
-  host:{
-    'aria-label': ARIA_LABELS.LIST_OF_MODALITIES
-  }
-})
-
-export class ModalityPicker implements OnChanges {
-
-  public modalityVisibility: Set<string> = new Set()
-
-  @Input()
-  public countedDataM: TCountedDataModality[] = []
-
-  public checkedModality: TCountedDataModality[] = []
-
-  @Output()
-  public modalityFilterEmitter: EventEmitter<TCountedDataModality[]> = new EventEmitter()
-
-  // filter(dataentries:DataEntry[]) {
-  //   return this.modalityVisibility.size === 0
-  //     ? dataentries
-  //     : dataentries.filter(de => de.activity.some(a => a.methods.some(m => this.modalityVisibility.has(this.dbService.temporaryFilterDataentryName(m)))))
-  // }
-
-  public ngOnChanges() {
-    this.checkedModality = this.countedDataM.filter(d => d.visible)
-  }
-
-  /**
-   * TODO
-   * togglemodailty should emit event, and let parent handle state
-   */
-  public toggleModality(modality: Partial<TCountedDataModality>) {
-    this.modalityFilterEmitter.emit(
-      this.countedDataM.map(d => d.name === modality.name
-        ? {
-          ...d,
-          visible: !d.visible,
-        }
-        : d),
-    )
-  }
-
-  public uncheckModality(modality: string) {
-    this.toggleModality({name: modality})
-  }
-
-  public clearAll() {
-    this.modalityFilterEmitter.emit(
-      this.countedDataM.map(d => {
-        return {
-          ...d,
-          visible: false,
-        }
-      }),
-    )
-  }
-}
-
-const sortByFn = (a: TCountedDataModality, b: TCountedDataModality) => (a.name || '0').toLowerCase().charCodeAt(0) - (b.name || '0').toLowerCase().charCodeAt(0) 
-
-@Pipe({
-  name: 'sortModalityAlphabetically',
-  pure: true
-})
-
-export class SortModalityAlphabeticallyPipe implements PipeTransform{
-  public transform(arr: TCountedDataModality[]): TCountedDataModality[]{
-    return [...arr].sort(sortByFn)
-  }
-}
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/modalityPicker/modalityPicker.style.css b/src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/modalityPicker/modalityPicker.style.css
deleted file mode 100644
index 85feb59e81b8a38ede569688b605d82e39932cca..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/modalityPicker/modalityPicker.style.css
+++ /dev/null
@@ -1,10 +0,0 @@
-:host
-{
-  display: flex;
-  flex-direction: column;
-}
-
-div
-{
-  white-space: nowrap;
-}
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/modalityPicker/modalityPicker.template.html b/src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/modalityPicker/modalityPicker.template.html
deleted file mode 100644
index 7bde04e8b2cc46f0872d978914bf9c254e79113a..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/modalityPicker/modalityPicker.template.html
+++ /dev/null
@@ -1,14 +0,0 @@
-<fieldset>
-  <legend class="sr-only">
-    Dataset modalities
-  </legend>
-
-  <mat-checkbox
-    
-    [checked]="datamodality.visible"
-    (change)="toggleModality(datamodality)"
-    [ngClass]="{'muted': datamodality.occurance === 0}"
-    *ngFor="let datamodality of countedDataM | sortModalityAlphabetically">
-    {{ datamodality.name }} <span class="text-muted">({{ datamodality.occurance }})</span>
-  </mat-checkbox>
-</fieldset>
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/module.ts b/src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/module.ts
deleted file mode 100644
index 232dba3734ce321bc2fd3a9fb1a5c52d0d4c9256..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/module.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import { CommonModule } from "@angular/common";
-import { NgModule } from "@angular/core";
-import { ShowDatasetDialogDirective } from "./showDataset/showDataset.directive";
-import { AngularMaterialModule } from "src/sharedModules";
-import { GetTrailingHexPipe } from "./getTrailingHex.pipe";
-import { ModalityPicker, SortModalityAlphabeticallyPipe } from "./modalityPicker/modalityPicker.component";
-
-// TODO break down into smaller components
-@NgModule({
-  imports: [
-    CommonModule,
-    AngularMaterialModule,
-  ],
-  declarations: [
-    ShowDatasetDialogDirective,
-    GetTrailingHexPipe,
-    ModalityPicker,
-    SortModalityAlphabeticallyPipe,
-  ],
-  exports: [
-    ShowDatasetDialogDirective,
-    GetTrailingHexPipe,
-    ModalityPicker,
-  ]
-})
-
-export class KgDatasetModule{}
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/showDataset/showDataset.directive.spec.ts b/src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/showDataset/showDataset.directive.spec.ts
deleted file mode 100644
index e37fa07aa72c7366814b10ecdbebb458118795c5..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/showDataset/showDataset.directive.spec.ts
+++ /dev/null
@@ -1,164 +0,0 @@
-import { Component } from "@angular/core";
-import { async, TestBed } from "@angular/core/testing";
-import { AngularMaterialModule } from "src/sharedModules";
-import { ShowDatasetDialogDirective, IAV_DATASET_SHOW_DATASET_DIALOG_CMP } from "./showDataset.directive";
-import { By } from "@angular/platform-browser";
-import { MatDialog } from "@angular/material/dialog";
-import { MatSnackBar } from "@angular/material/snack-bar";
-
-@Component({
-  template: ''
-})
-
-class TestCmp{}
-
-const dummyMatDialog = {
-  open: val => {}
-}
-
-const dummyMatSnackBar = {
-  open: val => {}
-}
-
-class DummyDialogCmp{}
-
-describe('ShowDatasetDialogDirective', () => {
-  beforeEach(async(() => {
-    TestBed.configureTestingModule({
-      imports: [
-        AngularMaterialModule
-      ],
-      declarations: [
-        TestCmp,
-        ShowDatasetDialogDirective,
-      ],
-      providers: [
-        {
-          provide: MatDialog,
-          useValue: dummyMatDialog
-        },
-        {
-          provide: MatSnackBar,
-          useValue: dummyMatSnackBar
-        },
-        {
-          provide: IAV_DATASET_SHOW_DATASET_DIALOG_CMP,
-          useValue: DummyDialogCmp
-        }
-      ]
-    })
-  }))
-
-  it('should be able to test directiv,', () => {
-
-    TestBed.overrideComponent(TestCmp, {
-      set: {
-        template: '<div iav-dataset-show-dataset-dialog></div>'
-      }
-    }).compileComponents()
-
-    const fixutre = TestBed.createComponent(TestCmp)
-    const directive = fixutre.debugElement.query( By.directive( ShowDatasetDialogDirective ) )
-
-    expect(directive).not.toBeNull()
-  })
-
-  it('if neither kgId nor fullId is defined, should not call dialog', () => {
-
-    TestBed.overrideComponent(TestCmp, {
-      set: {
-        template: '<div iav-dataset-show-dataset-dialog></div>'
-      }
-    }).compileComponents()
-
-    const snackbarOpenSpy = spyOn(dummyMatSnackBar, 'open').and.callThrough()
-    const dialogOpenSpy = spyOn(dummyMatDialog, 'open').and.callThrough()
-
-    const fixutre = TestBed.createComponent(TestCmp)
-    fixutre.detectChanges()
-
-    const directive = fixutre.debugElement.query( By.directive( ShowDatasetDialogDirective ) )
-    directive.nativeElement.click()
-
-    expect(snackbarOpenSpy).toHaveBeenCalled()
-    expect(dialogOpenSpy).not.toHaveBeenCalled()
-
-    snackbarOpenSpy.calls.reset()
-    dialogOpenSpy.calls.reset()
-  })
-
-  it('if kgId is defined, should call dialogOpen', () => {
-    
-    TestBed.overrideComponent(TestCmp, {
-      set: {
-        template: `
-        <div iav-dataset-show-dataset-dialog
-          iav-dataset-show-dataset-dialog-kgid="aaa-bbb">
-        </div>
-        `
-      }
-    }).compileComponents()
-
-    const snackbarOpenSpy = spyOn(dummyMatSnackBar, 'open').and.callThrough()
-    const dialogOpenSpy = spyOn(dummyMatDialog, 'open').and.callThrough()
-
-    const fixutre = TestBed.createComponent(TestCmp)
-    fixutre.detectChanges()
-
-    const directive = fixutre.debugElement.query( By.directive( ShowDatasetDialogDirective ) )
-    directive.nativeElement.click()
-
-    expect(snackbarOpenSpy).not.toHaveBeenCalled()
-    const mostRecentCall = dialogOpenSpy.calls.mostRecent()
-    const args = mostRecentCall.args as any[]
-
-    expect(args[0]).toEqual(DummyDialogCmp)
-    expect(args[1]).toEqual({
-      ...ShowDatasetDialogDirective.defaultDialogConfig,
-      panelClass: ['no-padding-dialog'],
-      data: {
-        fullId: `minds/core/dataset/v1.0.0/aaa-bbb`
-      }
-    })
-
-    snackbarOpenSpy.calls.reset()
-    dialogOpenSpy.calls.reset()
-  })
-
-  it('if fullId is defined, should call dialogOpen', () => {
-
-    TestBed.overrideComponent(TestCmp, {
-      set: {
-        template: `
-        <div iav-dataset-show-dataset-dialog
-          iav-dataset-show-dataset-dialog-fullid="abc/ccc-ddd">
-        </div>
-        `
-      }
-    }).compileComponents()
-
-    const snackbarOpenSpy = spyOn(dummyMatSnackBar, 'open').and.callThrough()
-    const dialogOpenSpy = spyOn(dummyMatDialog, 'open').and.callThrough()
-
-    const fixutre = TestBed.createComponent(TestCmp)
-    fixutre.detectChanges()
-
-    const directive = fixutre.debugElement.query( By.directive( ShowDatasetDialogDirective ) )
-    directive.nativeElement.click()
-
-    expect(snackbarOpenSpy).not.toHaveBeenCalled()
-    const mostRecentCall = dialogOpenSpy.calls.mostRecent()
-    const args = mostRecentCall.args as any[]
-    expect(args[0]).toEqual(DummyDialogCmp)
-    expect(args[1]).toEqual({
-      ...ShowDatasetDialogDirective.defaultDialogConfig,
-      panelClass: ['no-padding-dialog'],
-      data: {
-        fullId: `abc/ccc-ddd`
-      }
-    })
-
-    snackbarOpenSpy.calls.reset()
-    dialogOpenSpy.calls.reset()
-  })
-})
\ No newline at end of file
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/showDataset/showDataset.directive.ts b/src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/showDataset/showDataset.directive.ts
deleted file mode 100644
index d4b02e120d85776b02b0db606da5aa059cd46113..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/showDataset/showDataset.directive.ts
+++ /dev/null
@@ -1,85 +0,0 @@
-import { Directive, HostListener, Inject, Input, Optional } from "@angular/core";
-import { MatDialog, MatDialogConfig } from "@angular/material/dialog";
-import { MatSnackBar } from "@angular/material/snack-bar";
-import { OVERWRITE_SHOW_DATASET_DIALOG_TOKEN, TOverwriteShowDatasetDialog } from "src/util/interfaces";
-import { TRegionDetail as TSiibraRegion } from "src/util/siibraApiConstants/types";
-import { TRegion as TContextRegion } from 'src/atlasComponents/regionalFeatures/bsFeatures/type'
-
-export const IAV_DATASET_SHOW_DATASET_DIALOG_CMP = 'IAV_DATASET_SHOW_DATASET_DIALOG_CMP'
-export const IAV_DATASET_SHOW_DATASET_DIALOG_CONFIG = `IAV_DATASET_SHOW_DATASET_DIALOG_CONFIG`
-
-@Directive({
-  selector: '[iav-dataset-show-dataset-dialog]',
-  exportAs: 'iavDatasetShowDatasetDialog'
-})
-export class ShowDatasetDialogDirective{
-
-  static defaultDialogConfig: MatDialogConfig = {
-    autoFocus: false
-  }
-
-  @Input('iav-dataset-show-dataset-dialog-name')
-  name: string
-
-  @Input('iav-dataset-show-dataset-dialog-description')
-  description: string
-
-  @Input('iav-dataset-show-dataset-dialog-kgschema')
-  kgSchema: string = 'minds/core/dataset/v1.0.0'
-
-  @Input('iav-dataset-show-dataset-dialog-kgid')
-  kgId: string
-
-  @Input('iav-dataset-show-dataset-dialog-fullid')
-  fullId: string
-
-  @Input('iav-dataset-show-dataset-dialog-urls')
-  urls: {
-    cite: string
-    doi: string
-  }[] = []
-
-  @Input('iav-dataset-show-dataset-dialog-ignore-overwrite')
-  ignoreOverwrite = false
-
-  @Input('iav-dataset-show-dataset-dialog-contexted-region')
-  region: TSiibraRegion & TContextRegion
-
-  constructor(
-    private matDialog: MatDialog,
-    private snackbar: MatSnackBar,
-    @Optional() @Inject(IAV_DATASET_SHOW_DATASET_DIALOG_CMP) private dialogCmp: any,
-    @Optional() @Inject(OVERWRITE_SHOW_DATASET_DIALOG_TOKEN) private overwriteFn: TOverwriteShowDatasetDialog
-  ){ }
-
-  @HostListener('click')
-  onClick(){
-    const data = (() => {
-      if (this.fullId || (this.kgSchema && this.kgId)) {
-        return {
-          fullId: this.fullId || `${this.kgSchema}/${this.kgId}`
-        }
-      }
-      if (this.name || this.description) {
-        const { name, description, urls } = this
-        return { name, description, urls, useClassicUi: true }
-      }
-    })()
-
-    if (!data) {
-      return this.snackbar.open(`Cannot show dataset. Neither fullId nor kgId provided.`)
-    }
-
-    if (!this.ignoreOverwrite && this.overwriteFn) {
-      return this.overwriteFn(data)
-    }
-
-    if (!this.dialogCmp) throw new Error(`IAV_DATASET_SHOW_DATASET_DIALOG_CMP not provided!`)
-    const { useClassicUi } = data
-    this.matDialog.open(this.dialogCmp, {
-      ...ShowDatasetDialogDirective.defaultDialogConfig,
-      data,
-      ...(useClassicUi ? {} : { panelClass: ['no-padding-dialog'] })
-    })
-  }
-}
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/type.ts b/src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/type.ts
deleted file mode 100644
index e56921ce5c5e1e9adc110ba666dcf1d2eb192be3..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/type.ts
+++ /dev/null
@@ -1,82 +0,0 @@
-export type TCountedDataModality = {
-  name: string
-  occurance: number
-  visible: boolean
-}
-
-export type TBSSummary = {
-  ['@id']: string
-  src_name: string
-}
-
-export type TBSDetail = TBSSummary & {
-  __detail: {
-    formats: string[]
-    datasetDOI: {
-      cite: string
-      doi: string
-    }[]
-    activity: {
-      protocols: string[]
-      preparation: string[]
-    }[]
-    referenceSpaces: {
-      name: string
-      fullId: string
-    }[]
-    methods: string[]
-    custodians: {
-      "schema.org/shortName": string
-      identifier: string
-      name: string
-      '@id': string
-      shortName: string
-    }[]
-    project: string[]
-    description: string
-    parcellationAtlas: {
-      name: string
-      fullId: string
-      id: string[]
-    }[]
-    licenseInfo: {
-      name: string
-      url: string
-    }[]
-    embargoStatus: {
-      identifier: string[]
-      name: string
-      '@id': string
-    }[]
-    license: any[]
-    parcellationRegion: {
-      species: any[]
-      name: string
-      alias: string
-      fullId: string
-    }[]
-    species: string[]
-    name: string
-    files: {
-      byteSize: number
-      name: string
-      absolutePath: string
-      contentType: string
-    }[]
-    fullId: string
-    contributors: {
-      "schema.org/shortName": string
-      identifier: string
-      name: string
-      '@id': string
-      shortName: string
-    }[]
-    id: string
-    kgReference: string[] // aka doi
-    publications: {
-      name: string
-      cite: string
-      doi: string
-    }[]
-  }
-}
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/index.ts b/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/index.ts
deleted file mode 100644
index 3efa1d80ed5927d3777da4cc28faad043ce7795b..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/index.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-export {
-  KgRegionalFeatureModule
-} from './module'
-
-export {
-  EbrainsRegionalFeatureName,
-  KG_REGIONAL_FEATURE_KEY,
-  UNDER_REVIEW,
-  TBSDetail,
-  TBSSummary
-} from './type'
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegList/kgRegList.component.ts b/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegList/kgRegList.component.ts
deleted file mode 100644
index fd5b0afac18cbfd515e7ef21718e71084f75c0b2..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegList/kgRegList.component.ts
+++ /dev/null
@@ -1,89 +0,0 @@
-import { ChangeDetectionStrategy, Component, Inject, Input, OnDestroy, Optional } from "@angular/core";
-import { BehaviorSubject, Subscription } from "rxjs";
-import { filter, switchMap, tap } from "rxjs/operators";
-import { TCountedDataModality } from '../../kgDataset'
-import { BsRegionInputBase } from "../../bsRegionInputBase";
-import { BsFeatureService, TFeatureCmpInput } from "../../service";
-import { KG_REGIONAL_FEATURE_KEY, TBSDetail, TBSSummary } from "../type";
-import { ARIA_LABELS } from 'common/constants'
-import { REGISTERED_FEATURE_INJECT_DATA } from "../../constants";
-
-@Component({
-  selector: 'kg-regional-features-list',
-  templateUrl: './kgRegList.template.html',
-  styleUrls: [
-    './kgRegList.style.css'
-  ],
-  changeDetection: ChangeDetectionStrategy.OnPush
-})
-
-export class KgRegionalFeaturesList extends BsRegionInputBase implements OnDestroy{
-
-  public ARIA_LABELS = ARIA_LABELS
-
-  public dataModalities: TCountedDataModality[] = []
-
-  @Input()
-  public disableVirtualScroll = false
-  
-  public visibleRegionalFeatures: TBSSummary[] = []
-  public kgRegionalFeatures: TBSSummary[] = []
-  public kgRegionalFeatures$ = this.region$.pipe(
-    filter(v => {
-      this.busy$.next(false)
-      return !!v
-    }),
-    // must not use switchmapto here
-    switchMap(() => {
-      this.busy$.next(true)
-      return this.getFeatureInstancesList(KG_REGIONAL_FEATURE_KEY).pipe(
-        tap(() => {
-          this.busy$.next(false)
-        })
-      )
-    })
-  )
-  constructor(
-    svc: BsFeatureService,
-    @Optional() @Inject(REGISTERED_FEATURE_INJECT_DATA) data: TFeatureCmpInput
-  ){
-    super(svc, data)
-    this.sub.push(
-      this.kgRegionalFeatures$.subscribe(val => {
-        this.kgRegionalFeatures = val
-        this.visibleRegionalFeatures = val
-      })
-    )
-  }
-  private sub: Subscription[] = []
-  ngOnDestroy(){
-    while (this.sub.length) this.sub.pop().unsubscribe()
-  }
-
-  public trackByFn(_index: number, dataset: TBSSummary) {
-    return dataset['@id']
-  }
-
-  public detailDict: {
-    [key: string]: TBSDetail
-  } = {}
-
-  public handlePopulatedDetailEv(detail: TBSDetail){
-    this.detailDict = {
-      ...this.detailDict,
-      [detail["@id"]]: detail
-    }
-    for (const method of detail.__detail.methods) {
-      const found = this.dataModalities.find(v => v.name === method)
-      if (found) found.occurance = found.occurance + 1
-      else this.dataModalities.push({
-        name: method,
-        occurance: 1,
-        visible: false
-      })
-    }
-    this.dataModalities = [...this.dataModalities]
-  }
-
-  public busy$ = new BehaviorSubject(false)
-}
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegList/kgRegList.style.css b/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegList/kgRegList.style.css
deleted file mode 100644
index a0d49c32422a4789531e0cdaf7bf02fd4d43e20f..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegList/kgRegList.style.css
+++ /dev/null
@@ -1,9 +0,0 @@
-cdk-virtual-scroll-viewport
-{
-  min-height: 24rem;
-}
-
-modality-picker
-{
-  font-size: 90%;
-}
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegList/kgRegList.template.html b/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegList/kgRegList.template.html
deleted file mode 100644
index 010f4c85971f683525a8f3a6ca7026d648ea0e45..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegList/kgRegList.template.html
+++ /dev/null
@@ -1,30 +0,0 @@
-<spinner-cmp *ngIf="busy$ | async; else contentTmpl"></spinner-cmp>
-
-<ng-template #contentTmpl>
-  <cdk-virtual-scroll-viewport
-    [attr.aria-label]="ARIA_LABELS.LIST_OF_DATASETS_ARIA_LABEL"
-    class="h-100"
-    minBufferPx="200"
-    maxBufferPx="400"
-    itemSize="50">
-    <div *cdkVirtualFor="let dataset of visibleRegionalFeatures; trackBy: trackByFn; templateCacheSize: 20; let index = index"
-      class="h-50px overflow-hidden">
-
-      <!-- divider, show if not first -->
-      <mat-divider class="mt-1" *ngIf="index !== 0"></mat-divider>
-
-      <kg-regional-feature-summary
-        mat-ripple
-        iav-dataset-show-dataset-dialog
-        [iav-dataset-show-dataset-dialog-fullid]="dataset['@id']"
-        [iav-dataset-show-dataset-dialog-contexted-region]="region"
-        class="d-block pb-1 pt-1"
-        [region]="region"
-        [loadFull]="false"
-        [summary]="dataset"
-        (loadedDetail)="handlePopulatedDetailEv($event)">
-      </kg-regional-feature-summary>
-
-    </div>
-  </cdk-virtual-scroll-viewport>
-</ng-template>
\ No newline at end of file
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegList/kgReglist.directive.ts b/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegList/kgReglist.directive.ts
deleted file mode 100644
index df51bbb7f7c90d589e6e5c843d25f3a7f711f027..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegList/kgReglist.directive.ts
+++ /dev/null
@@ -1,59 +0,0 @@
-import { Directive, EventEmitter, Inject, OnDestroy, Optional, Output } from "@angular/core";
-import { KG_REGIONAL_FEATURE_KEY, TBSSummary } from "../type";
-import { BsFeatureService, TFeatureCmpInput } from "../../service";
-import { BsRegionInputBase } from "../../bsRegionInputBase";
-import { merge, of, Subscription } from "rxjs";
-import { catchError, mapTo, startWith, switchMap, tap } from "rxjs/operators";
-import { IRegionalFeatureReadyDirective } from "../../type";
-import { REGISTERED_FEATURE_INJECT_DATA } from "../../constants";
-
-@Directive({
-  selector: '[kg-regional-features-list-directive]',
-  exportAs: 'kgRegionalFeaturesListDirective'
-})
-
-export class KgRegionalFeaturesListDirective extends BsRegionInputBase implements IRegionalFeatureReadyDirective, OnDestroy {
-  public kgRegionalFeatures: TBSSummary[] = []
-  public kgRegionalFeatures$ = this.region$.pipe(
-    // must not use switchmapto here
-    switchMap(() => {
-      this.busyEmitter.emit(true)
-      return this.getFeatureInstancesList(KG_REGIONAL_FEATURE_KEY).pipe(
-        catchError(() => of([])),
-        tap(() => {
-          this.busyEmitter.emit(false)
-        }),
-      )
-    }),
-    startWith([])
-  )
-  
-  constructor(
-    svc: BsFeatureService,
-    @Optional() @Inject(REGISTERED_FEATURE_INJECT_DATA) data: TFeatureCmpInput,  
-  ){
-    super(svc, data)
-    this.sub.push(
-      this.kgRegionalFeatures$.subscribe(val => {
-        this.kgRegionalFeatures = val
-      })
-    )
-  }
-  private sub: Subscription[] = []
-  ngOnDestroy(){
-    while (this.sub.length) this.sub.pop().unsubscribe()
-  }
-
-  results$ = this.kgRegionalFeatures$
-  busy$ = this.region$.pipe(
-    switchMap(() => merge(
-      of(true),
-      this.results$.pipe(
-        mapTo(false)
-      )
-    ))
-  )
-
-  @Output('kg-regional-features-list-directive-busy')
-  busyEmitter = new EventEmitter<boolean>()
-}
\ No newline at end of file
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegSummary/kgRegSummary.component.ts b/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegSummary/kgRegSummary.component.ts
deleted file mode 100644
index 841226ad69f75fd821954f624878c4e1a4081de6..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegSummary/kgRegSummary.component.ts
+++ /dev/null
@@ -1,59 +0,0 @@
-import { Component, EventEmitter, Input, OnChanges, Output } from "@angular/core";
-import { BsRegionInputBase } from "../../bsRegionInputBase";
-import { BsFeatureService } from "../../service";
-import { KG_REGIONAL_FEATURE_KEY, TBSDetail, TBSSummary } from '../type'
-
-@Component({
-  selector: 'kg-regional-feature-summary',
-  templateUrl: './kgRegSummary.template.html',
-  styleUrls: [
-    './kgRegSummary.style.css'
-  ],
-  exportAs: 'kgRegionalFeatureSummary'
-})
-
-export class KgRegSummaryCmp extends BsRegionInputBase implements OnChanges{
-
-  @Input()
-  public loadFull = false
-
-  @Input()
-  public summary: TBSSummary = null
-
-  public detailLoaded = false
-  public loadingDetail = false
-  public detail: TBSDetail = null
-  @Output()
-  public loadedDetail = new EventEmitter<TBSDetail>()
-
-  public error: string = null
-  @Output()
-  public errorEmitter = new EventEmitter<string>()
-
-  constructor(svc: BsFeatureService){
-    super(svc)
-  }
-
-  ngOnChanges(){
-    if (this.loadFull && !!this.summary) {
-      if (this.loadingDetail || this.detailLoaded) {
-        return
-      }
-      this.loadingDetail = true
-      this.getFeatureInstance(KG_REGIONAL_FEATURE_KEY, this.summary["@id"]).subscribe(
-        detail => {
-          this.detail = detail
-          this.loadedDetail.emit(detail)
-        },
-        err => {
-          this.error = err
-          this.errorEmitter.emit(err)
-        },
-        () => {
-          this.detailLoaded = true
-          this.loadingDetail = false
-        } 
-      )
-    }
-  }
-}
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegSummary/kgRegSummary.template.html b/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegSummary/kgRegSummary.template.html
deleted file mode 100644
index 5fcb11b2b8f7a550e66a3491430a0629d62f7533..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegSummary/kgRegSummary.template.html
+++ /dev/null
@@ -1,3 +0,0 @@
-<small>
-  {{ summary.src_name }}
-</small>
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/module.ts b/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/module.ts
deleted file mode 100644
index 878eed872ca8ce48c90e6931eeb4351a2fd44892..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/module.ts
+++ /dev/null
@@ -1,50 +0,0 @@
-import { CommonModule } from "@angular/common";
-import { Inject, NgModule, Optional } from "@angular/core";
-import { AngularMaterialModule } from "src/sharedModules";
-import { KgRegSummaryCmp } from "./kgRegSummary/kgRegSummary.component";
-import { KgRegionalFeaturesList } from "./kgRegList/kgRegList.component";
-import { KgRegionalFeaturesListDirective } from "./kgRegList/kgReglist.directive";
-import { KgDatasetModule } from "../kgDataset";
-import { UtilModule } from "src/util";
-import { ComponentsModule } from "src/components";
-import { BsFeatureService } from "../service";
-import { EbrainsRegionalFeatureName } from "./type";
-import { GENERIC_INFO_INJ_TOKEN } from "../type";
-
-@NgModule({
-  imports: [
-    CommonModule,
-    AngularMaterialModule,
-    KgDatasetModule,
-    UtilModule,
-    ComponentsModule,
-  ],
-  declarations:[
-    KgRegSummaryCmp,
-    KgRegionalFeaturesList,
-    KgRegionalFeaturesListDirective,
-  ],
-  exports:[
-    KgRegSummaryCmp,
-    KgRegionalFeaturesList,
-    KgRegionalFeaturesListDirective,
-  ],
-})
-
-export class KgRegionalFeatureModule{
-  constructor(
-    svc: BsFeatureService,
-    @Optional() @Inject(GENERIC_INFO_INJ_TOKEN) Cmp: any
-  ){
-    if (!Cmp) {
-      console.warn(`GENERIC_INFO_INJ_TOKEN not injected!`)
-      return
-    }
-    svc.registerFeature({
-      name: EbrainsRegionalFeatureName,
-      icon: 'fas fa-ellipsis-h',
-      View: null,
-      Ctrl: KgRegionalFeaturesListDirective,
-    })
-  }
-}
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/type.ts b/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/type.ts
deleted file mode 100644
index f7ebe60367da3805b95b90c03237f31d4a23b4f2..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/type.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-export {
-  TBSDetail, TBSSummary
-} from '../kgDataset'
-
-export const EbrainsRegionalFeatureName = 'EBRAINS datasets'
-export const KG_REGIONAL_FEATURE_KEY = 'EbrainsRegionalDataset'
-
-export const UNDER_REVIEW = {
-  ['@id']: "https://nexus.humanbrainproject.org/v0/data/minds/core/embargostatus/v1.0.0/1d726b76-b176-47ed-96f0-b4f2e17d5f19"
-}
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/module.ts b/src/atlasComponents/regionalFeatures/bsFeatures/module.ts
deleted file mode 100644
index 20939be689ba1885ac2129c9d8f24e10743df630..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/bsFeatures/module.ts
+++ /dev/null
@@ -1,43 +0,0 @@
-import { CommonModule } from "@angular/common";
-import { NgModule } from "@angular/core";
-import { ComponentsModule } from "src/components";
-import { AngularMaterialModule } from "src/sharedModules";
-import { GenericInfoCmp, GenericInfoModule } from "./genericInfo";
-import { BsFeatureIEEGModule } from "./ieeg/module";
-import { KgRegionalFeatureModule } from "./kgRegionalFeature";
-import { GetBadgeFromFeaturePipe } from "./pipes/getBadgeFromFeature.pipe";
-import { RenderRegionalFeatureSummaryPipe } from "./pipes/renderRegionalFeatureSummary.pipe";
-import { BSFeatureReceptorModule } from "./receptor";
-import { RegionalFeatureWrapperCmp } from "./regionalFeatureWrapper/regionalFeatureWrapper.component";
-import { BsFeatureService } from "./service";
-import { GENERIC_INFO_INJ_TOKEN } from "./type";
-
-@NgModule({
-  imports: [
-    AngularMaterialModule,
-    CommonModule,
-    KgRegionalFeatureModule,
-    BSFeatureReceptorModule,
-    BsFeatureIEEGModule,
-    ComponentsModule,
-    GenericInfoModule,
-  ],
-  declarations: [
-    RegionalFeatureWrapperCmp,
-    RenderRegionalFeatureSummaryPipe,
-    GetBadgeFromFeaturePipe,
-  ],
-  providers: [
-    BsFeatureService,
-    {
-      provide: GENERIC_INFO_INJ_TOKEN,
-      useValue: GenericInfoCmp
-    }
-  ],
-  exports: [
-    RegionalFeatureWrapperCmp,
-    GenericInfoModule,
-  ]
-})
-
-export class BSFeatureModule{}
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/pipes/getBadgeFromFeature.pipe.ts b/src/atlasComponents/regionalFeatures/bsFeatures/pipes/getBadgeFromFeature.pipe.ts
deleted file mode 100644
index 8fe67e163d116fb68ccbbff6524cb21d544db3f2..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/bsFeatures/pipes/getBadgeFromFeature.pipe.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-import { Pipe, PipeTransform } from "@angular/core";
-import { IBSSummaryResponse, TContextedFeature } from "../type";
-import {
-  IEEG_FEATURE_NAME
-} from '../ieeg'
-import {
-  RECEPTOR_FEATURE_NAME
-} from '../receptor'
-
-export type TBadge = {
-  text: string
-  color: 'primary' | 'warn' | 'accent'
-}
-
-@Pipe({
-  name: 'getBadgeFromFeaturePipe',
-  pure: true
-})
-
-export class GetBadgeFromFeaturePipe implements PipeTransform{
-  public transform(input: TContextedFeature<keyof IBSSummaryResponse>): TBadge[]{
-    if (input.featureName === IEEG_FEATURE_NAME) {
-      return [{
-        text: IEEG_FEATURE_NAME,
-        color: 'primary',
-      }]
-    }
-    if (input.featureName === RECEPTOR_FEATURE_NAME) {
-      return [{
-        text: RECEPTOR_FEATURE_NAME,
-        color: 'accent',
-      }]
-    }
-    return []
-  }
-}
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/pipes/renderRegionalFeatureSummary.pipe.ts b/src/atlasComponents/regionalFeatures/bsFeatures/pipes/renderRegionalFeatureSummary.pipe.ts
deleted file mode 100644
index 829ed01981e3fb5a2061a058892f67742327dbb0..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/bsFeatures/pipes/renderRegionalFeatureSummary.pipe.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-import { Pipe, PipeTransform } from "@angular/core";
-import { IBSSummaryResponse, TContextedFeature } from "../type";
-import {
-  IEEG_FEATURE_NAME
-} from '../ieeg'
-import {
-  RECEPTOR_FEATURE_NAME
-} from '../receptor'
-import {
-  EbrainsRegionalFeatureName
-} from '../kgRegionalFeature'
-
-@Pipe({
-  name: 'renderRegionalFeatureSummaryPipe',
-  pure: true,
-})
-
-export class RenderRegionalFeatureSummaryPipe implements PipeTransform{
-  public transform(input: TContextedFeature<keyof IBSSummaryResponse>): string{
-    if (input.featureName === IEEG_FEATURE_NAME) {
-      return input.result['name']
-    }
-    if (input.featureName === RECEPTOR_FEATURE_NAME) {
-      return input.result['name']
-    }
-    if (input.featureName === EbrainsRegionalFeatureName) {
-      return input.result['src_name']
-    }
-    return `[Unknown feature type]`
-  }
-}
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/receptor/ar/autoradiograph.component.ts b/src/atlasComponents/regionalFeatures/bsFeatures/receptor/ar/autoradiograph.component.ts
deleted file mode 100644
index 0ea3e7fb5419c6db96466584773ba98e5d529934..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/bsFeatures/receptor/ar/autoradiograph.component.ts
+++ /dev/null
@@ -1,123 +0,0 @@
-import { Component, ElementRef, Input, OnChanges, ViewChild } from "@angular/core";
-import { BsFeatureReceptorBase } from "../base";
-import { CONST } from 'common/constants'
-import { environment } from 'src/environments/environment'
-import { AtlasWorkerService } from "src/atlasViewer/atlasViewer.workerService.service";
-
-const { RECEPTOR_AR_CAPTION } = CONST
-
-export function isAr(filename: string, label: string = ''){
-  return filename.indexOf(`_ar_${label}`) >= 0
-}
-
-@Component({
-  selector: 'bs-features-receptor-autoradiograph',
-  templateUrl: './autoradiograph.template.html',
-  styleUrls: [
-    './autoradiograph.style.css'
-  ]
-})
-
-export class BsFeatureReceptorAR extends BsFeatureReceptorBase implements OnChanges {
-
-  public RECEPTOR_AR_CAPTION = RECEPTOR_AR_CAPTION
-  private DS_PREVIEW_URL = environment.DATASET_PREVIEW_URL
-
-  @Input()
-  bsLabel: string
-
-  @ViewChild('arContainer', { read: ElementRef })
-  arContainer: ElementRef
-
-  private renderBuffer: Uint8ClampedArray
-  private width: number
-  private height: number
-  private pleaseRender = false
-
-  constructor(private worker: AtlasWorkerService){
-    super()
-  }
-  async ngOnChanges(){
-    this.error = null
-    this.urls = []
-    if (!this.bsFeature) {
-      this.error = `bsFeature not populated`
-      return
-    }
-    if (!this.bsLabel) {
-      this.error = `bsLabel not populated`
-      return
-    }
-
-    this.urls = this.bsFeature.__files
-      .filter(url => isAr(url, this.bsLabel))
-      .map(url => {
-        return { url }
-      })
-    try {
-      const {
-        "x-channel": channel,
-        "x-height": height,
-        "x-width": width,
-        content_type: contentType,
-        content_encoding: contentEncoding,
-        content,
-      } = this.bsFeature.__data.__autoradiographs[this.bsLabel]
-
-      if (contentType !== "application/octet-stream") {
-        throw new Error(`contentType expected to be application/octet-stream, but is instead ${contentType}`)
-      }
-      if (contentEncoding !== "gzip; base64") {
-        throw new Error(`contentEncoding expected to be gzip; base64, but is ${contentEncoding} instead.`)
-      }
-
-      const bin = atob(content)
-      const { pako } = (window as any).export_nehuba
-      const uint8array: Uint8Array = pako.inflate(bin)
-
-      this.width = width
-      this.height = height
-
-      const rgbaBuffer = await this.worker.sendMessage({
-        method: "PROCESS_TYPED_ARRAY",
-        param: {
-          inputArray: uint8array,
-          width,
-          height,
-          channel
-        },
-        transfers: [ uint8array.buffer ]
-      })
-
-      this.renderBuffer = rgbaBuffer.result.buffer
-      this.renderCanvas()
-    } catch (e) {
-      this.error = e.toString()
-    }
-  }
-
-  private renderCanvas(){
-    if (!this.arContainer) {
-      this.pleaseRender = true
-      return
-    }
-
-    const arContainer = (this.arContainer.nativeElement as HTMLElement)
-    while (arContainer.firstChild) {
-      arContainer.removeChild(arContainer.firstChild)
-    }
-
-    const canvas = document.createElement("canvas")
-    canvas.height = this.height
-    canvas.width = this.width
-    arContainer.appendChild(canvas)
-    const ctx = canvas.getContext("2d")
-    const imgData = ctx.createImageData(this.width, this.height)
-    imgData.data.set(this.renderBuffer)
-    ctx.putImageData(imgData, 0, 0)
-  }
-
-  ngAfterViewChecked(){
-    if (this.pleaseRender) this.renderCanvas()
-  }
-}
\ No newline at end of file
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/receptor/ar/autoradiograph.style.css b/src/atlasComponents/regionalFeatures/bsFeatures/receptor/ar/autoradiograph.style.css
deleted file mode 100644
index c7ebabfec67ff4346377d1dfed4a4ce63a40c066..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/bsFeatures/receptor/ar/autoradiograph.style.css
+++ /dev/null
@@ -1,5 +0,0 @@
-/* canvas created by createElement does not have encapsulation applied */
-.ar-container >>> canvas
-{
-  width: 100%;
-}
\ No newline at end of file
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/receptor/ar/autoradiograph.template.html b/src/atlasComponents/regionalFeatures/bsFeatures/receptor/ar/autoradiograph.template.html
deleted file mode 100644
index ad467adc5a5f15bf37343c49a0c02fbb96a2c4d7..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/bsFeatures/receptor/ar/autoradiograph.template.html
+++ /dev/null
@@ -1,21 +0,0 @@
-<ng-template [ngIf]="error">
-  {{ error }}
-</ng-template>
-
-<a *ngFor="let url of urls"
-  [href]="url.url"
-  class="no-hover"
-  download>
-  <i class="fas fa-download"></i>
-  <span>
-    {{ url.text || (url.url | getFilenamePipe) }}
-  </span>
-</a>
-
-<figure>
-  <figcaption class="text-muted">
-    Autoradiograph: {{ RECEPTOR_AR_CAPTION }}
-  </figcaption>
-  <div class="ar-container" #arContainer>
-  </div>
-</figure>
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/receptor/base.ts b/src/atlasComponents/regionalFeatures/bsFeatures/receptor/base.ts
deleted file mode 100644
index b149e359d7dca68da294e98e9138a557bfedf41a..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/bsFeatures/receptor/base.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import { Directive, Input } from "@angular/core";
-import { TBSDetail } from "./type";
-
-@Directive()
-export class BsFeatureReceptorBase {
-  @Input()
-  bsFeature: TBSDetail
-
-  public urls: {
-    url: string
-    text?: string
-  }[] = []
-
-  public error = null
-
-  // eslint-disable-next-line @typescript-eslint/no-empty-function
-  constructor(){}
-}
\ No newline at end of file
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/receptor/entry/entry.component.ts b/src/atlasComponents/regionalFeatures/bsFeatures/receptor/entry/entry.component.ts
deleted file mode 100644
index 7bcd410b784cdb61c221b969e4b3872074c63f3f..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/bsFeatures/receptor/entry/entry.component.ts
+++ /dev/null
@@ -1,92 +0,0 @@
-import { ChangeDetectorRef, Component, Inject, OnDestroy, Optional } from "@angular/core";
-import { BehaviorSubject, Observable, of, Subscription } from "rxjs";
-import { filter, map, shareReplay, startWith, switchMap, tap } from "rxjs/operators";
-import { BsRegionInputBase } from "../../bsRegionInputBase";
-import { REGISTERED_FEATURE_INJECT_DATA } from "../../constants";
-import { BsFeatureService, TFeatureCmpInput } from "../../service";
-import { TBSDetail } from "../type";
-import { ARIA_LABELS } from 'common/constants'
-
-@Component({
-  selector: 'bs-features-receptor-entry',
-  templateUrl: './entry.template.html',
-  styleUrls: [
-    './entry.style.css'
-  ],
-})
-
-export class BsFeatureReceptorEntry extends BsRegionInputBase implements OnDestroy{
-
-  private sub: Subscription[] = []
-  public ARIA_LABELS = ARIA_LABELS
-
-  private selectedREntryId$ = new BehaviorSubject<string>(null)
-  private _selectedREntryId: string
-  set selectedREntryId(id: string){
-    this.selectedREntryId$.next(id)
-    this._selectedREntryId = id
-  }
-  get selectedREntryId(){
-    return this._selectedREntryId
-  }
-
-  public selectedReceptor$: Observable<TBSDetail> = this.selectedREntryId$.pipe(
-    switchMap(id => id
-      ? this.getFeatureInstance('ReceptorDistribution', id)
-      : of(null)
-    ),
-    shareReplay(1),
-  )
-
-  public hasPrAr$: Observable<boolean> = this.selectedReceptor$.pipe(
-    map(detail => !!detail.__data.__profiles && Object.keys(detail.__data.__profiles).length > 0),
-  )
-
-  ngOnDestroy(){
-    while (this.sub.length > 0) this.sub.pop().unsubscribe()
-  }
-
-  public receptorsSummary$ = this.region$.pipe(
-    filter(v => !!v),
-    switchMap(() => this.getFeatureInstancesList('ReceptorDistribution')),
-    startWith([]),
-    map(
-      arr => this.data?.featureId
-        ? arr.filter(it => it['@id'] === this.data.featureId)
-        : arr
-    ),
-    shareReplay(1),
-  )
-
-  public onSelectReceptor(receptor: string){
-    this.selectedReceptor = receptor
-  }
-
-  public selectedReceptor = null
-  public allReceptors$ = this.selectedReceptor$.pipe(
-    map(rec => {
-      if (!rec) return []
-      return Object.keys(rec.__receptor_symbols || {})
-    })
-  )
-
-  constructor(
-    svc: BsFeatureService,
-    cdr: ChangeDetectorRef,
-    @Optional() @Inject(REGISTERED_FEATURE_INJECT_DATA) private data: TFeatureCmpInput
-  ){
-    super(svc, data)
-    this.sub.push(
-      this.selectedReceptor$.subscribe(() => {
-        cdr.markForCheck()
-      }),
-      this.receptorsSummary$.subscribe(arr => {
-        if (arr && arr.length > 0) {
-          this.selectedREntryId = arr[0]['@id']
-        } else {
-          this.selectedREntryId = null
-        }
-      })
-    )
-  }
-}
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/receptor/entry/entry.style.css b/src/atlasComponents/regionalFeatures/bsFeatures/receptor/entry/entry.style.css
deleted file mode 100644
index 71beb4683eec695ddf613bfcf2eecac35e79773d..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/bsFeatures/receptor/entry/entry.style.css
+++ /dev/null
@@ -1,8 +0,0 @@
-:host
-{
-  display: block;
-  width: 100%;
-  height: 100%;
-  padding-left:24px;
-  padding-right:24px;
-}
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/receptor/entry/entry.template.html b/src/atlasComponents/regionalFeatures/bsFeatures/receptor/entry/entry.template.html
deleted file mode 100644
index 48a24c834f0ef7e943434a6242c12fe49b0f5b91..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/bsFeatures/receptor/entry/entry.template.html
+++ /dev/null
@@ -1,50 +0,0 @@
-<mat-divider class="mt-2 mb-2"></mat-divider>
-
-<!-- potential selector for receptor data -->
-<mat-select class="invisible" [(value)]="selectedREntryId">
-  <mat-option *ngFor="let rec of receptorsSummary$ | async"
-    [value]="rec['@id']">
-    {{ rec.name }}
-  </mat-option>
-</mat-select>
-
-<ng-template [ngIf]="!(selectedReceptor$ | async)">
-  <spinner-cmp></spinner-cmp>
-</ng-template>
-
-<ng-template let-selectedRec [ngIf]="selectedReceptor$ | async">
-  <bs-features-receptor-fingerprint
-    (onSelectReceptor)="onSelectReceptor($event)"
-    [bsFeature]="selectedRec">
-  </bs-features-receptor-fingerprint>
-
-  <ng-template [ngIf]="hasPrAr$ | async">
-    <mat-divider></mat-divider>
-
-    <mat-form-field class="mt-2 w-100">
-      <mat-label>
-        Select a receptor
-      </mat-label>
-      <mat-select [(value)]="selectedReceptor">
-        <mat-option
-          *ngFor="let receptorName of (allReceptors$ | async)"
-          [value]="receptorName">
-          {{ receptorName }}
-        </mat-option>
-      </mat-select>
-    </mat-form-field>
-  
-    <bs-features-receptor-profile
-      *ngIf="selectedReceptor"
-      [bsFeature]="selectedRec"
-      [bsLabel]="selectedReceptor">
-    </bs-features-receptor-profile>
-  
-    <bs-features-receptor-autoradiograph
-      *ngIf="selectedReceptor"
-      [bsFeature]="selectedRec"
-      [bsLabel]="selectedReceptor">
-    </bs-features-receptor-autoradiograph>
-  </ng-template>
-
-</ng-template>
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/receptor/fp/fp.component.ts b/src/atlasComponents/regionalFeatures/bsFeatures/receptor/fp/fp.component.ts
deleted file mode 100644
index a061221118bf8420bbf6883b14c15f18680c4fb8..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/bsFeatures/receptor/fp/fp.component.ts
+++ /dev/null
@@ -1,90 +0,0 @@
-import { ChangeDetectionStrategy, Component, ElementRef, EventEmitter, HostListener, Inject, OnChanges, OnDestroy, OnInit, Optional, Output } from "@angular/core";
-import { fromEvent, Observable, Subscription } from "rxjs";
-import { distinctUntilChanged, map } from "rxjs/operators";
-import { BS_DARKTHEME } from "../../constants";
-import { BsFeatureReceptorBase } from "../base";
-import { CONST } from 'common/constants'
-
-const { RECEPTOR_FP_CAPTION } = CONST
-
-@Component({
-  selector: 'bs-features-receptor-fingerprint',
-  templateUrl: './fp.template.html',
-  styleUrls: [
-    './fp.style.css'
-  ],
-  changeDetection: ChangeDetectionStrategy.OnPush
-})
-
-export class BsFeatureReceptorFingerprint extends BsFeatureReceptorBase implements OnChanges, OnInit, OnDestroy{
-
-  public RECEPTOR_FP_CAPTION = RECEPTOR_FP_CAPTION
-  private sub: Subscription[] = []
-
-  @HostListener('click')
-  onClick(){
-    if (this.selectedReceptor) {
-      this.onSelectReceptor.emit(this.selectedReceptor)
-    }
-  }
-
-  @Output()
-  public onSelectReceptor = new EventEmitter()
-  private selectedReceptor: any
-
-  constructor(
-    private elRef: ElementRef,
-    @Optional() @Inject(BS_DARKTHEME) public darktheme$: Observable<boolean>,
-  ){
-    super()
-  }
-
-  ngOnInit(){
-    // without, when devtool is out, runs sluggishly
-    // informing angular that change occurs here will be handled by programmer, and not angular
-
-    this.sub.push(
-      fromEvent<CustomEvent>(this.elRef.nativeElement, 'kg-ds-prv-regional-feature-mouseover').pipe(
-        map(ev => ev.detail?.data?.receptor?.label),
-        distinctUntilChanged(),
-      ).subscribe(label => {
-        this.selectedReceptor = label
-      })
-    )
-  }
-
-  ngOnDestroy() {
-    while (this.sub.length > 0) this.sub.pop().unsubscribe()
-  }
-
-  ngOnChanges(){
-    this.error = null
-    this.urls = []
-
-    if (!this.bsFeature) {
-      this.error = `bsFeature is not populated`
-      return
-    }
-
-    this.urls.push(
-      ...this.bsFeature.__files
-        .filter(u => /_fp_/.test(u))
-        .map(url => {
-          return {
-            url,
-          }
-        }),
-      ...this.bsFeature.__files
-        .filter(u => !/_pr_|_ar_/.test(u) && /receptors\.tsv$/.test(u))
-        .map(url => {
-          return {
-            url,
-          }
-        })
-    )
-
-    const radarEl = (this.elRef.nativeElement as HTMLElement).querySelector<any>('kg-dataset-dumb-radar')
-    radarEl.radarBs = this.bsFeature.__data.__fingerprint
-    radarEl.metaBs = this.bsFeature.__receptor_symbols
-  }
-}
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/receptor/fp/fp.style.css b/src/atlasComponents/regionalFeatures/bsFeatures/receptor/fp/fp.style.css
deleted file mode 100644
index e2a98b2709c53c5853eebdbfc84370f371d84868..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/bsFeatures/receptor/fp/fp.style.css
+++ /dev/null
@@ -1,18 +0,0 @@
-kg-dataset-dumb-radar
-{
-  display: block;
-  min-height: 20em;
-}
-
-/* figure
-{
-  width: 100%;
-  height: 100%;
-}
-
-kg-dataset-dumb-radar
-{
-  display: block;
-  width: 100%;
-  height: 100%;
-} */
\ No newline at end of file
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/receptor/fp/fp.template.html b/src/atlasComponents/regionalFeatures/bsFeatures/receptor/fp/fp.template.html
deleted file mode 100644
index 50d505459bb9893b0d7b98ce20f596ba2ba241a9..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/bsFeatures/receptor/fp/fp.template.html
+++ /dev/null
@@ -1,22 +0,0 @@
-<ng-template [ngIf]="error">
-  {{ error }}
-</ng-template>
-
-<a *ngFor="let url of urls"
-  [href]="url.url"
-  class="no-hover"
-  download>
-  <i class="fas fa-download"></i>
-  <span>
-    {{ url.text || (url.url | getFilenamePipe) }}
-  </span>
-</a>
-
-<figure>
-  <figcaption class="text-muted">
-    Fingerprint : {{ RECEPTOR_FP_CAPTION }}
-  </figcaption>
-  <kg-dataset-dumb-radar
-    [attr.kg-ds-prv-darkmode]="darktheme$ && (darktheme$ | async)">
-  </kg-dataset-dumb-radar>
-</figure>
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/receptor/hasReceptor.directive.ts b/src/atlasComponents/regionalFeatures/bsFeatures/receptor/hasReceptor.directive.ts
deleted file mode 100644
index 6ef215934ac65dda3977d9e938d328ca45956f22..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/bsFeatures/receptor/hasReceptor.directive.ts
+++ /dev/null
@@ -1,55 +0,0 @@
-import { Directive, EventEmitter, Inject, OnDestroy, Optional, Output } from "@angular/core";
-import { merge, Observable, of, Subscription } from "rxjs";
-import { catchError, map, mapTo, switchMap } from "rxjs/operators";
-import { BsRegionInputBase } from "../bsRegionInputBase";
-import { REGISTERED_FEATURE_INJECT_DATA } from "../constants";
-import { BsFeatureService, TFeatureCmpInput } from "../service";
-import { IBSSummaryResponse, IRegionalFeatureReadyDirective } from "../type";
-
-@Directive({
-  selector: '[bs-features-receptor-directive]',
-  exportAs: 'bsFeatureReceptorDirective'
-})
-
-export class BsFeatureReceptorDirective extends BsRegionInputBase implements IRegionalFeatureReadyDirective, OnDestroy {
-  
-  private sub: Subscription[] = []
-
-  ngOnDestroy(){
-    while (this.sub.length > 0) this.sub.pop().unsubscribe()
-  }
-  public results$: Observable<IBSSummaryResponse['ReceptorDistribution'][]>  = this.region$.pipe(
-    switchMap(() => merge(
-      of([]),
-      this.getFeatureInstancesList('ReceptorDistribution').pipe(
-        catchError(() => of([]))
-      )
-    )),
-  )
-
-  public hasReceptor$ = this.results$.pipe(
-    map(arr => arr.length > 0)
-  )
-
-  public busy$ = this.region$.pipe(
-    switchMap(() => merge(
-      of(true),
-      this.results$.pipe(
-        mapTo(false)
-      )
-    ))
-  )
-  
-  constructor(
-    svc: BsFeatureService,
-    @Optional() @Inject(REGISTERED_FEATURE_INJECT_DATA) data: TFeatureCmpInput,
-  ){
-    super(svc, data)
-    this.sub.push(
-      this.busy$.subscribe(flag => this.fetchingFlagEmitter.emit(flag))
-    )
-  }
-
-  @Output('bs-features-receptor-directive-fetching-flag')
-  public fetchingFlagEmitter = new EventEmitter<boolean>()
-}
\ No newline at end of file
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/receptor/index.ts b/src/atlasComponents/regionalFeatures/bsFeatures/receptor/index.ts
deleted file mode 100644
index 79f8e55d97df512175ef1be0f913512bff41a16f..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/bsFeatures/receptor/index.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-export { BSFeatureReceptorModule } from './module'
-export { RECEPTOR_FEATURE_NAME } from './type'
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/receptor/module.ts b/src/atlasComponents/regionalFeatures/bsFeatures/receptor/module.ts
deleted file mode 100644
index 94f11b9df04e239090ed67a1bca192d2bd924b9c..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/bsFeatures/receptor/module.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-import { CommonModule } from "@angular/common";
-import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from "@angular/core";
-import { FormsModule } from "@angular/forms";
-import { AngularMaterialModule } from "src/sharedModules";
-import { UtilModule } from "src/util";
-import { BsFeatureService } from "../service";
-import { BsFeatureReceptorAR } from "./ar/autoradiograph.component";
-import { BsFeatureReceptorEntry } from "./entry/entry.component";
-import { BsFeatureReceptorFingerprint } from "./fp/fp.component";
-import { BsFeatureReceptorDirective } from "./hasReceptor.directive";
-import { BsFeatureReceptorProfile } from "./profile/profile.component";
-import { RECEPTOR_FEATURE_NAME } from "./type";
-
-@NgModule({
-  imports: [
-    CommonModule,
-    UtilModule,
-    AngularMaterialModule,
-    FormsModule,
-  ],
-  declarations: [
-    BsFeatureReceptorProfile,
-    BsFeatureReceptorAR,
-    BsFeatureReceptorFingerprint,
-    BsFeatureReceptorEntry,
-    BsFeatureReceptorDirective,
-  ],
-  exports: [
-    BsFeatureReceptorEntry,
-    BsFeatureReceptorDirective,
-  ],
-  schemas: [
-    CUSTOM_ELEMENTS_SCHEMA
-  ]
-})
-
-export class BSFeatureReceptorModule{
-  constructor(svc: BsFeatureService){
-    svc.registerFeature({
-      name: RECEPTOR_FEATURE_NAME,
-      icon: 'fas fa-info',
-      View: BsFeatureReceptorEntry,
-      Ctrl: BsFeatureReceptorDirective,
-    })
-  }
-}
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/receptor/profile/profile.component.ts b/src/atlasComponents/regionalFeatures/bsFeatures/receptor/profile/profile.component.ts
deleted file mode 100644
index 6b33a73b4810c5828752a783b006b0dbb6928b94..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/bsFeatures/receptor/profile/profile.component.ts
+++ /dev/null
@@ -1,59 +0,0 @@
-import { ChangeDetectionStrategy, Component, ElementRef, Inject, Input, OnChanges, Optional } from "@angular/core";
-import { Observable } from "rxjs";
-import { BS_DARKTHEME } from "../../constants";
-import { BsFeatureReceptorBase } from "../base";
-import { CONST } from 'common/constants'
-
-export function isPr(filename: string, label: string = ''){
-  return filename.indexOf(`_pr_${label}`) >= 0
-}
-
-const { RECEPTOR_PR_CAPTION } = CONST
-
-@Component({
-  selector: 'bs-features-receptor-profile',
-  templateUrl: './profile.template.html',
-  styleUrls: [
-    './profile.style.css'
-  ],
-  changeDetection: ChangeDetectionStrategy.OnPush
-})
-
-export class BsFeatureReceptorProfile extends BsFeatureReceptorBase implements OnChanges{
-  
-  public RECEPTOR_PR_CAPTION = RECEPTOR_PR_CAPTION
-
-  @Input()
-  bsLabel: string
-
-  constructor(
-    private elRef: ElementRef,
-    @Optional() @Inject(BS_DARKTHEME) public darktheme$: Observable<boolean>,
-  ){
-    super()
-  }
-
-  ngOnChanges(){
-    this.error = null
-    this.urls = []
-
-    if (!this.bsFeature) {
-      this.error = `bsFeature not populated`
-      return
-    }
-    if (!this.bsLabel) {
-      this.error = `bsLabel not populated`
-      return
-    }
-
-    this.urls = this.bsFeature.__files
-      .filter(url => isPr(url, this.bsLabel))
-      .map(url => {
-        return { url }
-      })
-
-    const profileBs = this.bsFeature.__data.__profiles[this.bsLabel]
-    const lineEl = (this.elRef.nativeElement as HTMLElement).querySelector<any>('kg-dataset-dumb-line')
-    lineEl.profileBs = profileBs
-  }
-}
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/receptor/profile/profile.template.html b/src/atlasComponents/regionalFeatures/bsFeatures/receptor/profile/profile.template.html
deleted file mode 100644
index 0b71d2ac46cb92527b2a44af80a94a69961568b5..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/bsFeatures/receptor/profile/profile.template.html
+++ /dev/null
@@ -1,22 +0,0 @@
-<ng-template [ngIf]="error">
-  {{ error }}
-</ng-template>
-
-<a *ngFor="let url of urls"
-  [href]="url.url"
-  class="no-hover"
-  download>
-  <i class="fas fa-download"></i>
-  <span>
-    {{ url.text || (url.url | getFilenamePipe) }}
-  </span>
-</a>
-
-<figure>
-  <figcaption class="text-muted">
-    Profile: {{ RECEPTOR_PR_CAPTION }}
-  </figcaption>
-  <kg-dataset-dumb-line
-    [attr.kg-ds-prv-darkmode]="darktheme$ && (darktheme$ | async)">
-  </kg-dataset-dumb-line>
-</figure>
\ No newline at end of file
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/receptor/type.ts b/src/atlasComponents/regionalFeatures/bsFeatures/receptor/type.ts
deleted file mode 100644
index 9ef1efe8b17727ded7f382784c035d780d323fc6..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/bsFeatures/receptor/type.ts
+++ /dev/null
@@ -1,63 +0,0 @@
-type TReceptorCommon = {
-  latex: string
-  markdown: string
-  name: string
-}
-
-type TReceptor = string // TODO complete all possible neuroreceptor
-
-type TReceptorSymbol = {
-  [key: string]: {
-    receptor: TReceptorCommon
-    neurotransmitter: TReceptorCommon & { label: string }
-  }
-}
-
-type TProfile = {
-  [key: number]: number
-}
-
-type TBSFingerprint = {
-  unit: string
-  labels: TReceptor[]
-  meanvals: number[]
-  stdvals: number[]
-  n: 1
-}
-
-export type TBSSummary = {
-  ['@id']: string
-  name: string
-  info: string
-  origin_datainfos?: ({
-    name: string
-    description: string
-    urls: {
-      doi: string
-      cite?: string
-    }[]
-  })[]
-}
-
-export type TBSDetail = TBSSummary & {
-  __files: string[]
-  __receptor_symbols: TReceptorSymbol
-  __data: {
-    __profiles: {
-      [key: string]: TProfile
-    }
-    __autoradiographs: {
-      [key: string]: {
-        content_type: string
-        content_encoding: string
-        ['x-width']: number
-        ['x-height']: number
-        ['x-channel']: number
-        content: string
-      }
-    }
-    __fingerprint: TBSFingerprint
-  }
-}
-
-export const RECEPTOR_FEATURE_NAME = 'receptor density'
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/regionalFeatureWrapper/regionalFeatureWrapper.component.ts b/src/atlasComponents/regionalFeatures/bsFeatures/regionalFeatureWrapper/regionalFeatureWrapper.component.ts
deleted file mode 100644
index 76a2ad1f2f9bdffbbb598992d542989a48127279..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/bsFeatures/regionalFeatureWrapper/regionalFeatureWrapper.component.ts
+++ /dev/null
@@ -1,211 +0,0 @@
-import { Component, ComponentFactory, ComponentFactoryResolver, Inject, Injector, Input, OnChanges, OnDestroy, Optional, ViewChild, ViewContainerRef } from "@angular/core";
-import { IBSSummaryResponse, TContextedFeature, TRegion } from "../type";
-import { BsFeatureService, TFeatureCmpInput } from "../service";
-import { combineLatest, Observable, Subject } from "rxjs";
-import { debounceTime, map, shareReplay, startWith } from "rxjs/operators";
-import { REGISTERED_FEATURE_INJECT_DATA } from "../constants";
-import { ARIA_LABELS } from 'common/constants'
-import {
-  IEEG_FEATURE_NAME
-} from '../ieeg'
-import {
-  RECEPTOR_FEATURE_NAME
-} from '../receptor'
-import {
-  EbrainsRegionalFeatureName
-} from '../kgRegionalFeature'
-import { OVERWRITE_SHOW_DATASET_DIALOG_TOKEN, TOverwriteShowDatasetDialog } from "src/util/interfaces";
-
-@Component({
-  selector: 'regional-feature-wrapper',
-  templateUrl: './regionalFeatureWrapper.template.html',
-  styleUrls: [
-    './regionalFeatureWrapper.style.css'
-  ]
-})
-
-export class RegionalFeatureWrapperCmp implements OnChanges, OnDestroy{
-
-  public useVirtualScroll = false
-
-  public ARIA_LABELS = ARIA_LABELS
-
-  @Input()
-  region: TRegion
-
-  @ViewChild('regionalFeatureContainerTmpl', { read: ViewContainerRef })
-  regionalFeatureContainerRef: ViewContainerRef
-
-  private weakmap = new WeakMap<(new () => any),  ComponentFactory<any>>()
-
-  private ondestroyCb: (() => void)[] = []
-  constructor(
-    private svc: BsFeatureService,
-    private cfr: ComponentFactoryResolver,
-    @Optional() @Inject(OVERWRITE_SHOW_DATASET_DIALOG_TOKEN) private overwriteFn: TOverwriteShowDatasetDialog
-  ){
-    const sub = this.registeredFeatures$.subscribe(arr => this.registeredFeatures = arr)
-    this.ondestroyCb.push(() => sub.unsubscribe())
-  }
-
-  private regionOnDestroyCb: (() => void)[] = []
-  private setupRegionalFeatureCtrl(){
-    if (!this.region) return
-    const { region } = this
-    for (const feat of this.svc.registeredFeatures){
-      const { name, icon } = feat
-      const ctrl = new feat.Ctrl(this.svc, { region })
-      const sub = combineLatest([
-        ctrl.busy$,
-        ctrl.results$.pipe(
-          startWith([])
-        )
-      ]).subscribe(
-        ([busy, results]) => {
-          this.registeredFeatureRawRegister[name] = { busy, results, icon }
-          this.registeredFeatureFireStream$.next(true)
-        }
-      )
-      this.regionOnDestroyCb.push(() => sub.unsubscribe())
-    }
-  }
-  private cleanUpRegionalFeature(){
-    while (this.regionOnDestroyCb.length) this.regionOnDestroyCb.pop()()
-    /**
-     * emit null to signify flush out of existing scan map
-     */
-    this.registeredFeatureRawRegister = {}
-    this.registeredFeatureFireStream$.next(true)
-  }
-
-  private registeredFeatureRawRegister: {
-    [key: string]: {
-      icon: string
-      busy: boolean
-      results: any[]
-    }
-  } = {}
-  private registeredFeatureFireStream$ = new Subject()
-  private registeredFeatureMasterStream$ = this.registeredFeatureFireStream$.pipe(
-    debounceTime(16),
-    /**
-     * must not use mapTo operator
-     * otherwise the emitted value will not change
-     */
-    map(() => this.registeredFeatureRawRegister),
-    shareReplay(1),
-  )
-  public busy$: Observable<boolean> = this.registeredFeatureMasterStream$.pipe(
-    map(obj => {
-      for (const key in obj) {
-        if(obj[key].busy) return true
-      }
-      return false
-    }),
-  )
-
-  public registeredFeatures: TContextedFeature<keyof IBSSummaryResponse>[] = []
-  private registeredFeatures$: Observable<TContextedFeature<keyof IBSSummaryResponse>[]> = this.registeredFeatureMasterStream$.pipe(
-    map(obj => {
-      const returnArr = []
-      for (const name in obj) {
-        if (obj[name].busy || obj[name].results.length === 0) {
-          continue
-        }
-        for (const result of obj[name].results) {
-          const objToBeInserted = {
-            featureName: name,
-            icon: obj[name].icon,
-            result
-          }
-          /**
-           * place ebrains regional features at the end
-           */
-          if (name === EbrainsRegionalFeatureName) {
-            returnArr.push(objToBeInserted)
-          } else {
-            returnArr.unshift(objToBeInserted)
-          }
-        }
-      }
-
-      return returnArr
-    }),
-  )
-
-  ngOnChanges(){
-    this.cleanUpRegionalFeature()
-    this.setupRegionalFeatureCtrl()
-  }
-
-  ngOnDestroy(){
-    this.cleanUpRegionalFeature()
-    while(this.ondestroyCb.length) this.ondestroyCb.pop()()
-  }
-
-  public handleFeatureClick(contextedFeature: TContextedFeature<any>){
-    if (!this.overwriteFn) {
-      console.warn(`show dialog function not overwritten!`)
-      return
-    }
-    
-    const featureId = contextedFeature.result['@id']
-    const arg = {}
-    if (contextedFeature.featureName === RECEPTOR_FEATURE_NAME) {
-      arg['name'] = contextedFeature.result['name']
-      arg['description'] = contextedFeature.result['info']
-      arg['urls'] = []
-      for (const info of contextedFeature.result['origin_datainfos']) {
-        arg['urls'].push(...info.urls)
-      }
-    }
-
-    if (contextedFeature.featureName === IEEG_FEATURE_NAME) {
-      arg['name'] = contextedFeature.result['name']
-      arg['description'] = contextedFeature.result['description'] || ' '
-      arg['isGdprProtected'] = true
-      arg['urls'] = []
-      for (const info of contextedFeature.result['origin_datainfos']) {
-        arg['urls'].push(...(info.urls || []))
-      }
-    }
-
-    if (contextedFeature.featureName === EbrainsRegionalFeatureName) {
-      arg['summary'] = contextedFeature.result
-    }
-
-    const { region } = this
-    
-    const feat = this.svc.registeredFeatures.find(f => f.name === contextedFeature.featureName)
-    if (!feat) {
-      console.log(`cannot find feature with name ${contextedFeature.featureName}`)
-      return
-    }
-    
-    const cf = (() => {
-      if (!feat.View) return null
-      const mapped = this.weakmap.get(feat.View)
-      if (mapped) return mapped
-      const _cf = this.cfr.resolveComponentFactory(feat.View)
-      this.weakmap.set(feat.View ,_cf)
-      return _cf
-    })()
-
-    this.overwriteFn({
-      region,
-      dataType: contextedFeature.featureName,
-      view: (() => {
-        if (!cf) return null
-        const injector = Injector.create({
-          providers: [{
-            provide: REGISTERED_FEATURE_INJECT_DATA,
-            useValue: { region, featureId } as TFeatureCmpInput
-          }],
-        })
-        const cmp = cf.create(injector)
-        return cmp.hostView
-      })(),
-      ...arg,
-    })
-  }
-}
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/regionalFeatureWrapper/regionalFeatureWrapper.style.css b/src/atlasComponents/regionalFeatures/bsFeatures/regionalFeatureWrapper/regionalFeatureWrapper.style.css
deleted file mode 100644
index 816e7ba25d2192ebd17900e60655db92bf17063b..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/bsFeatures/regionalFeatureWrapper/regionalFeatureWrapper.style.css
+++ /dev/null
@@ -1,19 +0,0 @@
-.button-text
-{
-  white-space: normal;
-  line-height: 1.5rem;
-  text-align: center;
-}
-
-.feature-container,
-cdk-virtual-scroll-viewport
-{
-  min-height: 24rem;
-}
-
-
-.feature-container
-{
-  height: 24rem;
-  overflow-y: scroll;
-}
\ No newline at end of file
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/regionalFeatureWrapper/regionalFeatureWrapper.template.html b/src/atlasComponents/regionalFeatures/bsFeatures/regionalFeatureWrapper/regionalFeatureWrapper.template.html
deleted file mode 100644
index 020145c3c36c406a74c3575422068d0b43700892..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/bsFeatures/regionalFeatureWrapper/regionalFeatureWrapper.template.html
+++ /dev/null
@@ -1,85 +0,0 @@
-<ng-template [ngTemplateOutlet]="resultTmpl">
-</ng-template>
-
-<ng-template #busyTmpl>
-  <spinner-cmp></spinner-cmp>
-</ng-template>
-
-<ng-template #resultTmpl>
-  
-  <!-- virtual scroll. do not activate until autosize is supported -->
-  <cdk-virtual-scroll-viewport
-    *ngIf="useVirtualScroll; else regularScrollTmpl"
-    [attr.aria-label]="ARIA_LABELS.LIST_OF_DATASETS_ARIA_LABEL"
-    class="h-100"
-    minBufferPx="200"
-    maxBufferPx="400"
-    itemSize="50">
-    <div *cdkVirtualFor="let feature of registeredFeatures; templateCacheSize: 20; let index = index"
-      class="h-50px overflow-hidden">
-
-      <!-- divider, show if not first -->
-      <mat-divider *ngIf="index !== 0"></mat-divider>
-      <ng-container *ngTemplateOutlet="itemContainer; context: { $implicit: feature }">
-      </ng-container>
-
-    </div>
-  </cdk-virtual-scroll-viewport>
-
-  <!-- fallback, regular scroll -->
-  <!-- less efficient on large list, but for now should do -->
-  <ng-template #regularScrollTmpl>
-    <div class="feature-container"
-      [attr.aria-label]="ARIA_LABELS.LIST_OF_DATASETS_ARIA_LABEL">
-
-      <!-- if busy, show spinner -->
-      <ng-template [ngIf]="busy$ | async" [ngIfElse]="notBusyTmpl">
-        <ng-template [ngTemplateOutlet]="busyTmpl"></ng-template>
-      </ng-template>
-
-      <ng-template #notBusyTmpl>
-        <ng-template [ngIf]="registeredFeatures.length === 0">
-          <span class="text-muted">
-            No regional features found.
-          </span>
-        </ng-template>
-      </ng-template>
-      <div *ngFor="let feature of registeredFeatures; let index = index"
-        class="overflow-hidden">
-
-        <!-- divider, show if not first -->
-        <mat-divider *ngIf="index !== 0"></mat-divider>
-        <ng-container *ngTemplateOutlet="itemContainer; context: { $implicit: feature }">
-        </ng-container>
-
-      </div>
-    </div>
-  </ng-template>
-  
-</ng-template>
-
-<!-- feature template -->
-<ng-template #itemContainer let-feature>
-  <div class="d-block pt-4 cursor-default"
-    (click)="handleFeatureClick(feature)"
-    mat-ripple>
-
-    <!-- mat-chip container -->
-    <!-- do not use mat-chip-list to avoid adding incorrect a11y info -->
-    <div class="transform-origin-left-center scale-80">
-      <mat-chip *ngFor="let badge of feature | getBadgeFromFeaturePipe"
-        [color]="badge.color"
-        selected>
-        {{ badge.text }}
-      </mat-chip>
-    </div>
-
-    <small>
-      {{ feature | renderRegionalFeatureSummaryPipe }}
-    </small>
-  </div>
-</ng-template>
-
-<!-- dummy container -->
-<ng-template #regionalFeatureContainerTmpl>
-</ng-template>
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/service.ts b/src/atlasComponents/regionalFeatures/bsFeatures/service.ts
deleted file mode 100644
index 7f52201e0619227b8ba17f23d0b4ce6cb710ce23..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/bsFeatures/service.ts
+++ /dev/null
@@ -1,140 +0,0 @@
-import { HttpClient } from "@angular/common/http";
-import { Inject, Injectable } from "@angular/core";
-import { BehaviorSubject } from "rxjs";
-import { shareReplay } from "rxjs/operators";
-import { CachedFunction } from "src/util/fn";
-import { BS_ENDPOINT } from "./constants";
-import { IBSSummaryResponse, IBSDetailResponse, TRegion, IFeatureList, IRegionalFeatureReadyDirective } from './type'
-import { SIIBRA_FEATURE_KEY as IEEG_FEATURE_KEY } from '../bsFeatures/ieeg/type'
-
-function processRegion(region: TRegion) {
-  return `${region.name} ${region.status ? region.status : '' }`
-}
-
-export type TFeatureCmpInput = {
-  region: TRegion
-  featureId?: string
-}
-
-export type TRegisteredFeature<V = any> = {
-  name: string
-  icon: string // fontawesome font class, e.g. `fas fa-link-alt`
-  View: new (...arg: any[]) => V
-  Ctrl: new (svc: BsFeatureService, data: TFeatureCmpInput) => IRegionalFeatureReadyDirective
-}
-
-@Injectable({
-  providedIn: 'root'
-})
-export class BsFeatureService{
-
-  static SpaceFeatureSet = new Set([
-    IEEG_FEATURE_KEY
-  ])
-
-  public registeredFeatures: TRegisteredFeature[] = []
-  public registeredFeatures$ = new BehaviorSubject<TRegisteredFeature[]>(this.registeredFeatures)
-  public getAllFeatures$ = this.http.get(`${this.bsEndpoint}/features`).pipe(
-    shareReplay(1)
-  )
-
-  public listFeatures(region: TRegion){
-    const { context } = region
-    const { atlas, parcellation } = context
-    return this.http.get<IFeatureList>(
-      `${this.bsEndpoint}/atlases/${encodeURIComponent(atlas["@id"])}/parcellations/${encodeURIComponent(parcellation['@id'])}/regions/${encodeURIComponent(processRegion(region))}/features`
-    )
-  }
-
-  private getUrl(arg: {
-    atlasId: string
-    parcId: string
-    spaceId: string
-    region: TRegion
-    featureName: string
-    featureId?: string
-  }){
-    const { 
-      atlasId,
-      parcId,
-      spaceId,
-      region,
-      featureName,
-      featureId,
-    } = arg
-
-    if (BsFeatureService.SpaceFeatureSet.has(featureName)) {
-      
-      const url = new URL(`${this.bsEndpoint}/atlases/${encodeURIComponent(atlasId)}/spaces/${encodeURIComponent(spaceId)}/features/${encodeURIComponent(featureName)}${ featureId ? ('/' + encodeURIComponent(featureId)) : '' }`)
-      url.searchParams.set('parcellation_id', parcId)
-      url.searchParams.set('region', processRegion(region))
-
-      return url.toString()
-    }
-    
-    if (!featureId) {
-      return `${this.bsEndpoint}/atlases/${encodeURIComponent(atlasId)}/parcellations/${encodeURIComponent(parcId)}/regions/${encodeURIComponent(processRegion(region))}/features/${encodeURIComponent(featureName)}`
-    }
-    return `${this.bsEndpoint}/atlases/${encodeURIComponent(atlasId)}/parcellations/${encodeURIComponent(parcId)}/regions/${encodeURIComponent(processRegion(region))}/features/${encodeURIComponent(featureName)}/${encodeURIComponent(featureId)}`
-  }
-
-  @CachedFunction({
-    serialization: (featureName, region) => `${featureName}::${processRegion(region)}`
-  })
-  public getFeatures<T extends keyof IBSSummaryResponse>(featureName: T, region: TRegion){
-    const { context } = region
-    const { atlas, parcellation, template } = context
-    const url = this.getUrl({
-      atlasId: atlas['@id'],
-      parcId: parcellation['@id'],
-      region,
-      featureName,
-      spaceId: template['@id']
-    })
-    
-    return this.http.get<IBSSummaryResponse[T][]>(
-      url
-    ).pipe(
-      shareReplay(1)
-    )
-  }
-
-  @CachedFunction({
-    serialization: (featureName, region, featureId) => `${featureName}::${processRegion(region)}::${featureId}`
-  })
-  public getFeature<T extends keyof IBSDetailResponse>(featureName: T, region: TRegion, featureId: string) {
-    const { context } = region
-    const { atlas, parcellation, template } = context
-    const url = this.getUrl({
-      atlasId: atlas['@id'],
-      parcId: parcellation['@id'],
-      spaceId: template['@id'],
-      region,
-      featureName,
-      featureId
-    })
-    return this.http.get<IBSSummaryResponse[T]&IBSDetailResponse[T]>(url).pipe(
-      shareReplay(1)
-    )
-  }
-
-  public registerFeature(feature: TRegisteredFeature){
-    if (this.registeredFeatures.find(v => v.name === feature.name)) {
-      throw new Error(`feature ${feature.name} already registered`)
-    }
-    this.registeredFeatures.push(feature)
-    this.registeredFeatures$.next(this.registeredFeatures)
-  }
-
-  public deregisterFeature(name: string){
-    this.registeredFeatures = this.registeredFeatures.filter(v => v.name !== name)
-    this.registeredFeatures$.next(this.registeredFeatures)
-  }
-  
-  constructor(
-    private http: HttpClient,
-    @Inject(BS_ENDPOINT) private bsEndpoint: string,
-  ){
-
-  }
-}
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/type.ts b/src/atlasComponents/regionalFeatures/bsFeatures/type.ts
deleted file mode 100644
index 84731e48984af2dd020be92f5d06e3b91429f558..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/bsFeatures/type.ts
+++ /dev/null
@@ -1,52 +0,0 @@
-import { IHasId } from "src/util/interfaces";
-import { TBSDetail as TReceptorDetail, TBSSummary as TReceptorSummary } from "./receptor/type";
-import { KG_REGIONAL_FEATURE_KEY, TBSDetail as TKGDetail, TBSSummary as TKGSummary } from './kgRegionalFeature/type'
-import { SIIBRA_FEATURE_KEY, TBSSummary as TIEEGSummary, TBSIeegSessionDetail as TIEEGDetail } from './ieeg/type'
-import { Observable } from "rxjs";
-import { InjectionToken } from "@angular/core";
-
-/**
- * change KgRegionalFeature -> EbrainsRegionalDataset in prod
- */
-
-export interface IBSSummaryResponse {
-  'ReceptorDistribution': TReceptorSummary
-  [KG_REGIONAL_FEATURE_KEY]: TKGSummary
-  [SIIBRA_FEATURE_KEY]: TIEEGSummary
-}
-
-export interface IBSDetailResponse {
-  'ReceptorDistribution': TReceptorDetail
-  [KG_REGIONAL_FEATURE_KEY]: TKGDetail
-  [SIIBRA_FEATURE_KEY]: TIEEGDetail
-}
-
-export type TRegion = {
-  name: string
-  status?: string
-  context: {
-    atlas: IHasId
-    template: IHasId
-    parcellation: IHasId
-  }
-}
-
-export interface IFeatureList {
-  features: {
-    [key: string]: string
-  }[]
-}
-
-export interface IRegionalFeatureReadyDirective {
-  ngOnDestroy(): void
-  busy$: Observable<boolean>
-  results$: Observable<IBSSummaryResponse[keyof IBSSummaryResponse][]>
-}
-
-export type TContextedFeature<T extends keyof IBSSummaryResponse> = {
-  featureName: string
-  icon: string
-  result: IBSSummaryResponse[T]
-}
-
-export const GENERIC_INFO_INJ_TOKEN = new InjectionToken('GENERIC_INFO_INJ_TOKEN')
diff --git a/src/atlasComponents/regionalFeatures/featureContainer/featureContainer.component.spec.ts b/src/atlasComponents/regionalFeatures/featureContainer/featureContainer.component.spec.ts
deleted file mode 100644
index 6821e237ff0aae95e6eaa42c99989dec7d5e9b65..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/featureContainer/featureContainer.component.spec.ts
+++ /dev/null
@@ -1,155 +0,0 @@
-import { CommonModule } from "@angular/common"
-import { ChangeDetectorRef, Component, ComponentRef, EventEmitter, NgModule } from "@angular/core"
-import { async, TestBed } from "@angular/core/testing"
-import { By } from "@angular/platform-browser"
-import { RegionalFeaturesService } from "../regionalFeature.service"
-import { ISingleFeature } from "../singleFeatures/interfaces"
-import { FeatureContainer } from "./featureContainer.component"
-
-const dummyCmpType = 'dummyType'
-
-@Component({
-  template: `{{ text }}`
-})
-
-class DummyComponent implements ISingleFeature{
-  text = 'hello world'
-  feature: any
-  region: any
-  viewChanged = new EventEmitter<boolean>()
-}
-
-@Component({
-  template: ''
-})
-
-class HostCmp{
-  public feature: any
-  public region: any
-
-  constructor(public cdr: ChangeDetectorRef){
-
-  }
-
-  detectChange(){
-    this.cdr.detectChanges()
-  }
-}
-
-const serviceStub = {
-  mapFeatToCmp: new Map([
-    [dummyCmpType, DummyComponent]
-  ])
-}
-
-describe('> featureContainer.component.ts', () => {
-  describe('> FeatureContainer', () => {
-
-    beforeEach(async () => {
-
-      await TestBed.configureTestingModule({
-        imports: [
-          CommonModule,
-        ],
-        declarations: [
-          FeatureContainer,
-          DummyComponent,
-          HostCmp,
-        ],
-        providers: [
-          {
-            provide: RegionalFeaturesService,
-            useValue: serviceStub
-          }
-        ]
-      }).overrideComponent(HostCmp, {
-        set: {
-          template: `
-          <feature-container
-            [feature]="feature"
-            [region]="region"
-            (viewChanged)="detectChange()">
-          </feature-container>`
-        }
-      }).compileComponents()
-
-    })
-
-    it('> can be created', () => {
-      const fixture = TestBed.createComponent(HostCmp)
-      expect(fixture).toBeTruthy()
-      const featContainer = fixture.debugElement.query(By.directive(FeatureContainer))
-      expect(featContainer).toBeTruthy()
-    })
-
-    describe('> if inputs change', () => {
-      it('> if input changed, but feature is not one of them, map.get will not be called', () => {
-        const fixture = TestBed.createComponent(HostCmp)
-        // const featContainer = fixture.debugElement.query(By.directive(FeatureContainer))
-        spyOn(serviceStub.mapFeatToCmp, 'get').and.callThrough()
-        fixture.componentInstance.region = {
-          name: 'tesla'
-        }
-        fixture.detectChanges()
-        expect(serviceStub.mapFeatToCmp.get).not.toHaveBeenCalled()
-      })
-
-      it('> if input changed, feature is one of them, will not call map.get', () => {
-        const fixture = TestBed.createComponent(HostCmp)
-        const dummyFeature = {
-          type: dummyCmpType
-        }
-        spyOn(serviceStub.mapFeatToCmp, 'get').and.callThrough()
-        fixture.componentInstance.feature = dummyFeature
-        fixture.detectChanges()
-        expect(serviceStub.mapFeatToCmp.get).toHaveBeenCalledWith(dummyCmpType)
-      })
-
-      it('> should render default txt', () => {
-        const fixture = TestBed.createComponent(HostCmp)
-        const dummyFeature = {
-          type: dummyCmpType
-        }
-        fixture.componentInstance.feature = dummyFeature
-        fixture.detectChanges()
-        const text = fixture.nativeElement.textContent
-        expect(text).toContain('hello world')
-      })
-
-      it('> if inner component changes, if view changed does not emit, will not change ui', () => {
-
-        const fixture = TestBed.createComponent(HostCmp)
-        const dummyFeature = {
-          type: dummyCmpType
-        }
-        fixture.componentInstance.feature = dummyFeature
-        fixture.detectChanges()
-        const featureContainer = fixture.debugElement.query(
-          By.directive(FeatureContainer)
-        )
-        const cr = (featureContainer.componentInstance as FeatureContainer)['cr'] as ComponentRef<DummyComponent>
-        cr.instance.text = 'foo bar'
-        const text = fixture.nativeElement.textContent
-        expect(text).toContain('hello world')
-      })
-
-      it('> if inner component changes, and viewChanged is emitted, ui should change accordingly', () => {
-
-        const fixture = TestBed.createComponent(HostCmp)
-        const dummyFeature = {
-          type: dummyCmpType
-        }
-        fixture.componentInstance.feature = dummyFeature
-        fixture.detectChanges()
-        const featureContainer = fixture.debugElement.query(
-          By.directive(FeatureContainer)
-        )
-        const cr = (featureContainer.componentInstance as FeatureContainer)['cr'] as ComponentRef<DummyComponent>
-        cr.instance.text = 'foo bar'
-        cr.instance.viewChanged.emit(true)
-        const text = fixture.nativeElement.textContent
-        expect(text).toContain('foo bar')
-      })
-    })
-  })
-})
diff --git a/src/atlasComponents/regionalFeatures/featureContainer/featureContainer.component.ts b/src/atlasComponents/regionalFeatures/featureContainer/featureContainer.component.ts
deleted file mode 100644
index 2b2391599494ac22cadc250fa479255fa39d2179..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/featureContainer/featureContainer.component.ts
+++ /dev/null
@@ -1,60 +0,0 @@
-import { ChangeDetectionStrategy, Component, ComponentFactoryResolver, ComponentRef, Input, OnChanges, Output, SimpleChanges, ViewContainerRef, EventEmitter } from "@angular/core";
-import { Subscription } from "rxjs";
-import { IFeature, RegionalFeaturesService } from "../regionalFeature.service";
-import { ISingleFeature } from "../singleFeatures/interfaces";
-
-@Component({
-  selector: 'feature-container',
-  template: '',
-  changeDetection: ChangeDetectionStrategy.OnPush,
-})
-
-export class FeatureContainer implements OnChanges{
-  @Input()
-  feature: IFeature
-
-  @Input()
-  region: any
-
-  @Output()
-  viewChanged: EventEmitter<boolean> = new EventEmitter()
-
-  private cr: ComponentRef<ISingleFeature>
-  
-  constructor(
-    private vCRef: ViewContainerRef,
-    private rService: RegionalFeaturesService,
-    private cfr: ComponentFactoryResolver,
-  ){
-  }
-
-  private viewChangedSub: Subscription
-
-  ngOnChanges(simpleChanges: SimpleChanges){
-    if (!simpleChanges.feature) return
-    const { currentValue, previousValue } = simpleChanges.feature
-    if (currentValue === previousValue) return
-    this.clear()
-
-    /**
-     * catching instances where currentValue for feature is falsy
-     */
-    if (!currentValue) return
-
-    /**
-     * TODO catch if map is undefined
-     */
-    const comp = this.rService.mapFeatToCmp.get(currentValue.type)
-    if (!comp) throw new Error(`mapFeatToCmp for ${currentValue.type} not defined`)
-    const cf = this.cfr.resolveComponentFactory<ISingleFeature>(comp)
-    this.cr = this.vCRef.createComponent(cf)
-    this.cr.instance.feature = this.feature
-    this.cr.instance.region = this.region
-    this.viewChangedSub = this.cr.instance.viewChanged.subscribe(() => this.viewChanged.emit(true))
-  }
-
-  clear(){
-    if (this.viewChangedSub) this.viewChangedSub.unsubscribe()
-    this.vCRef.clear()
-  }
-}
diff --git a/src/atlasComponents/regionalFeatures/index.ts b/src/atlasComponents/regionalFeatures/index.ts
deleted file mode 100644
index a71230d7107939f7da63d18d843339937ed89290..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/index.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-export { RegionalFeaturesModule } from './module'
-export { IFeature } from './regionalFeature.service'
\ No newline at end of file
diff --git a/src/atlasComponents/regionalFeatures/module.ts b/src/atlasComponents/regionalFeatures/module.ts
deleted file mode 100644
index 573acb57dfd9b3f0f0e303a622be2926f8013f27..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/module.ts
+++ /dev/null
@@ -1,52 +0,0 @@
-import { CommonModule } from "@angular/common";
-import { HttpClientModule } from "@angular/common/http";
-import { NgModule } from "@angular/core";
-import { UtilModule } from "src/util";
-import { AngularMaterialModule } from "src/sharedModules";
-import { FeatureContainer } from "./featureContainer/featureContainer.component";
-import { FilterRegionalFeaturesByTypePipe } from "./pipes/filterRegionalFeaturesByType.pipe";
-import { FilterRegionFeaturesById } from "./pipes/filterRegionFeaturesById.pipe";
-import { FindRegionFEatureById } from "./pipes/findRegionFeatureById.pipe";
-import { RegionalFeaturesService } from "./regionalFeature.service";
-import { RegionGetAllFeaturesDirective } from "./regionGetAllFeatures.directive";
-import { FeatureIEEGRecordings } from "./singleFeatures/iEEGRecordings/module";
-import { ReceptorDensityModule } from "./singleFeatures/receptorDensity/module";
-
-@NgModule({
-  imports: [
-    CommonModule,
-    UtilModule,
-    AngularMaterialModule,
-    FeatureIEEGRecordings,
-    ReceptorDensityModule,
-    HttpClientModule,
-  ],
-  declarations: [
-    /**
-     * components
-     */
-    FeatureContainer,
-
-    /**
-     * Directives
-     */
-    RegionGetAllFeaturesDirective,
-
-    /**
-     * pipes
-     */
-    FilterRegionalFeaturesByTypePipe,
-    FindRegionFEatureById,
-    FilterRegionFeaturesById,
-  ],
-  exports: [
-    RegionGetAllFeaturesDirective,
-    FilterRegionFeaturesById,
-    FeatureContainer,
-  ],
-  providers: [
-    RegionalFeaturesService,
-  ]
-})
-
-export class RegionalFeaturesModule{}
diff --git a/src/atlasComponents/regionalFeatures/pipes/filterRegionFeaturesById.pipe.ts b/src/atlasComponents/regionalFeatures/pipes/filterRegionFeaturesById.pipe.ts
deleted file mode 100644
index ed20ef67cfc96dcf3f21b1789cc7cdf82ac2fa01..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/pipes/filterRegionFeaturesById.pipe.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import { Pipe, PipeTransform } from "@angular/core";
-import { IFeature } from "../regionalFeature.service";
-import { getIdFromFullId } from 'common/util'
-@Pipe({
-  name: 'filterRegionFeaturesById',
-  pure: true
-})
-
-export class FilterRegionFeaturesById implements PipeTransform{
-  public transform(features: IFeature[], id: string){
-    const filterId = getIdFromFullId(id)
-    return features.filter(f => getIdFromFullId(f['@id']) === filterId)
-  }
-}
diff --git a/src/atlasComponents/regionalFeatures/pipes/filterRegionalFeaturesByType.pipe.ts b/src/atlasComponents/regionalFeatures/pipes/filterRegionalFeaturesByType.pipe.ts
deleted file mode 100644
index f5bf231bddf4cbb5fa1ae97121c6315074b2305e..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/pipes/filterRegionalFeaturesByType.pipe.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import { Pipe, PipeTransform } from "@angular/core";
-import { IFeature } from "../regionalFeature.service";
-
-@Pipe({
-  name: 'filterRegionalFeaturesBytype',
-  pure: true,
-})
-
-export class FilterRegionalFeaturesByTypePipe implements PipeTransform{
-  public transform(array: IFeature[], featureType: string){
-    return array.filter(f => featureType ? f.type === featureType : true )
-  }
-}
diff --git a/src/atlasComponents/regionalFeatures/pipes/findRegionFeatureById.pipe.ts b/src/atlasComponents/regionalFeatures/pipes/findRegionFeatureById.pipe.ts
deleted file mode 100644
index a083a28a5a843ef36c47c29183efd72ed7e28936..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/pipes/findRegionFeatureById.pipe.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import { Pipe, PipeTransform } from "@angular/core";
-import { IFeature } from "../regionalFeature.service";
-
-@Pipe({
-  name: 'findRegionFeaturebyId',
-  pure: true
-})
-
-export class FindRegionFEatureById implements PipeTransform{
-  public transform(features: IFeature[], id: string){
-    return features.find(f => f['@id'] === id)
-  }
-}
diff --git a/src/atlasComponents/regionalFeatures/regionGetAllFeatures.directive.ts b/src/atlasComponents/regionalFeatures/regionGetAllFeatures.directive.ts
deleted file mode 100644
index 920e1f0a98c7cd791514755d48351b63d4005684..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/regionGetAllFeatures.directive.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-import { Directive, EventEmitter, OnDestroy, OnInit, Output } from "@angular/core";
-import { Subscription } from "rxjs";
-import { RegionalFeaturesService } from "./regionalFeature.service";
-import { RegionFeatureBase } from "./singleFeatures/base/regionFeature.base";
-
-@Directive({
-  selector: '[region-get-all-features-directive]',
-  exportAs: 'rfGetAllFeatures'
-})
-
-export class RegionGetAllFeaturesDirective extends RegionFeatureBase implements OnDestroy, OnInit{
-  @Output()
-  loadingStateChanged: EventEmitter<boolean> = new EventEmitter()
-
-  private subscriptions: Subscription[] = []
-
-  /**
-   * since the base class has DI
-   * sub class needs to call super() with the correct DI
-   */
-  constructor(
-    rfService: RegionalFeaturesService
-  ){
-    super(rfService)
-  }
-
-  ngOnInit(){
-    this.subscriptions.push(
-      this.isLoading$.subscribe(val => {
-        this.loadingStateChanged.emit(val)
-      })
-    )
-  }
-  ngOnDestroy(){
-    while (this.subscriptions.length > 0) this.subscriptions.pop().unsubscribe()
-  }
-}
diff --git a/src/atlasComponents/regionalFeatures/regionalFeature.service.ts b/src/atlasComponents/regionalFeatures/regionalFeature.service.ts
deleted file mode 100644
index 0525ba80e033d9db9245d54f368b152b210fda11..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/regionalFeature.service.ts
+++ /dev/null
@@ -1,170 +0,0 @@
-import { HttpClient } from "@angular/common/http";
-import { Inject, Injectable, OnDestroy, Optional } from "@angular/core";
-import { PureContantService } from "src/util";
-import { getIdFromFullId, getRegionHemisphere, getStringIdsFromRegion, flattenReducer } from 'common/util'
-import { forkJoin, from, Observable, of, Subject, Subscription, throwError } from "rxjs";
-import { catchError, map, mapTo, shareReplay, switchMap } from "rxjs/operators";
-import { IHasId } from "src/util/interfaces";
-import { select, Store } from "@ngrx/store";
-import { viewerStateSelectedTemplateSelector } from "src/services/state/viewerState/selectors";
-import { viewerStateAddUserLandmarks, viewreStateRemoveUserLandmarks } from "src/services/state/viewerState/actions";
-import { uiStateMouseoverUserLandmark } from "src/services/state/uiState/selectors";
-import { APPEND_SCRIPT_TOKEN } from "src/util/constants";
-
-const libraries = [
-  'https://cdnjs.cloudflare.com/ajax/libs/d3/6.2.0/d3.min.js',
-  'https://cdnjs.cloudflare.com/ajax/libs/mathjax/3.1.2/es5/tex-svg.js'
-]
-
-export interface IFeature extends IHasId{
-  type: string
-  name: string
-  data?: IHasId[]
-}
-
-@Injectable({
-  providedIn: 'root'
-})
-
-export class RegionalFeaturesService implements OnDestroy{
-
-  public depScriptLoaded$: Observable<boolean>
-
-  private subs: Subscription[] = []
-  private templateSelected: any
-  constructor(
-    private http: HttpClient,
-    private pureConstantService: PureContantService,
-    private store$: Store<any>,
-    @Optional() @Inject(APPEND_SCRIPT_TOKEN) private appendScript: (src: string) => Promise<HTMLScriptElement>
-  ){
-    this.subs.push(
-      this.store$.pipe(
-        select(viewerStateSelectedTemplateSelector)
-      ).subscribe(val => this.templateSelected = val)
-    )
-
-    this.depScriptLoaded$ = this.appendScript
-      ? from(
-        libraries.map(this.appendScript)
-      ).pipe(
-        mapTo(true),
-        catchError(() => of(false)),
-        shareReplay(1),
-      )
-      : of(false)
-  }
-
-  public mapFeatToCmp = new Map<string, any>()
-
-  ngOnDestroy(){
-    while (this.subs.length > 0) this.subs.pop().unsubscribe()
-  }
-
-  public onHoverLandmarks$ = this.store$.pipe(
-    select(uiStateMouseoverUserLandmark)
-  )
-
-  public getAllFeaturesByRegion(_region: {['fullId']: string} | { id: { kg: {kgSchema: string, kgId: string} } }){
-    
-    const region = {
-      ..._region,
-    }
-    if (!region['fullId']) {
-      const { kgSchema, kgId } = region['id']?.kg || {}
-      if (kgSchema && kgId) region['fullId'] = `${kgSchema}/${kgId}`
-    }
-
-    if (!region['fullId']) throw new Error(`getAllFeaturesByRegion - region does not have fullId defined`)
-    const regionFullIds = getStringIdsFromRegion(region)
-    const hemisphereObj = (() => {
-      const hemisphere = getRegionHemisphere(region)
-      return hemisphere ? { hemisphere } : {}
-    })()
-
-    const refSpaceObj = this.templateSelected && this.templateSelected.fullId
-      ? { referenceSpaceId: getIdFromFullId(this.templateSelected.fullId) }
-      : {}
-    
-    return forkJoin(
-      regionFullIds.map(regionFullId => this.http.get<{features: IHasId[]}>(
-        `${this.pureConstantService.backendUrl}regionalFeatures/byRegion/${encodeURIComponent( regionFullId )}`,
-        {
-          params: {
-            ...hemisphereObj,
-            ...refSpaceObj,
-          },
-          responseType: 'json'
-        }
-      ).pipe(
-        switchMap(({ features }) => forkJoin(
-          features.map(({ ['@id']: featureId }) => 
-            this.http.get<IFeature>(
-              `${this.pureConstantService.backendUrl}regionalFeatures/byRegion/${encodeURIComponent( regionFullId )}/${encodeURIComponent( featureId )}`,
-              {
-                params: {
-                  ...hemisphereObj,
-                  ...refSpaceObj,
-                },
-                responseType: 'json'
-              }
-            )
-          )
-        )),
-      ))
-    ).pipe(
-      map((arr: IFeature[][]) => arr.reduce(flattenReducer, []))
-    )
-  }
-
-  public getFeatureData(region: any,feature: IFeature, data: IHasId){
-    if (!feature['@id']) throw new Error(`@id attribute for feature is required`)
-    if (!data['@id']) throw new Error(`@id attribute for data is required`)
-    const refSpaceObj = this.templateSelected && this.templateSelected.fullId
-      ? { referenceSpaceId: getIdFromFullId(this.templateSelected.fullId) }
-      : {}
-    const hemisphereObj = (() => {
-      const hemisphere = getRegionHemisphere(region)
-      return hemisphere ? { hemisphere } : {}
-    })()
-
-    const regionId = getIdFromFullId(region && region.fullId)
-    const url = regionId
-      ? `${this.pureConstantService.backendUrl}regionalFeatures/byRegion/${encodeURIComponent(regionId)}/${encodeURIComponent(feature['@id'])}/${encodeURIComponent(data['@id'])}`
-      : `${this.pureConstantService.backendUrl}regionalFeatures/byFeature/${encodeURIComponent(feature['@id'])}/${encodeURIComponent(data['@id'])}`
-    return this.http.get<IHasId>(
-      url,
-      {
-        params: {
-          ...hemisphereObj,
-          ...refSpaceObj,
-        },
-        responseType: 'json'
-      }
-    )
-  }
-
-  public addLandmarks(lms: IHasId[]) {
-    this.store$.dispatch(
-      viewerStateAddUserLandmarks({
-        landmarks: lms.map(lm => ({
-          ...lm,
-          id: lm['@id'],
-          name: `region feature: ${lm['@id']}`
-        }))
-      })
-    )
-  }
-
-  public removeLandmarks(lms: IHasId[]) {
-    this.store$.dispatch(
-      viewreStateRemoveUserLandmarks({
-        payload: {
-          landmarkIds: lms.map(l => l['@id'])
-        }
-      })
-    )
-  }
-
-  showDatafeatureInfo$ = new Subject<{ fullId: string } | { name: string, description: string }>()
-}
diff --git a/src/atlasComponents/regionalFeatures/singleFeatures/base/regionFeature.base.ts b/src/atlasComponents/regionalFeatures/singleFeatures/base/regionFeature.base.ts
deleted file mode 100644
index 21dde06920b0ddf29f384683c5851de9a171445f..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/singleFeatures/base/regionFeature.base.ts
+++ /dev/null
@@ -1,96 +0,0 @@
-import { Directive, EventEmitter, Input, Output, SimpleChanges } from "@angular/core"
-import { BehaviorSubject, forkJoin, Observable, of } from "rxjs"
-import { catchError, shareReplay, switchMap, tap } from "rxjs/operators"
-import { IHasId } from "src/util/interfaces"
-import { IFeature, RegionalFeaturesService } from "../../regionalFeature.service"
-
-@Directive()
-export class RegionFeatureBase{
-
-  private _feature: IFeature
-
-  private feature$ = new BehaviorSubject(null)
-  @Input()
-  set feature(val) {
-    this._feature = val
-    this.feature$.next(val)
-  }
-  get feature(){
-    return this._feature
-  }
-
-  @Input()
-  public region: any
-
-  @Output('feature-explorer-is-loading')
-  public dataIsLoadingEventEmitter: EventEmitter<boolean> = new EventEmitter()
-
-  public features: IFeature[] = []
-  public data$: Observable<IHasId[]>
-
-  /**
-   * using isLoading flag for conditional rendering of root element (or display loading spinner)
-   * this is necessary, or the transcluded tab will always be the active tab,
-   * as this.features as populated via async
-   */
-  public isLoading$ = new BehaviorSubject(false)
-  private _isLoading: boolean = false
-  get isLoading(){
-    return this._isLoading
-  }
-  set isLoading(val){
-    if (val !== this._isLoading)
-      this._isLoading = val
-    this.isLoading$.next(val)
-  }
-
-  public dataIsLoading$ = new BehaviorSubject(false)
-  private _dataIsLoading = false
-  set dataIsLoading(val) {
-    if (val === this._dataIsLoading) return
-    this._dataIsLoading = val
-    this.dataIsLoading$.next(val)
-    this.dataIsLoadingEventEmitter.emit(val)
-  }
-  get dataIsLoading(){
-    return this._dataIsLoading
-  }
-
-  ngOnChanges(changes: SimpleChanges){
-    if (changes.region && changes.region.previousValue !== changes.region.currentValue) {
-      this.isLoading = true
-      this.features = []
-      
-      const _ = (changes.region.currentValue
-        ? this._regionalFeatureService.getAllFeaturesByRegion(changes.region.currentValue)
-        : of([])
-      ).pipe(
-        /**
-         * region may have no fullId defined
-         * in this case, just emit []
-         */
-        catchError(() => of([]))
-      ).subscribe({
-        next: features => this.features = features,
-        complete: () => this.isLoading = false
-      })
-    }
-  }
-
-  constructor(
-    private _regionalFeatureService: RegionalFeaturesService
-  ){
-
-    /**
-    * once feature stops loading, watch for input feature
-    */
-    this.data$ = this.feature$.pipe(
-      tap(() => this.dataIsLoading = true),
-      switchMap((feature: IFeature) => forkJoin(
-        feature.data.map(datum => this._regionalFeatureService.getFeatureData(this.region, feature, datum)))
-      ),
-      tap(() => this.dataIsLoading = false),
-      shareReplay(1),
-    )
-  }
-}
diff --git a/src/atlasComponents/regionalFeatures/singleFeatures/iEEGRecordings/iEEGRecordings/iEEGRecordings.component.ts b/src/atlasComponents/regionalFeatures/singleFeatures/iEEGRecordings/iEEGRecordings/iEEGRecordings.component.ts
deleted file mode 100644
index 11fd197fbd45b569e35f15f5a9ae54ab13f07777..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/singleFeatures/iEEGRecordings/iEEGRecordings/iEEGRecordings.component.ts
+++ /dev/null
@@ -1,167 +0,0 @@
-import { Component, Inject, Optional, EventEmitter } from "@angular/core";
-import { Store } from "@ngrx/store";
-import { merge, Subject, Subscription } from "rxjs";
-import { debounceTime, map, scan, take } from "rxjs/operators";
-import { viewerStateChangeNavigation } from "src/services/state/viewerState/actions";
-import { RegionalFeaturesService } from "src/atlasComponents/regionalFeatures/regionalFeature.service";
-import { ClickInterceptor, CLICK_INTERCEPTOR_INJECTOR } from "src/util";
-import { IHasId } from "src/util/interfaces";
-import { RegionFeatureBase } from "../../base/regionFeature.base";
-import { ISingleFeature } from '../../interfaces'
-
-const selectedColor = [ 255, 0, 0 ]
-
-@Component({
-  templateUrl: './iEEGRecordings.template.html',
-  styleUrls: [
-    './iEEGRecordings.style.css'
-  ]
-})
-
-export class IEEGRecordingsCmp extends RegionFeatureBase implements ISingleFeature{
-  private landmarksLoaded: IHasId[] = []
-  private onDestroyCb: (() => void)[] = []
-  private sub: Subscription[] = []
-
-  constructor(
-    private regionFeatureService: RegionalFeaturesService,
-    private store: Store<any>,
-    @Optional() @Inject(CLICK_INTERCEPTOR_INJECTOR) private regClickIntp: ClickInterceptor,
-  ){
-    super(regionFeatureService)
-  }
-
-  public viewChanged = new EventEmitter<boolean>()
-
-  ngOnInit(){
-    if (this.regClickIntp) {
-      const { deregister, register } = this.regClickIntp
-      const clickIntp = this.clickIntp.bind(this)
-      register(clickIntp)
-      this.onDestroyCb.push(() => {
-        deregister(clickIntp)
-      })
-    }
-    this.sub.push(
-      this.data$.subscribe(data => {
-        const landmarksTobeLoaded: IHasId[] = []
-        
-        for (const datum of data) {
-          const electrodeId = datum['@id']
-          landmarksTobeLoaded.push(
-            ...datum['contactPoints'].map(({ ['@id']: contactPtId, position }) => {
-              return {
-                _: {
-                  electrodeId,
-                  contactPtId
-                },
-                ['@id']: `${electrodeId}#${contactPtId}`,
-                position
-              }
-            })
-          )
-        }
-        /**
-         * remove first, then add
-         */
-        if (this.landmarksLoaded.length > 0) this.regionFeatureService.removeLandmarks(this.landmarksLoaded)
-        if (landmarksTobeLoaded.length > 0) this.regionFeatureService.addLandmarks(landmarksTobeLoaded)
-        this.landmarksLoaded = landmarksTobeLoaded
-      })
-    )
-
-    this.sub.push(
-      this.dataIsLoading$.subscribe(() => this.viewChanged.emit(true))
-    )
-
-    this.onDestroyCb.push(() => {
-      if (this.landmarksLoaded.length > 0) this.regionFeatureService.removeLandmarks(this.landmarksLoaded)
-    })
-
-    this.sub.push(
-      this.openElectrodeId$.pipe(
-        debounceTime(200)
-      ).subscribe(arr => {
-
-        if (this.landmarksLoaded.length > 0) {
-          this.regionFeatureService.removeLandmarks(this.landmarksLoaded)
-          this.regionFeatureService.addLandmarks(this.landmarksLoaded.map(lm => {
-            const selected = arr.some(id => id === lm['_']['electrodeId'])
-            return {
-              ...lm,
-              color: selected ? selectedColor : null,
-              showInSliceView: selected
-            }
-          }))
-        }
-      })
-    )
-  }
-
-
-  ngOnDestroy(){
-    while(this.onDestroyCb.length > 0) this.onDestroyCb.pop()()
-    while(this.sub.length > 0) this.sub.pop().unsubscribe()
-  }
-
-  handleContactPtClk(contactPt: IHasId & { position: number[] }){
-    const { position } = contactPt
-    this.store.dispatch(
-      viewerStateChangeNavigation({
-        navigation: {
-          position: position.map(v => v * 1e6),
-          positionReal: true,
-          animation: {}
-        },
-      })
-    )
-  }
-
-  handleDatumExpansion(electrodeId: string, open: boolean){
-    /**
-     * TODO either debounce call here, or later down stream
-     */
-    if (open) this.exploreElectrode$.next(electrodeId)
-    else this.unExploreElectrode$.next(electrodeId)
-  }
-
-  private unExploreElectrode$ = new Subject<string>()
-  private exploreElectrode$ = new Subject<string>()
-  public openElectrodeId$ = merge(
-    this.unExploreElectrode$.pipe(
-      map(id => ({
-        add: null,
-        remove: id
-      }))
-    ),
-    this.exploreElectrode$.pipe(
-      map(id => ({
-        add: id,
-        remove: null
-      }))
-    )
-  ).pipe(
-    scan((acc, curr) => {
-      const { add, remove } = curr
-      const set = new Set(acc)
-      if (add) set.add(add)
-      if (remove) set.delete(remove)
-      return Array.from(set)
-    }, [])
-  )
-
-  private clickIntp(ev: any): boolean {
-    let hoveredLandmark = null
-    this.regionFeatureService.onHoverLandmarks$.pipe(
-      take(1)
-    ).subscribe(val => {
-      hoveredLandmark = val
-    })
-    if (!hoveredLandmark) return true
-    const isOne = this.landmarksLoaded.some(lm => {
-      return lm['_']['electrodeId'] === hoveredLandmark['_']['electrodeId']
-    })
-    if (!isOne) return true
-    this.exploreElectrode$.next(hoveredLandmark['_']['electrodeId'])
-  }
-}
diff --git a/src/atlasComponents/regionalFeatures/singleFeatures/iEEGRecordings/iEEGRecordings/iEEGRecordings.template.html b/src/atlasComponents/regionalFeatures/singleFeatures/iEEGRecordings/iEEGRecordings/iEEGRecordings.template.html
deleted file mode 100644
index 7a4b70d88a55e2e8325446c946c2035ed29d59bf..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/singleFeatures/iEEGRecordings/iEEGRecordings/iEEGRecordings.template.html
+++ /dev/null
@@ -1,56 +0,0 @@
-<ng-container *ngIf="!dataIsLoading; else loadingTmpl">
-  
-  <mat-accordion
-    class="ml-24px-n mr-24px-n d-block">
-    <mat-expansion-panel *ngFor="let datum of (data$ | async)"
-      [expanded]="openElectrodeId$ | async | includes : datum['@id']"
-      (opened)="handleDatumExpansion(datum['@id'], true)"
-      (closed)="handleDatumExpansion(datum['@id'], false)"
-      togglePosition="before">
-      <mat-expansion-panel-header>
-        <mat-panel-title>
-          Electrode
-        </mat-panel-title>
-        <mat-panel-description class="text-nowrap">
-          {{ datum['@id'] }}
-        </mat-panel-description>
-      </mat-expansion-panel-header>
-
-      <label for="task-list" class="d-block mat-h4 mt-4 text-muted">
-        Tasks
-      </label>
-      <section class="d-flex align-items-center mt-1">
-        <section id="task-list" class="flex-grow-1 flex-shrink-1 overflow-x-auto">
-          <div role="list">
-            <mat-chip *ngFor="let task of datum['tasks']" class="ml-1">
-              {{ task }}
-            </mat-chip>
-          </div>
-        </section>
-      </section>
-
-      <label for="contact-points-list" class="d-block mat-h4 mt-4 text-muted">
-        Contact Points
-      </label>
-      <section class="d-flex align-items-center mt-1">
-        <section id="contact-points-list" class="flex-grow-1 flex-shrink-1 overflow-x-auto">
-          <div role="list">
-            <mat-chip *ngFor="let contactPt of datum['contactPoints']"
-              [matTooltip]="contactPt['position']"
-              (click)="handleContactPtClk(contactPt)"
-              class="ml-1">
-              {{ contactPt['@id'] }}
-            </mat-chip>
-          </div>
-        </section>
-      </section>
-  
-    </mat-expansion-panel>
-  </mat-accordion>
-
-</ng-container>
-
-<!-- loading template -->
-<ng-template #loadingTmpl>
-  <spinner-cmp></spinner-cmp>
-</ng-template>
diff --git a/src/atlasComponents/regionalFeatures/singleFeatures/iEEGRecordings/module.ts b/src/atlasComponents/regionalFeatures/singleFeatures/iEEGRecordings/module.ts
deleted file mode 100644
index 76d6cd9f67dba1c93bc6e51ee9c845bb035d4766..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/singleFeatures/iEEGRecordings/module.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-import { CommonModule } from "@angular/common";
-import { NgModule } from "@angular/core";
-import { ComponentsModule } from "src/components";
-import { AngularMaterialModule } from "src/sharedModules";
-import { UtilModule } from "src/util";
-import { RegionalFeaturesService } from "../../regionalFeature.service";
-import { IEEGRecordingsCmp } from "./iEEGRecordings/iEEGRecordings.component";
-
-@NgModule({
-  imports: [
-    CommonModule,
-    UtilModule,
-    AngularMaterialModule,
-    ComponentsModule,
-  ],
-  declarations: [
-    IEEGRecordingsCmp
-  ],
-  exports: [
-    IEEGRecordingsCmp
-  ]
-})
-
-export class FeatureIEEGRecordings{
-  constructor(
-    rService: RegionalFeaturesService
-  ){
-    rService.mapFeatToCmp.set('iEEG recording', IEEGRecordingsCmp)
-  }
-}
diff --git a/src/atlasComponents/regionalFeatures/singleFeatures/interfaces.ts b/src/atlasComponents/regionalFeatures/singleFeatures/interfaces.ts
deleted file mode 100644
index bd3861d391b4bcc90d262ee16e124b51e4affee2..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/singleFeatures/interfaces.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import { EventEmitter } from "@angular/core";
-import { IFeature } from "../regionalFeature.service";
-
-export interface ISingleFeature{
-  feature: IFeature
-  region: any
-  viewChanged: EventEmitter<boolean>
-}
diff --git a/src/atlasComponents/regionalFeatures/singleFeatures/receptorDensity/filterReceptorBytype.pipe.ts b/src/atlasComponents/regionalFeatures/singleFeatures/receptorDensity/filterReceptorBytype.pipe.ts
deleted file mode 100644
index 1b0a91dd80079acf6e3cc6709c4ac3cc74ec5f97..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/singleFeatures/receptorDensity/filterReceptorBytype.pipe.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import { Pipe, PipeTransform } from "@angular/core";
-import { IHasId } from "src/util/interfaces";
-
-@Pipe({
-  name: 'filterReceptorByType',
-  pure: true
-})
-
-export class FilterReceptorByType implements PipeTransform{
-  public transform(arr: IHasId[], qualifer: string): IHasId[]{
-    return (arr || []).filter(({ ['@id']: dId }) => dId.indexOf(qualifer) >= 0)
-  }
-}
diff --git a/src/atlasComponents/regionalFeatures/singleFeatures/receptorDensity/getAllReceptors.pipe.ts b/src/atlasComponents/regionalFeatures/singleFeatures/receptorDensity/getAllReceptors.pipe.ts
deleted file mode 100644
index ba3016186957b4851e469792cc834110152ddb1b..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/singleFeatures/receptorDensity/getAllReceptors.pipe.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import { Pipe, PipeTransform } from "@angular/core";
-import { IHasId } from "src/util/interfaces";
-
-@Pipe({
-  name: 'getAllReceptors',
-  pure: true
-})
-
-export class GetAllReceptorsPipe implements PipeTransform{
-  public transform(arr: IHasId[]): string[]{
-    return (arr || []).reduce((acc, curr) => {
-      const thisType = /_(pr|ar)_([a-zA-Z0-9_]+)\./.exec(curr['@id'])
-      if (!thisType) return acc
-      return new Set(acc).has(thisType) ? acc : acc.concat(thisType[2])
-    }, [])
-  }
-}
\ No newline at end of file
diff --git a/src/atlasComponents/regionalFeatures/singleFeatures/receptorDensity/getId.pipe.ts b/src/atlasComponents/regionalFeatures/singleFeatures/receptorDensity/getId.pipe.ts
deleted file mode 100644
index 3af1a02bd4a98455bc9b0166ff4adfc5afa53646..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/singleFeatures/receptorDensity/getId.pipe.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import { Pipe, PipeTransform } from "@angular/core";
-
-@Pipe({
-  name: 'getId',
-  pure: true
-})
-
-export class GetIdPipe implements PipeTransform{
-  public transform(fullId: string): string{
-    const re = /\/([a-f0-9-]+)$/.exec(fullId)
-    return (re && re[1]) || null
-  }
-}
diff --git a/src/atlasComponents/regionalFeatures/singleFeatures/receptorDensity/getUrl.pipe.ts b/src/atlasComponents/regionalFeatures/singleFeatures/receptorDensity/getUrl.pipe.ts
deleted file mode 100644
index f4fdfab5613c225aa293a05b45c9d0ab8be666ab..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/singleFeatures/receptorDensity/getUrl.pipe.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-import { Pipe, PipeTransform } from "@angular/core";
-import { IHasId } from "src/util/interfaces";
-
-interface IReceptorDatum extends IHasId{
-  ['@context']: {
-    [key: string]: string
-  }
-  filename: string
-  mimetype: string
-  url: string | {
-    url: string
-    ['receptors.tsv']: string
-  }
-}
-
-interface IHRef{
-  url: string
-  filename: string
-}
-
-@Pipe({
-  name: 'getUrls',
-  pure: true
-})
-
-export class GetUrlsPipe implements PipeTransform{
-  public transform(input: IReceptorDatum): IHRef[]{
-    const output: IHRef[] = []
-    let _url = typeof input.url === 'string'
-      ? input.url
-      : input.url.url
-
-    for (const key in (input['@context'] || {})) {
-      _url = _url.replace(`${key}:`, input['@context'][key])
-    }
-
-    const match = /\/([\w-.]+)$/.exec(_url)
-    output.push({
-      url: _url,
-      filename: match ? match[1] : 'download'
-    })
-    return output
-  }
-}
-
diff --git a/src/atlasComponents/regionalFeatures/singleFeatures/receptorDensity/module.ts b/src/atlasComponents/regionalFeatures/singleFeatures/receptorDensity/module.ts
deleted file mode 100644
index e8ab67e4273b33310171ba1b36f7199bf5286008..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/singleFeatures/receptorDensity/module.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-import { CommonModule } from "@angular/common";
-import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from "@angular/core";
-import { AngularMaterialModule } from "src/sharedModules";
-import { RegionalFeaturesService } from "../../regionalFeature.service";
-import { FilterReceptorByType } from "./filterReceptorBytype.pipe";
-import { GetAllReceptorsPipe } from "./getAllReceptors.pipe";
-import { GetIdPipe } from "./getId.pipe";
-import { GetUrlsPipe } from "./getUrl.pipe";
-import { ReceptorDensityFeatureCmp } from "./receptorDensity/receptorDensity.component";
-
-@NgModule({
-  imports: [
-    CommonModule,
-    AngularMaterialModule,
-  ],
-  declarations: [
-    ReceptorDensityFeatureCmp,
-    FilterReceptorByType,
-    GetIdPipe,
-    GetAllReceptorsPipe,
-    GetUrlsPipe,
-  ],
-  schemas: [
-    CUSTOM_ELEMENTS_SCHEMA
-  ]
-})
-
-export class ReceptorDensityModule{
-  constructor(
-    rService: RegionalFeaturesService
-  ){
-    rService.mapFeatToCmp.set(`Receptor density measurement`, ReceptorDensityFeatureCmp)
-  }
-}
diff --git a/src/atlasComponents/regionalFeatures/singleFeatures/receptorDensity/receptorDensity/receptorDensity.component.ts b/src/atlasComponents/regionalFeatures/singleFeatures/receptorDensity/receptorDensity/receptorDensity.component.ts
deleted file mode 100644
index 52cda125d7aa02bfaa062a121b62b94d365fa612..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/singleFeatures/receptorDensity/receptorDensity/receptorDensity.component.ts
+++ /dev/null
@@ -1,70 +0,0 @@
-import { Component, ElementRef, EventEmitter, HostListener, OnDestroy, Optional } from "@angular/core";
-import { fromEvent, Observable, of, Subscription } from "rxjs";
-import { RegionalFeaturesService } from "src/atlasComponents/regionalFeatures/regionalFeature.service";
-import { PureContantService } from "src/util";
-import { RegionFeatureBase } from "../../base/regionFeature.base";
-import { ISingleFeature } from "../../interfaces";
-import { CONST } from 'common/constants'
-import { environment } from 'src/environments/environment'
-
-const {
-  RECEPTOR_FP_CAPTION,
-  RECEPTOR_PR_CAPTION,
-  RECEPTOR_AR_CAPTION,
-} = CONST
-@Component({
-  templateUrl: './receptorDensity.template.html',
-  styleUrls: [
-    './receptorDensity.style.css'
-  ]
-})
-
-export class ReceptorDensityFeatureCmp extends RegionFeatureBase implements ISingleFeature, OnDestroy{
-
-  public RECEPTOR_FP_CAPTION = RECEPTOR_FP_CAPTION
-  public RECEPTOR_PR_CAPTION = RECEPTOR_PR_CAPTION
-  public RECEPTOR_AR_CAPTION = RECEPTOR_AR_CAPTION
-
-  public DS_PREVIEW_URL = environment.DATASET_PREVIEW_URL
-  viewChanged: EventEmitter<null> = new EventEmitter()
-
-  private WEB_COMPONENT_MOUSEOVER_EVENT_NAME = 'kg-ds-prv-regional-feature-mouseover'
-  private webComponentOnHover: string = null
-
-  public selectedReceptor: string
-
-  public darktheme$: Observable<boolean>
-
-  private subs: Subscription[] = []
-  public depScriptLoaded$: Observable<boolean>
-  constructor(
-    regService: RegionalFeaturesService,
-    el: ElementRef,
-    @Optional() pureConstantService: PureContantService
-  ){
-    super(regService)
-    this.depScriptLoaded$ = regService.depScriptLoaded$
-    if (pureConstantService) {
-      this.darktheme$ = pureConstantService.darktheme$
-    } else {
-      this.darktheme$ = of(false)
-    }
-
-    this.subs.push(
-      fromEvent(el.nativeElement, this.WEB_COMPONENT_MOUSEOVER_EVENT_NAME).subscribe((ev: CustomEvent) => {
-        this.webComponentOnHover = ev.detail?.data?.receptor?.label
-      })
-    )
-  }
-
-  @HostListener('click')
-  onClick(){
-    if (this.webComponentOnHover) {
-      this.selectedReceptor = this.webComponentOnHover
-    }
-  }
-
-  ngOnDestroy(){
-    while(this.subs.length > 0) this.subs.pop().unsubscribe()
-  }
-}
diff --git a/src/atlasComponents/regionalFeatures/singleFeatures/receptorDensity/receptorDensity/receptorDensity.style.css b/src/atlasComponents/regionalFeatures/singleFeatures/receptorDensity/receptorDensity/receptorDensity.style.css
deleted file mode 100644
index 0437be86256eb2a7039e72e33e80de0a92ea62ef..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/singleFeatures/receptorDensity/receptorDensity/receptorDensity.style.css
+++ /dev/null
@@ -1,11 +0,0 @@
-kg-dataset-previewer
-{
-  display: block;
-  height: 20em;
-}
-
-kg-ds-prv-regional-feature-view
-{
-  display: block;
-  min-height: 20em;
-}
diff --git a/src/atlasComponents/regionalFeatures/singleFeatures/receptorDensity/receptorDensity/receptorDensity.template.html b/src/atlasComponents/regionalFeatures/singleFeatures/receptorDensity/receptorDensity/receptorDensity.template.html
deleted file mode 100644
index f1454e06ca2bef47d6629a80a3907497b650ed76..0000000000000000000000000000000000000000
--- a/src/atlasComponents/regionalFeatures/singleFeatures/receptorDensity/receptorDensity/receptorDensity.template.html
+++ /dev/null
@@ -1,143 +0,0 @@
-<label for="fingerprint-cmp" class="d-inline mat-h4 mt-4 text-muted">
-  Fingerprint :
-</label>
-
-<ng-container *ngFor="let datum of (data$ | async | filterReceptorByType : '_fp_')">
-
-  <ng-container *ngTemplateOutlet="labelTmpl; context: {
-    for: 'fingerprint-cmp',
-    label: RECEPTOR_FP_CAPTION
-  }">
-  </ng-container>
-
-  <div class="d-block">
-    <ng-container *ngTemplateOutlet="datasetPreviewTmpl; context: {
-      id: 'fingerprint-cmp',
-      kgId: (feature['@id'] | getId),
-      filename: datum['@id'],
-      datum: datum
-    }">
-    </ng-container>
-  </div>
-
-</ng-container>
-
-<mat-divider></mat-divider>
-
-<ng-container *ngIf="data$ | async | getAllReceptors as allReceptors; else selectPlaceHolderTmpl">
-
-  <mat-form-field class="mt-2 w-100" *ngIf="allReceptors.length > 0; else selectPlaceHolderTmpl">
-    <mat-label>
-      Select a receptor
-    </mat-label>
-    <mat-select [(value)]="selectedReceptor">
-      <mat-option
-        *ngFor="let receptor of allReceptors"
-        [value]="receptor">
-        {{ receptor }}
-      </mat-option>
-    </mat-select>
-  </mat-form-field>
-
-</ng-container>
-
-<ng-template #selectPlaceHolderTmpl>
-  <span class="text-muted">No profile or autoradiographs available.</span>
-</ng-template>
-
-<ng-template [ngIf]="selectedReceptor">
-  <ng-container *ngTemplateOutlet="prArTmpl; context: { filter: '_pr_', label: 'Profile' }">
-  </ng-container>
-  <ng-container *ngTemplateOutlet="prArTmpl; context: { filter: '_ar_', label: 'Autoradiograph' }">
-  </ng-container>
-</ng-template>
-
-<!-- ar/pr template -->
-<ng-template #prArTmpl let-label="label" let-filter="filter">
-  <ng-container *ngFor="let datum of (data$ | async | filterReceptorByType : selectedReceptor | filterReceptorByType : filter); let first = first">
-    <ng-template [ngIf]="first">
-      <label [attr.for]="label + '-cmp'" class="d-inline mat-h4 mt-4 text-muted">
-        {{ label }} : 
-      </label>
-    </ng-template>
-
-    <ng-container *ngTemplateOutlet="labelTmpl; context: {
-      for: label + '-cmp',
-      label: 'Autoradiograph' ? RECEPTOR_AR_CAPTION : RECEPTOR_PR_CAPTION
-    }">
-    </ng-container>
-
-    <div class="d-block">
-      <ng-container *ngTemplateOutlet="datasetPreviewTmpl; context: {
-        id: label + '-cmp',
-        kgId: (feature['@id'] | getId),
-        filename: datum['@id'],
-        datum: datum
-      }">
-      </ng-container>
-    </div>
-
-  </ng-container>
-</ng-template>
-
-
-<!-- display preview tmpl -->
-<ng-template #datasetPreviewTmpl
-  let-id="id"
-  let-kgId="kgId"
-  let-filename="filename"
-  let-datum="datum">
-
-  <!-- download btns -->
-  <ng-container *ngFor="let urlObj of datum | getUrls">
-    <ng-container *ngTemplateOutlet="downloadBtnTmpl; context: {
-      url: urlObj.url,
-      filename: urlObj.filename,
-      tooltip: 'download ' + urlObj.filename
-    }">
-    </ng-container>
-  </ng-container>
-
-  <!-- render preview -->
-  <kg-ds-prv-regional-feature-view
-    *ngIf="depScriptLoaded$ | async; else fallbackTmpl"
-    [attr.id]="id"
-    [darkmode]="darktheme$ | async"
-    (renderEvent)="viewChanged.emit()"
-    [backendUrl]="DS_PREVIEW_URL"
-    [kgId]="kgId"
-    [filename]="filename">
-  </kg-ds-prv-regional-feature-view>
-
-  <ng-template #fallbackTmpl>
-    <kg-dataset-previewer
-      [attr.id]="id"
-      (renderEvent)="viewChanged.emit()"
-      [backendUrl]="DS_PREVIEW_URL"
-      [kgId]="kgId"
-      [filename]="filename">
-    </kg-dataset-previewer>
-  </ng-template>
-</ng-template>
-
-<ng-template #downloadBtnTmpl
-  let-url="url"
-  let-filename="filename"
-  let-tooltip="tooltip">
-  <a [href]="url"
-    class="d-inline-block"
-    [download]="filename"
-    [matTooltip]="tooltip">
-    <button mat-icon-button>
-      <i class="fas fa-download"></i>
-    </button>
-  </a>
-</ng-template>
-
-<ng-template #labelTmpl let-label="label" let-for="for">
-  <label [attr.for]="for" class="d-inline text-muted">
-    <span>
-      {{ label }}
-    </span>
-  </label>
-</ng-template>
\ No newline at end of file
diff --git a/src/atlasComponents/sapi/constants.ts b/src/atlasComponents/sapi/constants.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b31cfc1b14703de11f2c5e2efaee1cacb369347b
--- /dev/null
+++ b/src/atlasComponents/sapi/constants.ts
@@ -0,0 +1,16 @@
+export const IDS = {
+  ATLAES: {
+    HUMAN: "juelich/iav/atlas/v1.0.0/1",
+    RAT: "minds/core/parcellationatlas/v1.0.0/522b368e-49a3-49fa-88d3-0870a307974a",
+  },
+  TEMPLATES: {
+    BIG_BRAIN: "minds/core/referencespace/v1.0.0/a1655b99-82f1-420f-a3c2-fe80fd4c8588",
+    MNI152: "minds/core/referencespace/v1.0.0/dafcffc5-4826-4bf1-8ff6-46b8a31ff8e2",
+    COLIN27: "minds/core/referencespace/v1.0.0/7f39f7be-445b-47c0-9791-e971c0b6d992",
+    WAXHOLM: "minds/core/referencespace/v1.0.0/d5717c4a-0fa1-46e6-918c-b8003069ade8",
+  },
+  PARCELLATION: {
+    JBA29: "minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579-290",
+    WAXHOLMV4: "minds/core/parcellationatlas/v1.0.0/ebb923ba-b4d5-4b82-8088-fa9215c2e1fe-v4",
+  }
+}
diff --git a/src/atlasComponents/sapi/core/index.ts b/src/atlasComponents/sapi/core/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a7fc4cbb696db25e9fa2159b5862e23c0bdb260e
--- /dev/null
+++ b/src/atlasComponents/sapi/core/index.ts
@@ -0,0 +1,4 @@
+export { SAPIAtlas } from "./sapiAtlas"
+export { SAPISpace } from "./sapiSpace"
+export { SAPIParcellation } from "./sapiParcellation"
+export { SAPIRegion } from "./sapiRegion"
diff --git a/src/atlasComponents/sapi/core/sapiAtlas.ts b/src/atlasComponents/sapi/core/sapiAtlas.ts
new file mode 100644
index 0000000000000000000000000000000000000000..788a803bd32eb8cf8b5dcc98545284abbf4fe056
--- /dev/null
+++ b/src/atlasComponents/sapi/core/sapiAtlas.ts
@@ -0,0 +1,5 @@
+import { SAPI } from "../sapi.service";
+
+export class SAPIAtlas{
+  constructor(private sapi: SAPI, public id: string){}
+}
diff --git a/src/atlasComponents/sapi/core/sapiParcellation.ts b/src/atlasComponents/sapi/core/sapiParcellation.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4767cc2897ab0ed6c6567f3ee727ad2f73fece04
--- /dev/null
+++ b/src/atlasComponents/sapi/core/sapiParcellation.ts
@@ -0,0 +1,62 @@
+import { Observable } from "rxjs"
+import { SapiVolumeModel } from ".."
+import { SAPI } from "../sapi.service"
+import {SapiParcellationFeatureModel, SapiParcellationModel, SapiQueryPriorityArg, SapiRegionModel} from "../type"
+
+type PaginationQuery = {
+  size: number
+  page: number
+}
+
+type ParcellationPaginationQuery = {
+  type?: string
+  size?: number
+  page: number
+}
+
+export class SAPIParcellation{
+  constructor(private sapi: SAPI, public atlasId: string, public id: string){
+
+  }
+
+  getDetail(queryParam?: SapiQueryPriorityArg): Observable<SapiParcellationModel>{
+    return this.sapi.httpGet<SapiParcellationModel>(
+      `${this.sapi.bsEndpoint}/atlases/${encodeURIComponent(this.atlasId)}/parcellations/${encodeURIComponent(this.id)}`,
+      null,
+      queryParam
+    )
+  }
+
+  getRegions(spaceId: string, queryParam?: SapiQueryPriorityArg): Observable<SapiRegionModel[]> {
+    return this.sapi.httpGet<SapiRegionModel[]>(
+      `${this.sapi.bsEndpoint}/atlases/${encodeURIComponent(this.atlasId)}/parcellations/${encodeURIComponent(this.id)}/regions`,
+      {
+        space_id: spaceId
+      },
+      queryParam
+    )
+  }
+  getVolumes(): Observable<SapiVolumeModel[]>{
+    return this.sapi.httpGet<SapiVolumeModel[]>(
+      `${this.sapi.bsEndpoint}/atlases/${encodeURIComponent(this.atlasId)}/parcellations/${encodeURIComponent(this.id)}/volumes`
+    )
+  }
+
+  getFeatures(parcPagination?: ParcellationPaginationQuery, queryParam?: SapiQueryPriorityArg): Observable<SapiParcellationFeatureModel[]> {
+    return this.sapi.httpGet<SapiParcellationFeatureModel[]>(
+      `${this.sapi.bsEndpoint}/atlases/${encodeURIComponent(this.atlasId)}/parcellations/${encodeURIComponent(this.id)}/features`,
+      {
+        type: parcPagination?.type,
+        size: parcPagination?.size?.toString() || '5',
+        page: parcPagination?.page.toString() || '0',
+      },
+      queryParam
+    )
+  }
+
+  getFeatureInstance(instanceId: string): Observable<SapiParcellationFeatureModel> {
+    return this.sapi.http.get<SapiParcellationFeatureModel>(
+      `${this.sapi.bsEndpoint}/atlases/${encodeURIComponent(this.atlasId)}/parcellations/${encodeURIComponent(this.id)}/features/${encodeURIComponent(instanceId)}`,
+    )
+  }
+}
diff --git a/src/atlasComponents/sapi/core/sapiRegion.ts b/src/atlasComponents/sapi/core/sapiRegion.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8248992c16ef86a1bbd6e28c395b8dd08db9d66f
--- /dev/null
+++ b/src/atlasComponents/sapi/core/sapiRegion.ts
@@ -0,0 +1,105 @@
+import { SAPI } from "..";
+import { SapiRegionalFeatureModel, SapiRegionMapInfoModel, SapiRegionModel, cleanIeegSessionDatasets, SapiIeegSessionModel, CleanedIeegDataset, SapiVolumeModel, PaginatedResponse } from "../type";
+import { strToRgb, hexToRgb } from 'common/util'
+import { merge, Observable, of } from "rxjs";
+import { catchError, map, scan } from "rxjs/operators";
+
+export class SAPIRegion{
+
+  static GetDisplayColor(region: SapiRegionModel): [number, number, number]{
+    if (!region) {
+      throw new Error(`region must be provided!`)
+    }
+    if (region.hasAnnotation?.displayColor) {
+      return hexToRgb(region.hasAnnotation.displayColor)
+    }
+    return strToRgb(JSON.stringify(region))
+  }
+
+  private prefix: string
+
+  constructor(
+    private sapi: SAPI,
+    public atlasId: string,
+    public parcId: string,
+    public id: string,
+  ){
+    this.prefix = `${this.sapi.bsEndpoint}/atlases/${encodeURIComponent(this.atlasId)}/parcellations/${encodeURIComponent(this.parcId)}/regions/${encodeURIComponent(this.id)}`
+  }
+
+  getFeatures(spaceId: string): Observable<(SapiRegionalFeatureModel | CleanedIeegDataset)[]> {
+    return merge(
+      this.sapi.httpGet<SapiRegionalFeatureModel[]>(
+        `${this.prefix}/features`,
+        {
+          space_id: spaceId
+        }
+      ).pipe(
+        catchError((err, obs) => {
+          return of([])
+        })
+      ),
+      spaceId
+        ? this.sapi.getSpace(this.atlasId, spaceId).getFeatures({ parcellationId: this.parcId, region: this.id }).pipe(
+          catchError((err, obs) => {
+            return of([])
+          }),
+          map(feats => {
+            const ieegSessions: SapiIeegSessionModel[] = feats.filter(feat => feat["@type"] === "siibra/features/ieegSession")
+            return cleanIeegSessionDatasets(ieegSessions)
+          }),
+        )
+        : of([] as CleanedIeegDataset[])
+    ).pipe(
+      scan((acc, curr) => [...acc, ...curr], [])
+    )
+  }
+
+  getFeatureInstance(instanceId: string, spaceId: string = null): Observable<SapiRegionalFeatureModel> {
+    return this.sapi.httpGet<SapiRegionalFeatureModel>(
+      `${this.prefix}/features/${encodeURIComponent(instanceId)}`,
+      {
+        space_id: spaceId
+      }
+    )
+  }
+
+  getMapInfo(spaceId: string): Observable<SapiRegionMapInfoModel> {
+    return this.sapi.http.get<SapiRegionMapInfoModel>(
+      `${this.prefix}/regional_map/info`,
+      {
+        params: {
+          space_id: spaceId
+        }
+      }
+    )
+  }
+
+  getMapUrl(spaceId: string): string {
+    return `${this.prefix}/regional_map/map?space_id=${encodeURI(spaceId)}`
+  }
+
+  getVolumes(): Observable<PaginatedResponse<SapiVolumeModel>>{
+    const url = `${this.prefix}/volumes`
+    return this.sapi.httpGet<PaginatedResponse<SapiVolumeModel>>(
+      url
+    )
+  }
+
+  getVolumeInstance(volumeId: string): Observable<SapiVolumeModel> {
+    const url = `${this.prefix}/volumes/${encodeURIComponent(volumeId)}`
+    return this.sapi.httpGet<SapiVolumeModel>(
+      url
+    )
+  }
+
+  getDetail(spaceId: string): Observable<SapiRegionModel> {
+    const url = `${this.prefix}/${encodeURIComponent(this.id)}`
+    return this.sapi.httpGet<SapiRegionModel>(
+      url,
+      {
+        space_id: spaceId
+      }
+    )
+  }
+}
diff --git a/src/atlasComponents/sapi/core/sapiSpace.ts b/src/atlasComponents/sapi/core/sapiSpace.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3effd6f16345aa82eebd3cf94b9f83118fb9546e
--- /dev/null
+++ b/src/atlasComponents/sapi/core/sapiSpace.ts
@@ -0,0 +1,70 @@
+import { Observable } from "rxjs"
+import { SAPI } from '../sapi.service'
+import { camelToSnake } from 'common/util'
+import {SapiQueryPriorityArg, SapiSpaceModel, SapiSpatialFeatureModel, SapiVolumeModel} from "../type"
+
+type FeatureResponse = {
+  features: {
+    [key: string]: string
+  }
+}
+
+type RegionalSpatialFeatureOpts = {
+  parcellationId: string
+  region: string
+}
+
+type BBoxSpatialFEatureOpts = {
+  bbox: string
+}
+
+type SpatialFeatureOpts = RegionalSpatialFeatureOpts | BBoxSpatialFEatureOpts
+
+export class SAPISpace{
+
+  constructor(private sapi: SAPI, public atlasId: string, public id: string){}
+
+  getModalities(param?: SapiQueryPriorityArg): Observable<FeatureResponse> {
+    return this.sapi.httpGet<FeatureResponse>(
+      `${this.sapi.bsEndpoint}/atlases/${encodeURIComponent(this.atlasId)}/spaces/${encodeURIComponent(this.id)}/features`,
+      null,
+      param
+    )
+  }
+
+  getFeatures(opts: SpatialFeatureOpts): Observable<SapiSpatialFeatureModel[]> {
+    const query: Record<string, string> = {}
+    for (const [key, value] of Object.entries(opts)) {
+      query[camelToSnake(key)] = value
+    }
+    return this.sapi.httpGet<SapiSpatialFeatureModel[]>(
+      `${this.sapi.bsEndpoint}/atlases/${encodeURIComponent(this.atlasId)}/spaces/${encodeURIComponent(this.id)}/features`,
+      query
+    )
+  }
+
+  getFeatureInstance(instanceId: string, opts: SpatialFeatureOpts): Observable<SapiSpatialFeatureModel> {
+    const query: Record<string, string> = {}
+    for (const [key, value] of Object.entries(opts)) {
+      query[camelToSnake(key)] = value
+    }
+    return this.sapi.httpGet<SapiSpatialFeatureModel>(
+      `${this.sapi.bsEndpoint}/atlases/${encodeURIComponent(this.atlasId)}/spaces/${encodeURIComponent(this.id)}/features/${encodeURIComponent(instanceId)}`,
+      query
+    )
+  }
+
+  getDetail(param?: SapiQueryPriorityArg): Observable<SapiSpaceModel>{
+    return this.sapi.httpGet<SapiSpaceModel>(
+      `${this.sapi.bsEndpoint}/atlases/${encodeURIComponent(this.atlasId)}/spaces/${encodeURIComponent(this.id)}`,
+      null,
+      param
+    )
+  }
+
+  getVolumes(): Observable<SapiVolumeModel[]>{
+    return this.sapi.httpGet<SapiVolumeModel[]>(
+      `${this.sapi.bsEndpoint}/atlases/${encodeURIComponent(this.atlasId)}/spaces/${encodeURIComponent(this.id)}/volumes`,
+    )
+  }
+}
diff --git a/src/atlasComponents/sapi/core/space/interSpaceCoordXform.service.spec.ts b/src/atlasComponents/sapi/core/space/interSpaceCoordXform.service.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..30542d6fc0441e31a41fc7ac5b372208ede998d1
--- /dev/null
+++ b/src/atlasComponents/sapi/core/space/interSpaceCoordXform.service.spec.ts
@@ -0,0 +1,114 @@
+import { InterSpaceCoordXformSvc, VALID_TEMPLATE_SPACE_NAMES } from './interSpaceCoordXform.service'
+import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'
+import { TestBed, fakeAsync, tick } from '@angular/core/testing'
+
+describe('InterSpaceCoordXformSvc.service.spec.ts', () => {
+  describe('InterSpaceCoordXformSvc', () => {
+    beforeEach(() => {
+      TestBed.configureTestingModule({
+        imports: [
+          HttpClientTestingModule
+        ],
+        providers: [
+          InterSpaceCoordXformSvc
+        ]
+      })
+    })
+
+    afterEach(() => {
+      const ctrl = TestBed.inject(HttpTestingController)
+      ctrl.verify()
+    })
+
+    describe('#transform', () => {
+      it('should instantiate service properly', () => {
+        const service = TestBed.inject(InterSpaceCoordXformSvc)
+        expect(service).toBeTruthy()
+        expect(service.transform).toBeTruthy()
+      })
+
+      it('should transform argument properly', () => {
+        const service = TestBed.inject(InterSpaceCoordXformSvc)
+        const httpTestingController = TestBed.inject(HttpTestingController)
+
+        // subscriptions are necessary for http fetch to occur
+        service.transform(
+          VALID_TEMPLATE_SPACE_NAMES.MNI152,
+          VALID_TEMPLATE_SPACE_NAMES.BIG_BRAIN,
+          [1,2,3]
+        ).subscribe((_ev) => {
+          
+        })
+        const req = httpTestingController.expectOne(service['url'])
+        expect(req.request.method).toEqual('POST')
+        expect(
+          JSON.parse(req.request.body)
+        ).toEqual({
+          'source_space': VALID_TEMPLATE_SPACE_NAMES.MNI152,
+          'target_space': VALID_TEMPLATE_SPACE_NAMES.BIG_BRAIN,
+          'source_points': [
+            [1e-6, 2e-6, 3e-6]
+          ]
+        })
+        req.flush({})
+      })
+
+
+      it('should transform response properly', () => {
+
+        const service = TestBed.inject(InterSpaceCoordXformSvc)
+        const httpTestingController = TestBed.inject(HttpTestingController)
+
+        service.transform(
+          VALID_TEMPLATE_SPACE_NAMES.MNI152,
+          VALID_TEMPLATE_SPACE_NAMES.BIG_BRAIN,
+          [1,2,3]
+        ).subscribe(({ status, result }) => {
+          expect(status).toEqual('completed')
+          expect(result).toEqual([1e6, 2e6, 3e6])
+        })
+        const req = httpTestingController.expectOne(service['url'])
+        req.flush({
+          'target_points':[
+            [1, 2, 3]
+          ]
+        })
+      })
+
+      it('if server returns >=400, fallback gracefully', done => {
+        const service = TestBed.inject(InterSpaceCoordXformSvc)
+        const httpTestingController = TestBed.inject(HttpTestingController)
+
+        service.transform(
+          VALID_TEMPLATE_SPACE_NAMES.MNI152,
+          VALID_TEMPLATE_SPACE_NAMES.BIG_BRAIN,
+          [1,2,3]
+        ).subscribe(({ status }) => {
+          expect(status).toEqual('error')
+          done()
+        })
+        const req = httpTestingController.expectOne(service['url'])
+        
+        req.flush('intercepted', { status: 500, statusText: 'internal server error' })
+      })
+
+      it('if server does not respond after 3s, fallback gracefully', fakeAsync(() => {
+
+        const service = TestBed.inject(InterSpaceCoordXformSvc)
+        const httpTestingController = TestBed.inject(HttpTestingController)
+
+        service.transform(
+          VALID_TEMPLATE_SPACE_NAMES.MNI152,
+          VALID_TEMPLATE_SPACE_NAMES.BIG_BRAIN,
+          [1,2,3]
+        ).subscribe(({ status, statusText }) => {
+          expect(status).toEqual('error')
+          expect(statusText).toEqual(`Timeout after 3s`)
+        })
+        const req = httpTestingController.expectOne(service['url'])
+        tick(4000)
+        expect(req.cancelled).toBe(true)
+      }))
+    })
+  })
+})
diff --git a/src/atlasComponents/sapi/core/space/interSpaceCoordXform.service.ts b/src/atlasComponents/sapi/core/space/interSpaceCoordXform.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1a0c0c690be0eb7615922a8068ffc86422af54fb
--- /dev/null
+++ b/src/atlasComponents/sapi/core/space/interSpaceCoordXform.service.ts
@@ -0,0 +1,107 @@
+import { Injectable } from "@angular/core";
+import { HttpClient, HttpHeaders, HttpErrorResponse } from "@angular/common/http";
+import { catchError, timeout, map } from "rxjs/operators";
+import { of, Observable } from "rxjs";
+import { environment } from 'src/environments/environment'
+import { IDS } from "src/atlasComponents/sapi/constants"
+
+type ITemplateCoordXformResp = {
+  status: 'pending' | 'error' | 'completed' | 'cached'
+  statusText?: string
+  result? : [number, number, number]
+}
+
+export const VALID_TEMPLATE_SPACE_NAMES = {
+  MNI152: 'MNI 152 ICBM 2009c Nonlinear Asymmetric',
+  COLIN27: 'MNI Colin 27',
+  BIG_BRAIN: 'Big Brain (Histology)',
+  INFANT: 'Infant Atlas',
+} as const
+
+export type ValidTemplateSpaceName = typeof VALID_TEMPLATE_SPACE_NAMES[keyof typeof VALID_TEMPLATE_SPACE_NAMES]
+
+@Injectable({
+  providedIn: 'root',
+})
+export class InterSpaceCoordXformSvc {
+
+  static TmplIdToValidSpaceName(tmplId: string): ValidTemplateSpaceName{
+    switch (tmplId) {
+    case IDS.TEMPLATES.BIG_BRAIN: return VALID_TEMPLATE_SPACE_NAMES.BIG_BRAIN
+    case IDS.TEMPLATES.MNI152: return VALID_TEMPLATE_SPACE_NAMES.MNI152
+    case IDS.TEMPLATES.COLIN27: return VALID_TEMPLATE_SPACE_NAMES.COLIN27
+    default: return null
+    }
+  }
+
+  private cache = {
+    _map: new Map<string, [number, number, number]>(),
+    _getKey(srcTmplName: ValidTemplateSpaceName, targetTmplName: ValidTemplateSpaceName, coordinatesInNm: [number, number, number]) {
+      return `${srcTmplName}:${targetTmplName}:${coordinatesInNm.map(v => Math.round(v / 1e3)).join(',')}`
+    },
+    set(srcTmplName: ValidTemplateSpaceName, targetTmplName: ValidTemplateSpaceName, coordinatesInNm: [number, number, number], result: [number, number, number]) {
+      const key = this._getKey(srcTmplName, targetTmplName, coordinatesInNm)
+      return this._map.set(key, result)
+    },
+    get(srcTmplName: ValidTemplateSpaceName, targetTmplName: ValidTemplateSpaceName, coordinatesInNm: [number, number, number]) {
+      const key = this._getKey(srcTmplName, targetTmplName, coordinatesInNm)
+      return this._map.get(key)
+    }
+  }
+
+  constructor(private httpClient: HttpClient) {}
+
+  private url = `${environment.SPATIAL_TRANSFORM_BACKEND.replace(/\/$/, '')}/v1/transform-points`
+
+  // jasmine marble cannot test promise properly
+  // see https://github.com/ngrx/platform/issues/498#issuecomment-337465179
+  // in order to properly test with marble, use obs instead of promise
+  transform(srcTmplName: ValidTemplateSpaceName, targetTmplName: ValidTemplateSpaceName, coordinatesInNm: [number, number, number]): Observable<ITemplateCoordXformResp> {
+    if (!srcTmplName || !targetTmplName) {
+      return of({
+        status: 'error',
+        statusText: 'either srcTmplName or targetTmplName is undefined'
+      } as ITemplateCoordXformResp)
+    }
+    const cachedResult = this.cache.get(srcTmplName, targetTmplName, coordinatesInNm)
+    if (cachedResult) {
+      return of({
+        status: 'cached',
+        result: cachedResult,
+      } as ITemplateCoordXformResp)
+    }
+
+    const httpOptions = {
+      headers: new HttpHeaders({
+        'Content-Type':  'application/json',
+      })
+    }
+    return this.httpClient.post(
+      this.url,
+      JSON.stringify({
+        'source_points': [[...coordinatesInNm.map(c => c/1e6)]],
+        'source_space': srcTmplName,
+        'target_space': targetTmplName
+      }),
+      httpOptions
+    ).pipe(
+      map(resp => {
+        const result = resp['target_points'][0].map((r: number)=> r * 1e6) as [number, number, number]
+        this.cache.set(srcTmplName, targetTmplName, coordinatesInNm, result)
+        return {
+          status: 'completed',
+          result
+        } as ITemplateCoordXformResp
+      }),
+      catchError(err => {
+        if (err instanceof HttpErrorResponse) {
+          return of(({ status: 'error', statusText: err.message } as ITemplateCoordXformResp))
+        } else {
+          return of(({ status: 'error', statusText: err.toString() } as ITemplateCoordXformResp))
+        }
+      }),
+      timeout(3000),
+      catchError(() => of(({ status: 'error', statusText: `Timeout after 3s` } as ITemplateCoordXformResp))),
+    )
+  }
+}
\ No newline at end of file
diff --git a/src/atlasComponents/sapi/features/index.ts b/src/atlasComponents/sapi/features/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..560300f7fbccdc5d69dbdb52be17c003f0bcfd25
--- /dev/null
+++ b/src/atlasComponents/sapi/features/index.ts
@@ -0,0 +1,3 @@
+export {
+  SAPIFeature
+} from "./sapiFeature"
\ No newline at end of file
diff --git a/src/atlasComponents/sapi/features/sapiFeature.ts b/src/atlasComponents/sapi/features/sapiFeature.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5abaa0040f6cb71046c70bf57e7a77a57f2794a8
--- /dev/null
+++ b/src/atlasComponents/sapi/features/sapiFeature.ts
@@ -0,0 +1,13 @@
+import { SAPI } from "../sapi.service";
+import { SapiFeatureModel } from "../type";
+
+export class SAPIFeature {
+  constructor(private sapi: SAPI, public id: string, public opts: Record<string, string> = {}){
+
+  }
+
+  public detail$ = this.sapi.httpGet<SapiFeatureModel>(
+    `${SAPI.bsEndpoint}/features/${this.id}`,
+    this.opts
+  )
+}
diff --git a/src/atlasComponents/sapi/index.ts b/src/atlasComponents/sapi/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e5d8afd20e4e298a52db0b0edfc3fa61c55a5e74
--- /dev/null
+++ b/src/atlasComponents/sapi/index.ts
@@ -0,0 +1,30 @@
+export { SAPIModule } from './module'
+
+export {
+  SapiAtlasModel,
+  SapiParcellationModel,
+  SapiSpaceModel,
+  SapiRegionModel,
+  SapiVolumeModel,
+  SapiDatasetModel,
+  SapiRegionalFeatureModel,
+  SapiSpatialFeatureModel,
+  SapiFeatureModel,
+  SapiParcellationFeatureModel,
+  CleanedIeegDataset,
+  SxplrCleanedFeatureModel,
+  OpenMINDSCoordinatePoint,
+  CLEANED_IEEG_DATASET_TYPE,
+} from "./type"
+
+export { SAPI } from "./sapi.service"
+export {
+  SAPIAtlas,
+  SAPISpace,
+  SAPIParcellation,
+  SAPIRegion
+} from "./core"
+
+export {
+  IDS
+} from "./constants"
\ No newline at end of file
diff --git a/src/atlasComponents/sapi/module.ts b/src/atlasComponents/sapi/module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a64cc8bc817f05c801cde40d58a95d93c1d198a1
--- /dev/null
+++ b/src/atlasComponents/sapi/module.ts
@@ -0,0 +1,27 @@
+import { NgModule } from "@angular/core";
+import { SAPI } from "./sapi.service";
+import { CommonModule } from "@angular/common";
+import { HttpClientModule, HTTP_INTERCEPTORS } from "@angular/common/http";
+import { PriorityHttpInterceptor } from "src/util/priority";
+import { MatSnackBarModule } from "@angular/material/snack-bar";
+
+@NgModule({
+  imports: [
+    CommonModule,
+    HttpClientModule,
+    MatSnackBarModule,
+  ],
+  declarations: [
+  ],
+  exports: [
+  ],
+  providers: [
+    SAPI,
+    {
+      provide: HTTP_INTERCEPTORS,
+      useClass: PriorityHttpInterceptor,
+      multi: true
+    }
+  ]
+})
+export class SAPIModule{}
diff --git a/src/atlasComponents/sapi/sapi.service.ts b/src/atlasComponents/sapi/sapi.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..62ba0e2528a8633db52bc44acdcadd3e9e851e04
--- /dev/null
+++ b/src/atlasComponents/sapi/sapi.service.ts
@@ -0,0 +1,259 @@
+import { Injectable } from "@angular/core";
+import { HttpClient } from '@angular/common/http';
+import { map, shareReplay } from "rxjs/operators";
+import { SAPIAtlas, SAPISpace } from './core'
+import {
+  SapiAtlasModel, SapiModalityModel,
+  SapiParcellationModel,
+  SapiQueryPriorityArg,
+  SapiRegionalFeatureModel,
+  SapiRegionModel,
+  SapiSpaceModel,
+  SpyNpArrayDataModel,
+  SxplrCleanedFeatureModel
+} from "./type";
+import { getExportNehuba } from "src/util/fn";
+import { SAPIParcellation } from "./core/sapiParcellation";
+import { SAPIRegion } from "./core/sapiRegion"
+import { MatSnackBar } from "@angular/material/snack-bar";
+import { AtlasWorkerService } from "src/atlasViewer/atlasViewer.workerService.service";
+import { EnumColorMapName } from "src/util/colorMaps";
+import { PRIORITY_HEADER } from "src/util/priority";
+import { Observable } from "rxjs";
+import { SAPIFeature } from "./features";
+import { environment } from "src/environments/environment"
+
+export const SIIBRA_API_VERSION_HEADER_KEY='x-siibra-api-version'
+export const SIIBRA_API_VERSION = '0.2.0'
+
+type RegistryType = SAPIAtlas | SAPISpace | SAPIParcellation
+
+@Injectable()
+export class SAPI{
+  static bsEndpoint = environment.BS_REST_URL || `https://siibra-api-latest.apps-dev.hbp.eu/v2_0`
+
+  public bsEndpoint = SAPI.bsEndpoint
+  
+  registry = {
+    _map: {} as Record<string, {
+      func: (...arg: any[]) => RegistryType
+      args: string[]
+    }>,
+    get<T>(id: string): T {
+      if (!this._map[id]) return null
+      const { func, args } = this._map[id]
+      return func(...args)
+    },
+    set(id: string, func: (...args: any[]) => RegistryType, args: string[]) {
+      if (this._map[id]) {
+        console.warn(`id ${id} already mapped as ${this._map[id]}`)
+      }
+      this._map[id] = { func, args }
+    }
+  }
+
+  getAtlas(atlasId: string): SAPIAtlas {
+    return new SAPIAtlas(this, atlasId)
+  }
+
+  getSpace(atlasId: string, spaceId: string): SAPISpace {
+    return new SAPISpace(this, atlasId, spaceId)
+  }
+
+  getParcellation(atlasId: string, parcId: string): SAPIParcellation {
+    return new SAPIParcellation(this, atlasId, parcId)
+  }
+
+  getRegion(atlasId: string, parcId: string, regionId: string): SAPIRegion{
+    return new SAPIRegion(this, atlasId, parcId, regionId)
+  }
+
+  getSpaceDetail(atlasId: string, spaceId: string, param?: SapiQueryPriorityArg): Observable<SapiSpaceModel> {
+    return this.getSpace(atlasId, spaceId).getDetail(param)
+  }
+
+  getParcDetail(atlasId: string, parcId: string, param?: SapiQueryPriorityArg): Observable<SapiParcellationModel> {
+    return this.getParcellation(atlasId, parcId).getDetail(param)
+  }
+
+  getParcRegions(atlasId: string, parcId: string, spaceId: string, queryParam?: SapiQueryPriorityArg): Observable<SapiRegionModel[]> {
+    const parc = this.getParcellation(atlasId, parcId)
+    return parc.getRegions(spaceId, queryParam)
+  }
+
+  getFeature(featureId: string, opts: Record<string, string> = {}) {
+    return new SAPIFeature(this, featureId, opts)
+  }
+
+  getRegionFeatures(atlasId: string, parcId: string, spaceId: string, regionId: string, priority = 0): Observable<(SapiRegionalFeatureModel | SxplrCleanedFeatureModel)[]>{
+
+    const reg = this.getRegion(atlasId, parcId, regionId)
+    return reg.getFeatures(spaceId)
+  }
+
+  getModalities(): Observable<SapiModalityModel[]> {
+    return this.http.get<SapiModalityModel[]>(`${SAPI.bsEndpoint}/modalities`)
+  }
+
+  httpGet<T>(url: string, params?: Record<string, string>, sapiParam?: SapiQueryPriorityArg){
+    const headers: Record<string, string> = {}
+    if (sapiParam?.priority) {
+      headers[PRIORITY_HEADER] = sapiParam.priority.toString()
+    }
+    return this.http.get<T>(
+      url,
+      {
+        headers,
+        params
+      }
+    )
+  }
+
+  public atlases$ = this.http.get<SapiAtlasModel[]>(
+    `${this.bsEndpoint}/atlases`,
+    {
+      observe: "response"
+    }
+  ).pipe(
+    map(resp => {
+      const respVersion = resp.headers.get(SIIBRA_API_VERSION_HEADER_KEY)
+      if (respVersion !== SIIBRA_API_VERSION) {
+        this.snackbar.open(`Expecting ${SIIBRA_API_VERSION}, got ${respVersion}. Some functionalities may not work as expected.`, 'Dismiss', {
+          duration: 5000
+        })
+      }
+      console.log(`siibra-api::version::${respVersion}, expecting::${SIIBRA_API_VERSION}`)
+      return resp.body
+    }),
+    shareReplay(1)
+  )
+
+  constructor(
+    public http: HttpClient,
+    private snackbar: MatSnackBar,
+    private workerSvc: AtlasWorkerService,
+  ){
+    this.atlases$.subscribe(atlases => {
+      for (const atlas of atlases) {
+        for (const space of atlas.spaces) {
+          this.registry.set(space["@id"], this.getSpace.bind(this), [atlas["@id"], space["@id"]])
+          this.getSpaceDetail(atlas["@id"], space["@id"])
+        }
+        for (const parc of atlas.parcellations) {
+          this.registry.set(parc["@id"], this.getParcellation.bind(this), [atlas["@id"], parc["@id"]])
+          this.getParcDetail(atlas["@id"], parc["@id"])
+        }
+      }
+    })
+  }
+  
+  async processNpArrayData<T extends keyof ProcessTypedArrayResult>(input: SpyNpArrayDataModel, method: PARSE_TYPEDARRAY = PARSE_TYPEDARRAY.RAW_ARRAY, params: ProcessTypedArrayResult[T]['input'] = null): Promise<ProcessTypedArrayResult[T]['output']> {
+
+    const supportedDtype = [
+      "uint8",
+      "int32",
+      "float32"
+    ]
+    const {
+      "x-channel": channel,
+      "x-width": width,
+      "x-height": height,
+      content,
+      dtype,
+      content_encoding: contentEncoding, 
+      content_type: contentType
+    } = input
+    
+    if (contentType !== "application/octet-stream") {
+      throw new Error(`sapi.service#decodeNpArrayDataModel error: expecting content_type to be "application/octet-stream", but is ${contentType}`)
+    }
+    if (contentEncoding !== "gzip; base64") {
+      throw new Error(`sapi.service#decodeNpArrayDataModel error: expecting content_encoding to be "gzip; base64", but is ${contentEncoding}`)
+    }
+    if (supportedDtype.indexOf(dtype) < 0) {
+      throw new Error(`sapi.service#decodeNpArrayDataModel error: expecting dtype to be in ${JSON.stringify(supportedDtype)}, but is ${dtype}`)
+    }
+
+    try {
+      const bin = atob(content)
+      const { pako } = getExportNehuba()
+      const array = pako.inflate(bin)
+      let workerMsg: string
+      switch (method) {
+      case PARSE_TYPEDARRAY.CANVAS_FORTRAN_RGBA: {
+        workerMsg = "PROCESS_TYPED_ARRAY_F2RGBA"
+        break
+      }
+      case PARSE_TYPEDARRAY.CANVAS_COLORMAP_RGBA: {
+        workerMsg = "PROCESS_TYPED_ARRAY_CM2RGBA"
+        break
+      }
+      case PARSE_TYPEDARRAY.RAW_ARRAY: {
+        workerMsg = "PROCESS_TYPED_ARRAY_RAW"
+        break
+      }
+      default:{
+        throw new Error(`sapi.service#decodeNpArrayDataModel: method cannot be deciphered: ${method}`)
+      }
+      }
+      const { result } = await this.workerSvc.sendMessage({
+        method: workerMsg,
+        param: {
+          inputArray: array,
+          width,
+          height,
+          channel,
+          dtype,
+          processParams: params
+        },
+        transfers: [ array.buffer ]
+      })
+      const { buffer, outputArray, min, max } = result
+      return {
+        type: method,
+        result: buffer,
+        rawArray: outputArray,
+        min,
+        max
+      }
+    } catch (e) {
+      throw new Error(`sapi.service#decodeNpArrayDataModel error: ${e}`)
+    }
+  }
+}
+
+export enum PARSE_TYPEDARRAY {
+  CANVAS_FORTRAN_RGBA="CANVAS_FORTRAN_RGBA",
+  CANVAS_COLORMAP_RGBA="CANVAS_COLORMAP_RGBA",
+  RAW_ARRAY="RAW_ARRAY",
+}
+
+type ProcessTypedArrayResult = {
+  [PARSE_TYPEDARRAY.CANVAS_FORTRAN_RGBA]: {
+    input: null
+    output: {
+      type: PARSE_TYPEDARRAY
+      result: Uint8ClampedArray
+    }
+  }
+  [PARSE_TYPEDARRAY.CANVAS_COLORMAP_RGBA]: {
+    input?: {
+      colormap?: EnumColorMapName
+      log?: boolean
+    }
+    output: {
+      type: PARSE_TYPEDARRAY
+      result: Uint8ClampedArray
+      max: number
+      min: number
+    }
+  }
+  [PARSE_TYPEDARRAY.RAW_ARRAY]: {
+    input: null
+    output: {
+      rawArray: number[][]
+      min: number
+      max: number
+    }
+  }
+}
diff --git a/src/atlasComponents/sapi/schema.ts b/src/atlasComponents/sapi/schema.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a54de84decb2dbb61346f6fc6182706d4542213c
--- /dev/null
+++ b/src/atlasComponents/sapi/schema.ts
@@ -0,0 +1,1993 @@
+/**
+ * This file was auto-generated by openapi-typescript.
+ * Do not make direct changes to the file.
+ */
+
+export interface paths {
+  "/atlases/{atlas_id}/parcellations/{parcellation_id}/regions": {
+    /** Returns all regions for a given parcellation id. */
+    get: operations["get_all_regions_from_atlas_parc_space_atlases__atlas_id__parcellations__parcellation_id__regions_get"]
+  }
+  "/atlases/{atlas_id}/parcellations/{parcellation_id}/regions/{region_id}/features": {
+    /** Returns all regional features for a region. */
+    get: operations["get_all_regional_features_for_region_atlases__atlas_id__parcellations__parcellation_id__regions__region_id__features_get"]
+  }
+  "/atlases/{atlas_id}/parcellations/{parcellation_id}/regions/{region_id}/features/{feature_id}": {
+    /** Returns a feature for a region, as defined by by the modality and feature ID */
+    get: operations["get_single_detailed_regional_feature_atlases__atlas_id__parcellations__parcellation_id__regions__region_id__features__feature_id__get"]
+  }
+  "/atlases/{atlas_id}/parcellations/{parcellation_id}/regions/{region_id}/regional_map/info": {
+    /** Returns information about a regional map for given region name. */
+    get: operations["get_regional_map_info_atlases__atlas_id__parcellations__parcellation_id__regions__region_id__regional_map_info_get"]
+  }
+  "/atlases/{atlas_id}/parcellations/{parcellation_id}/regions/{region_id}/regional_map/map": {
+    /** Returns a regional map for given region name. */
+    get: operations["get_regional_map_file_atlases__atlas_id__parcellations__parcellation_id__regions__region_id__regional_map_map_get"]
+  }
+  "/atlases/{atlas_id}/parcellations/{parcellation_id}/regions/{region_id}/volumes": {
+    get: operations["get_regional_volumes_atlases__atlas_id__parcellations__parcellation_id__regions__region_id__volumes_get"]
+  }
+  "/atlases/{atlas_id}/parcellations/{parcellation_id}/regions/{region_id}": {
+    get: operations["get_single_region_detail_atlases__atlas_id__parcellations__parcellation_id__regions__region_id__get"]
+  }
+  "/atlases/{atlas_id}/parcellations": {
+    /** Returns all parcellations that are defined in the siibra client for given atlas. */
+    get: operations["get_all_parcellations_atlases__atlas_id__parcellations_get"]
+  }
+  "/atlases/{atlas_id}/parcellations/{parcellation_id}/features/{feature_id}": {
+    /** Returns a global feature for a specific modality id. */
+    get: operations["get_single_detailed_global_feature_atlases__atlas_id__parcellations__parcellation_id__features__feature_id__get"]
+  }
+  "/atlases/{atlas_id}/parcellations/{parcellation_id}/features": {
+    /** Returns all global features for a parcellation. */
+    get: operations["get_all_global_features_for_parcellation_atlases__atlas_id__parcellations__parcellation_id__features_get"]
+  }
+  "/atlases/{atlas_id}/parcellations/{parcellation_id}/volumes": {
+    /** Returns one parcellation for given id. */
+    get: operations["get_volumes_for_parcellation_atlases__atlas_id__parcellations__parcellation_id__volumes_get"]
+  }
+  "/atlases/{atlas_id}/parcellations/{parcellation_id}": {
+    /** Returns one parcellation for given id. */
+    get: operations["get_single_parcellation_detail_atlases__atlas_id__parcellations__parcellation_id__get"]
+  }
+  "/atlases/{atlas_id}/spaces": {
+    /** Returns all spaces that are defined in the siibra client. */
+    get: operations["get_all_spaces_atlases__atlas_id__spaces_get"]
+  }
+  "/atlases/{atlas_id}/spaces/{space_id}/templates": {
+    /** Returns a template for a given space id. */
+    get: operations["get_template_by_space_id_atlases__atlas_id__spaces__space_id__templates_get"]
+  }
+  "/atlases/{atlas_id}/spaces/{space_id}/parcellation_maps": {
+    /** Returns all parcellation maps for a given space id. */
+    get: operations["get_parcellation_map_for_space_atlases__atlas_id__spaces__space_id__parcellation_maps_get"]
+  }
+  "/atlases/{atlas_id}/spaces/{space_id}/features/{feature_id}": {
+    /**
+     * Get a detailed view on a single spatial feature.
+     * A parcellation id and region id can be provided optional to get more details.
+     */
+    get: operations["get_single_detailed_spatial_feature_atlases__atlas_id__spaces__space_id__features__feature_id__get"]
+  }
+  "/atlases/{atlas_id}/spaces/{space_id}/features": {
+    /** Return all possible feature names and links to get more details */
+    get: operations["get_all_spatial_features_for_space_atlases__atlas_id__spaces__space_id__features_get"]
+  }
+  "/atlases/{atlas_id}/spaces/{space_id}/volumes": {
+    get: operations["get_volumes_for_space_atlases__atlas_id__spaces__space_id__volumes_get"]
+  }
+  "/atlases/{atlas_id}/spaces/{space_id}": {
+    /** Returns one space for given id, with links to further resources */
+    get: operations["get_single_space_detail_atlases__atlas_id__spaces__space_id__get"]
+  }
+  "/atlases": {
+    /** Get all atlases known by siibra. */
+    get: operations["get_all_atlases_atlases_get"]
+  }
+  "/atlases/{atlas_id}": {
+    /** Get more information for a specific atlas with links to further objects. */
+    get: operations["get_atlas_by_id_atlases__atlas_id__get"]
+  }
+  "/genes": {
+    /** Return all genes (name, acronym) in siibra */
+    get: operations["get_gene_names_genes_get"]
+  }
+  "/modalities": {
+    /** Return all possible modalities */
+    get: operations["get_all_available_modalities_modalities_get"]
+  }
+  "/features/{feature_id}": {
+    /**
+     * Get all details for one feature by id.
+     * Since the feature id is unique, no atlas concept is required.
+     *
+     * Further optional params can extend the result.
+     * :param feature_id:
+     * :param atlas_id:
+     * :param space_id:
+     * :param parcellation_id:
+     * :param region_id:
+     * :return: FeatureModels
+     */
+    get: operations["get_feature_details_features__feature_id__get"]
+  }
+}
+
+export interface components {
+  schemas: {
+    /** AutoradiographyDataModel */
+    AutoradiographyDataModel: {
+      /**
+       * Content Type
+       * @default application/octet-stream
+       */
+      content_type?: string
+      /**
+       * Content Encoding
+       * @default gzip; base64
+       */
+      content_encoding?: string
+      /** X-Width */
+      "x-width": number
+      /** X-Height */
+      "x-height": number
+      /** X-Channel */
+      "x-channel": number
+      /** Dtype */
+      dtype: string
+      /** Content */
+      content: string
+    }
+    /** AxesOrigin */
+    AxesOrigin: {
+      /**
+       * @Context
+       * @default [object Object]
+       */
+      "@context"?: components["schemas"]["VocabModel"]
+      /**
+       * typeOfUncertainty
+       * @description Distinct technique used to quantify the uncertainty of a measurement.
+       */
+      typeOfUncertainty?: unknown
+      /**
+       * uncertainty
+       * @description Quantitative value range defining the uncertainty of a measurement.
+       */
+      uncertainty?: number[]
+      /**
+       * unit
+       * @description Determinate quantity adopted as a standard of measurement.
+       */
+      unit?: unknown
+      /**
+       * value
+       * @description Entry for a property.
+       */
+      value: number
+    }
+    /** BaseDatasetJsonModel */
+    BaseDatasetJsonModel: {
+      /** @Id */
+      "@id": string
+      /**
+       * @Type
+       * @constant
+       */
+      "@type"?: "https://openminds.ebrains.eu/core/DatasetVersion"
+      metadata: components["schemas"]["siibra__openminds__core__v4__products__datasetVersion__Model"]
+      /** Urls */
+      urls: components["schemas"]["Url"][]
+    }
+    /** BestViewPoint */
+    BestViewPoint: {
+      /**
+       * @Context
+       * @default [object Object]
+       */
+      "@context"?: components["schemas"]["VocabModel"]
+      /**
+       * coordinateSpace
+       * @description Two or three dimensional geometric setting.
+       */
+      coordinateSpace: unknown
+      /**
+       * Coordinates
+       * @description Structured information on a quantitative value.
+       */
+      coordinates: components["schemas"]["siibra__openminds__SANDS__v3__atlas__parcellationEntityVersion__Coordinates"][]
+    }
+    /** BoundingBoxModel */
+    BoundingBoxModel: {
+      /** @Type */
+      "@type": string
+      /** Space */
+      space: { [key: string]: string }
+      center: components["schemas"]["siibra__openminds__SANDS__v3__miscellaneous__coordinatePoint__Model"]
+      minpoint: components["schemas"]["siibra__openminds__SANDS__v3__miscellaneous__coordinatePoint__Model"]
+      maxpoint: components["schemas"]["siibra__openminds__SANDS__v3__miscellaneous__coordinatePoint__Model"]
+      /** Shape */
+      shape: number[]
+      /** Isplanar */
+      isPlanar: boolean
+    }
+    /** ConnectivityMatrixDataModel */
+    ConnectivityMatrixDataModel: {
+      /** @Id */
+      "@id": string
+      /** @Type */
+      "@type": string
+      /** Name */
+      name: string
+      /** Parcellations */
+      parcellations: { [key: string]: string }[]
+      matrix?: components["schemas"]["NpArrayDataModel"]
+      /** Columns */
+      columns?: string[]
+    }
+    /** CorticalCellDistributionModel */
+    CorticalCellDistributionModel: {
+      /** @Id */
+      "@id": string
+      /**
+       * @Type
+       * @constant
+       */
+      "@type"?: "siibra/features/cells"
+      metadata: components["schemas"]["siibra__openminds__core__v4__products__datasetVersion__Model"]
+      /** Urls */
+      urls: components["schemas"]["Url"][]
+      /** Cells */
+      cells?: components["schemas"]["CorticalCellModel"][]
+      /** Section */
+      section?: string
+      /** Patch */
+      patch?: string
+    }
+    /** CorticalCellModel */
+    CorticalCellModel: {
+      /** X */
+      x: number
+      /** Y */
+      y: number
+      /** Area */
+      area: number
+      /** Layer */
+      layer: number
+      /** Instance Label */
+      "instance label": number
+    }
+    /** DatasetJsonModel */
+    DatasetJsonModel: {
+      /** @Id */
+      "@id": string
+      /**
+       * @Type
+       * @constant
+       */
+      "@type"?: "https://openminds.ebrains.eu/core/DatasetVersion"
+      metadata: components["schemas"]["siibra__openminds__core__v4__products__datasetVersion__Model"]
+      /** Urls */
+      urls: components["schemas"]["Url"][]
+    }
+    /** FingerPrintDataModel */
+    FingerPrintDataModel: {
+      /** Mean */
+      mean: number
+      /** Std */
+      std: number
+      /** Unit */
+      unit: string
+    }
+    /** HTTPValidationError */
+    HTTPValidationError: {
+      /** Detail */
+      detail?: components["schemas"]["ValidationError"][]
+    }
+    /** HasAnnotation */
+    HasAnnotation: {
+      /**
+       * @Context
+       * @default [object Object]
+       */
+      "@context"?: components["schemas"]["VocabModel"]
+      /**
+       * Bestviewpoint
+       * @description Structured information on a coordinate point.
+       */
+      bestViewPoint?: components["schemas"]["BestViewPoint"]
+      /**
+       * criteria
+       * @description Aspects or standards on which a judgement or decision is based.
+       */
+      criteria?: unknown
+      /**
+       * criteriaQualityType
+       * @description Distinct class that defines how the judgement or decision was made for a particular criteria.
+       */
+      criteriaQualityType: unknown
+      /**
+       * displayColor
+       * @description Preferred coloring.
+       */
+      displayColor?: string
+      /**
+       * inspiredBy
+       * @description Reference to an inspiring element.
+       */
+      inspiredBy?: unknown[]
+      /**
+       * internalIdentifier
+       * @description Term or code that identifies someone or something within a particular product.
+       */
+      internalIdentifier: string
+      /**
+       * laterality
+       * @description Differentiation between a pair of lateral homologous parts of the body.
+       */
+      laterality?: unknown[]
+      /**
+       * visualizedIn
+       * @description Reference to an image in which something is visible.
+       */
+      visualizedIn?: unknown
+    }
+    /** HasTerminologyVersion */
+    HasTerminologyVersion: {
+      /**
+       * @Context
+       * @default [object Object]
+       */
+      "@context"?: components["schemas"]["VocabModel"]
+      /**
+       * definedIn
+       * @description Reference to a file instance in which something is stored.
+       */
+      definedIn?: unknown[]
+      /** hasEntityVersion */
+      hasEntityVersion: unknown[]
+      /**
+       * ontologyIdentifier
+       * @description Term or code used to identify something or someone registered within a particular ontology.
+       */
+      ontologyIdentifier?: string[]
+    }
+    /** HrefModel */
+    HrefModel: {
+      /** Href */
+      href: string
+    }
+    /** IEEGContactPointModel */
+    IEEGContactPointModel: {
+      /** Inroi */
+      inRoi?: boolean
+      /** Id */
+      id: string
+      point: components["schemas"]["siibra__openminds__SANDS__v3__miscellaneous__coordinatePoint__Model"]
+    }
+    /** IEEGElectrodeModel */
+    IEEGElectrodeModel: {
+      /** Inroi */
+      inRoi?: boolean
+      /** Electrode Id */
+      electrode_id: string
+      /** Contact Points */
+      contact_points: {
+        [key: string]: components["schemas"]["IEEGContactPointModel"]
+      }
+    }
+    /** IEEGSessionModel */
+    IEEGSessionModel: {
+      /** Inroi */
+      inRoi?: boolean
+      /** @Id */
+      "@id": string
+      /**
+       * @Type
+       * @constant
+       */
+      "@type"?: "siibra/features/ieegSession"
+      dataset: components["schemas"]["DatasetJsonModel"]
+      /** Sub Id */
+      sub_id: string
+      /** Electrodes */
+      electrodes: {
+        [key: string]: components["schemas"]["IEEGElectrodeModel"]
+      }
+    }
+    /** NeurotransmitterMarkupModel */
+    NeurotransmitterMarkupModel: {
+      /** Latex */
+      latex: string
+      /** Markdown */
+      markdown: string
+      /** Name */
+      name: string
+      /** Label */
+      label: string
+    }
+    /** NiiMetadataModel */
+    NiiMetadataModel: {
+      /** Min */
+      min: number
+      /** Max */
+      max: number
+    }
+    /** NpArrayDataModel */
+    NpArrayDataModel: {
+      /**
+       * Content Type
+       * @default application/octet-stream
+       */
+      content_type?: string
+      /**
+       * Content Encoding
+       * @default gzip; base64
+       */
+      content_encoding?: string
+      /** X-Width */
+      "x-width": number
+      /** X-Height */
+      "x-height": number
+      /** X-Channel */
+      "x-channel": number
+      /** Dtype */
+      dtype: string
+      /** Content */
+      content: string
+    }
+    /** Page[Union[siibra.features.connectivity.ConnectivityMatrixDataModel, app.models.SerializationErrorModel]] */
+    "Page_Union_siibra.features.connectivity.ConnectivityMatrixDataModel__app.models.SerializationErrorModel__": {
+      /** Items */
+      items: (Partial<components["schemas"]["ConnectivityMatrixDataModel"]> &
+        Partial<components["schemas"]["SerializationErrorModel"]>)[]
+      /** Total */
+      total: number
+      /** Page */
+      page: number
+      /** Size */
+      size: number
+    }
+    /** Page[VolumeModel] */
+    Page_VolumeModel_: {
+      /** Items */
+      items: components["schemas"]["VolumeModel"][]
+      /** Total */
+      total: number
+      /** Page */
+      page: number
+      /** Size */
+      size: number
+    }
+    /** ProfileDataModel */
+    ProfileDataModel: {
+      density: components["schemas"]["NpArrayDataModel"]
+      /** Unit */
+      unit: string
+    }
+    /** QuantitativeOverlapItem */
+    QuantitativeOverlapItem: {
+      /**
+       * @Context
+       * @default [object Object]
+       */
+      "@context"?: components["schemas"]["VocabModel"]
+      /**
+       * typeOfUncertainty
+       * @description Distinct technique used to quantify the uncertainty of a measurement.
+       */
+      typeOfUncertainty?: unknown
+      /**
+       * uncertainty
+       * @description Quantitative value range defining the uncertainty of a measurement.
+       */
+      uncertainty?: number[]
+      /**
+       * unit
+       * @description Determinate quantity adopted as a standard of measurement.
+       */
+      unit?: unknown
+      /**
+       * value
+       * @description Entry for a property.
+       */
+      value: number
+    }
+    /** QuantitativeOverlapItem1 */
+    QuantitativeOverlapItem1: {
+      /**
+       * @Context
+       * @default [object Object]
+       */
+      "@context"?: components["schemas"]["VocabModel"]
+      /**
+       * maxValue
+       * @description Greatest quantity attained or allowed.
+       */
+      maxValue: number
+      /** maxValueUnit */
+      maxValueUnit?: unknown
+      /**
+       * minValue
+       * @description Smallest quantity attained or allowed.
+       */
+      minValue: number
+      /** minValueUnit */
+      minValueUnit?: unknown
+    }
+    /** ReceptorDataModel */
+    ReceptorDataModel: {
+      /** Autoradiographs */
+      autoradiographs: {
+        [key: string]: components["schemas"]["AutoradiographyDataModel"]
+      }
+      /** Profiles */
+      profiles: { [key: string]: components["schemas"]["ProfileDataModel"] }
+      /** Fingerprints */
+      fingerprints: {
+        [key: string]: components["schemas"]["FingerPrintDataModel"]
+      }
+      /** Receptor Symbols */
+      receptor_symbols: {
+        [key: string]: components["schemas"]["SymbolMarkupClass"]
+      }
+    }
+    /** ReceptorDatasetModel */
+    ReceptorDatasetModel: {
+      /** @Id */
+      "@id": string
+      /**
+       * @Type
+       * @constant
+       */
+      "@type"?: "siibra/features/receptor"
+      metadata: components["schemas"]["siibra__openminds__core__v4__products__datasetVersion__Model"]
+      /** Urls */
+      urls: components["schemas"]["Url"][]
+      data?: components["schemas"]["ReceptorDataModel"]
+    }
+    /** ReceptorMarkupModel */
+    ReceptorMarkupModel: {
+      /** Latex */
+      latex: string
+      /** Markdown */
+      markdown: string
+      /** Name */
+      name: string
+    }
+    /** RelationAssessmentItem */
+    RelationAssessmentItem: {
+      /**
+       * @Context
+       * @default [object Object]
+       */
+      "@context"?: components["schemas"]["VocabModel"]
+      /**
+       * criteria
+       * @description Aspects or standards on which a judgement or decision is based.
+       */
+      criteria?: unknown
+      /**
+       * inRelationTo
+       * @description Reference to a related element.
+       */
+      inRelationTo: unknown
+      /**
+       * qualitativeOverlap
+       * @description Semantic characterization of how much two things occupy the same space.
+       */
+      qualitativeOverlap: unknown
+    }
+    /** RelationAssessmentItem1 */
+    RelationAssessmentItem1: {
+      /**
+       * @Context
+       * @default [object Object]
+       */
+      "@context"?: components["schemas"]["VocabModel"]
+      /**
+       * criteria
+       * @description Aspects or standards on which a judgement or decision is based.
+       */
+      criteria?: unknown
+      /**
+       * inRelationTo
+       * @description Reference to a related element.
+       */
+      inRelationTo: unknown
+      /** Quantitativeoverlap */
+      quantitativeOverlap: Partial<
+        components["schemas"]["QuantitativeOverlapItem"]
+      > &
+        Partial<components["schemas"]["QuantitativeOverlapItem1"]>
+    }
+    /** SapiAtlasModel */
+    SapiAtlasModel: {
+      /** Links */
+      links: { [key: string]: components["schemas"]["HrefModel"] }
+      /** @Id */
+      "@id": string
+      /** Name */
+      name: string
+      /**
+       * @Type
+       * @constant
+       */
+      "@type"?: "juelich/iav/atlas/v1.0.0"
+      /** Spaces */
+      spaces: components["schemas"]["SiibraAtIdModel"][]
+      /** Parcellations */
+      parcellations: components["schemas"]["SiibraAtIdModel"][]
+      species: components["schemas"]["SpeciesModel"]
+    }
+    /** SapiParcellationModel */
+    SapiParcellationModel: {
+      /** Links */
+      links: { [key: string]: components["schemas"]["HrefModel"] }
+      /** @Id */
+      "@id": string
+      /**
+       * @Type
+       * @constant
+       */
+      "@type"?: "minds/core/parcellationatlas/v1.0.0"
+      /** Name */
+      name: string
+      /** Modality */
+      modality?: string
+      /** Datasets */
+      datasets: components["schemas"]["DatasetJsonModel"][]
+      /** Brainatlasversions */
+      brainAtlasVersions: components["schemas"]["siibra__openminds__SANDS__v3__atlas__brainAtlasVersion__Model"][]
+      version?: components["schemas"]["SiibraParcellationVersionModel"]
+    }
+    /** SapiSpaceModel */
+    SapiSpaceModel: {
+      /** Links */
+      links: { [key: string]: components["schemas"]["HrefModel"] }
+      /**
+       * @Context
+       * @default [object Object]
+       */
+      "@context"?: components["schemas"]["VocabModel"]
+      /**
+       * @Id
+       * @description Metadata node identifier.
+       */
+      "@id": string
+      /** @Type */
+      "@type": string
+      /**
+       * anatomicalAxesOrientation
+       * @description Relation between reference planes used in anatomy and mathematics.
+       */
+      anatomicalAxesOrientation: { [key: string]: unknown }
+      /**
+       * Axesorigin
+       * @description Structured information on a quantitative value.
+       */
+      axesOrigin: components["schemas"]["AxesOrigin"][]
+      /**
+       * defaultImage
+       * @description Two or three dimensional image that particluarly represents a specific coordinate space.
+       */
+      defaultImage?: unknown[]
+      /**
+       * digitalIdentifier
+       * @description Digital handle to identify objects or legal persons.
+       */
+      digitalIdentifier?: { [key: string]: unknown }
+      /**
+       * fullName
+       * @description Whole, non-abbreviated name of something or somebody.
+       */
+      fullName: string
+      /**
+       * homepage
+       * @description Main website of something or someone.
+       */
+      homepage?: { [key: string]: unknown }
+      /**
+       * howToCite
+       * @description Preferred format for citing a particular object or legal person.
+       */
+      howToCite?: string
+      /**
+       * nativeUnit
+       * @description Determinate quantity used in the original measurement.
+       */
+      nativeUnit: { [key: string]: unknown }
+      /**
+       * ontologyIdentifier
+       * @description Term or code used to identify something or someone registered within a particular ontology.
+       */
+      ontologyIdentifier?: string[]
+      /**
+       * releaseDate
+       * Format: date
+       * @description Fixed date on which a product is due to become or was made available for the general public to see or buy
+       */
+      releaseDate: string
+      /**
+       * shortName
+       * @description Shortened or fully abbreviated name of something or somebody.
+       */
+      shortName: string
+      /**
+       * versionIdentifier
+       * @description Term or code used to identify the version of something.
+       */
+      versionIdentifier: string
+    }
+    /** SerializationErrorModel */
+    SerializationErrorModel: {
+      /**
+       * Type
+       * @constant
+       */
+      type?: "spy/serialization-error"
+      /** Message */
+      message: string
+    }
+    /** SiibraAtIdModel */
+    SiibraAtIdModel: {
+      /** @Id */
+      "@id": string
+    }
+    /** SiibraParcellationVersionModel */
+    SiibraParcellationVersionModel: {
+      /** Name */
+      name: string
+      /** Deprecated */
+      deprecated?: boolean
+      prev?: components["schemas"]["SiibraAtIdModel"]
+      next?: components["schemas"]["SiibraAtIdModel"]
+    }
+    /** SpeciesModel */
+    SpeciesModel: {
+      /**
+       * @Context
+       * @default [object Object]
+       */
+      "@context"?: components["schemas"]["VocabModel"]
+      /**
+       * @Id
+       * @description Metadata node identifier.
+       */
+      "@id": string
+      /** @Type */
+      "@type": string
+      /**
+       * definition
+       * @description Short, but precise statement of the meaning of a word, word group, sign or a symbol.
+       */
+      definition?: string
+      /**
+       * description
+       * @description Longer statement or account giving the characteristics of someone or something.
+       */
+      description?: string
+      /**
+       * interlexIdentifier
+       * @description Persistent identifier for a term registered in the InterLex project.
+       */
+      interlexIdentifier?: string
+      /**
+       * knowledgeSpaceLink
+       * @description Persistent link to an encyclopedia entry in the Knowledge Space project.
+       */
+      knowledgeSpaceLink?: string
+      /**
+       * name
+       * @description Word or phrase that constitutes the distinctive designation of a being or thing.
+       */
+      name: string
+      /**
+       * preferredOntologyIdentifier
+       * @description Persistent identifier of a preferred ontological term.
+       */
+      preferredOntologyIdentifier?: string
+      /**
+       * synonym
+       * @description Words or expressions used in the same language that have the same or nearly the same meaning in some or all senses.
+       */
+      synonym?: string[]
+      /** Kgv1Id */
+      kgV1Id: string
+    }
+    /** SymbolMarkupClass */
+    SymbolMarkupClass: {
+      receptor: components["schemas"]["ReceptorMarkupModel"]
+      neurotransmitter: components["schemas"]["NeurotransmitterMarkupModel"]
+    }
+    /** Url */
+    Url: {
+      /** Doi */
+      doi: string
+      /** Cite */
+      cite?: string
+    }
+    /** VOIDataModel */
+    VOIDataModel: {
+      /** @Id */
+      "@id": string
+      /**
+       * @Type
+       * @constant
+       */
+      "@type"?: "siibra/features/voi"
+      metadata: components["schemas"]["siibra__openminds__core__v4__products__datasetVersion__Model"]
+      /** Urls */
+      urls: components["schemas"]["Url"][]
+      /** Volumes */
+      volumes: components["schemas"]["VolumeModel"][]
+      location: components["schemas"]["BoundingBoxModel"]
+    }
+    /** ValidationError */
+    ValidationError: {
+      /** Location */
+      loc: (Partial<string> & Partial<number>)[]
+      /** Message */
+      msg: string
+      /** Error Type */
+      type: string
+    }
+    /** VocabModel */
+    VocabModel: {
+      /** @Vocab */
+      "@vocab": string
+    }
+    /** VolumeDataModel */
+    VolumeDataModel: {
+      /** Type */
+      type: string
+      /** Is Volume */
+      is_volume: boolean
+      /** Is Surface */
+      is_surface: boolean
+      /** Detail */
+      detail: { [key: string]: unknown }
+      space: components["schemas"]["SiibraAtIdModel"]
+      /** Url */
+      url?: string
+      /** Url Map */
+      url_map?: { [key: string]: string }
+      /** Map Type */
+      map_type?: string
+      /** Volume Type */
+      volume_type?: string
+    }
+    /** VolumeModel */
+    VolumeModel: {
+      /** @Id */
+      "@id": string
+      /** @Type */
+      "@type": string
+      metadata: components["schemas"]["siibra__openminds__core__v4__products__datasetVersion__Model"]
+      /** Urls */
+      urls: components["schemas"]["Url"][]
+      data: components["schemas"]["VolumeDataModel"]
+    }
+    /** Copyright */
+    siibra__openminds__SANDS__v3__atlas__brainAtlasVersion__Copyright: {
+      /**
+       * @Context
+       * @default [object Object]
+       */
+      "@context"?: components["schemas"]["VocabModel"]
+      /**
+       * holder
+       * @description Legal person in possession of something.
+       */
+      holder: unknown[]
+      /**
+       * year
+       * @description Cycle in the Gregorian calendar specified by a number and comprised of 365 or 366 days divided into 12 months beginning with January and ending with December.
+       */
+      year: string
+    }
+    /** Model */
+    siibra__openminds__SANDS__v3__atlas__brainAtlasVersion__Model: {
+      /**
+       * @Context
+       * @default [object Object]
+       */
+      "@context"?: components["schemas"]["VocabModel"]
+      /**
+       * @Id
+       * @description Metadata node identifier.
+       */
+      "@id": string
+      /** @Type */
+      "@type": string
+      /** abbreviation */
+      abbreviation?: string
+      /**
+       * accessibility
+       * @description Level to which something is accessible to someone or something.
+       */
+      accessibility: { [key: string]: unknown }
+      /** atlasType */
+      atlasType?: { [key: string]: unknown }
+      /**
+       * author
+       * @description Creator of a literary or creative work, as well as a dataset publication.
+       */
+      author?: unknown[]
+      /**
+       * coordinateSpace
+       * @description Two or three dimensional geometric setting.
+       */
+      coordinateSpace: { [key: string]: unknown }
+      /**
+       * Copyright
+       * @description Structured information on the copyright.
+       */
+      copyright?: components["schemas"]["siibra__openminds__SANDS__v3__atlas__brainAtlasVersion__Copyright"]
+      /**
+       * custodian
+       * @description The 'custodian' is a legal person who is responsible for the content and quality of the data, metadata, and/or code of a research product.
+       */
+      custodian?: unknown[]
+      /**
+       * description
+       * @description Longer statement or account giving the characteristics of someone or something.
+       */
+      description?: string
+      /**
+       * digitalIdentifier
+       * @description Digital handle to identify objects or legal persons.
+       */
+      digitalIdentifier?: { [key: string]: unknown }
+      /**
+       * fullDocumentation
+       * @description Non-abridged instructions, comments, and information for using a particular product.
+       */
+      fullDocumentation: { [key: string]: unknown }
+      /**
+       * fullName
+       * @description Whole, non-abbreviated name of something or somebody.
+       */
+      fullName?: string
+      /**
+       * funding
+       * @description Money provided by a legal person for a particular purpose.
+       */
+      funding?: unknown[]
+      hasTerminologyVersion: components["schemas"]["HasTerminologyVersion"]
+      /**
+       * homepage
+       * @description Main website of something or someone.
+       */
+      homepage?: { [key: string]: unknown }
+      /**
+       * howToCite
+       * @description Preferred format for citing a particular object or legal person.
+       */
+      howToCite?: string
+      /**
+       * isAlternativeVersionOf
+       * @description Reference to an original form where the essence was preserved, but presented in an alternative form.
+       */
+      isAlternativeVersionOf?: unknown[]
+      /**
+       * isNewVersionOf
+       * @description Reference to a previous (potentially outdated) particular form of something.
+       */
+      isNewVersionOf?: { [key: string]: unknown }
+      /**
+       * keyword
+       * @description Significant word or concept that are representative of something or someone.
+       */
+      keyword?: unknown[]
+      /**
+       * license
+       * @description Grant by a party to another party as an element of an agreement between those parties that permits to do, use, or own something.
+       */
+      license: { [key: string]: unknown }
+      /**
+       * ontologyIdentifier
+       * @description Term or code used to identify something or someone registered within a particular ontology.
+       */
+      ontologyIdentifier?: string[]
+      /**
+       * Othercontribution
+       * @description Structured information on the contribution made to a research product.
+       */
+      otherContribution?: components["schemas"]["siibra__openminds__SANDS__v3__atlas__brainAtlasVersion__OtherContribution"]
+      /**
+       * relatedPublication
+       * @description Reference to something that was made available for the general public to see or buy.
+       */
+      relatedPublication?: unknown[]
+      /**
+       * releaseDate
+       * Format: date
+       * @description Fixed date on which a product is due to become or was made available for the general public to see or buy
+       */
+      releaseDate: string
+      /**
+       * repository
+       * @description Place, room, or container where something is deposited or stored.
+       */
+      repository?: { [key: string]: unknown }
+      /**
+       * shortName
+       * @description Shortened or fully abbreviated name of something or somebody.
+       */
+      shortName: string
+      /**
+       * supportChannel
+       * @description Way of communication used to interact with users or customers.
+       */
+      supportChannel?: string[]
+      /**
+       * versionIdentifier
+       * @description Term or code used to identify the version of something.
+       */
+      versionIdentifier: string
+      /**
+       * versionInnovation
+       * @description Documentation on what changed in comparison to a previously published form of something.
+       */
+      versionInnovation: string
+    }
+    /** OtherContribution */
+    siibra__openminds__SANDS__v3__atlas__brainAtlasVersion__OtherContribution: {
+      /**
+       * @Context
+       * @default [object Object]
+       */
+      "@context"?: components["schemas"]["VocabModel"]
+      /**
+       * contributionType
+       * @description Distinct class of what was given or supplied as a part or share.
+       */
+      contributionType: unknown[]
+      /**
+       * contributor
+       * @description Legal person that gave or supplied something as a part or share.
+       */
+      contributor: unknown
+    }
+    /** Coordinates */
+    siibra__openminds__SANDS__v3__atlas__parcellationEntityVersion__Coordinates: {
+      /**
+       * @Context
+       * @default [object Object]
+       */
+      "@context"?: components["schemas"]["VocabModel"]
+      /**
+       * typeOfUncertainty
+       * @description Distinct technique used to quantify the uncertainty of a measurement.
+       */
+      typeOfUncertainty?: unknown
+      /**
+       * uncertainty
+       * @description Quantitative value range defining the uncertainty of a measurement.
+       */
+      uncertainty?: number[]
+      /**
+       * unit
+       * @description Determinate quantity adopted as a standard of measurement.
+       */
+      unit?: unknown
+      /**
+       * value
+       * @description Entry for a property.
+       */
+      value: number
+    }
+    /** Model */
+    siibra__openminds__SANDS__v3__atlas__parcellationEntityVersion__Model: {
+      /**
+       * @Context
+       * @default [object Object]
+       */
+      "@context"?: components["schemas"]["VocabModel"]
+      /**
+       * @Id
+       * @description Metadata node identifier.
+       */
+      "@id": string
+      /** @Type */
+      "@type": string
+      hasAnnotation?: components["schemas"]["HasAnnotation"]
+      /**
+       * hasParent
+       * @description Reference to a parent object or legal person.
+       */
+      hasParent?: unknown[]
+      /** lookupLabel */
+      lookupLabel?: string
+      /**
+       * name
+       * @description Word or phrase that constitutes the distinctive designation of a being or thing.
+       */
+      name?: string
+      /**
+       * ontologyIdentifier
+       * @description Term or code used to identify something or someone registered within a particular ontology.
+       */
+      ontologyIdentifier?: string[]
+      /** Relationassessment */
+      relationAssessment?: Partial<
+        components["schemas"]["RelationAssessmentItem"]
+      > &
+        Partial<components["schemas"]["RelationAssessmentItem1"]>
+      /**
+       * versionIdentifier
+       * @description Term or code used to identify the version of something.
+       */
+      versionIdentifier: string
+      /**
+       * versionInnovation
+       * @description Documentation on what changed in comparison to a previously published form of something.
+       */
+      versionInnovation?: string
+    }
+    /** Coordinates */
+    siibra__openminds__SANDS__v3__miscellaneous__coordinatePoint__Coordinates: {
+      /**
+       * @Context
+       * @default [object Object]
+       */
+      "@context"?: components["schemas"]["VocabModel"]
+      /**
+       * typeOfUncertainty
+       * @description Distinct technique used to quantify the uncertainty of a measurement.
+       */
+      typeOfUncertainty?: unknown
+      /**
+       * uncertainty
+       * @description Quantitative value range defining the uncertainty of a measurement.
+       */
+      uncertainty?: number[]
+      /**
+       * unit
+       * @description Determinate quantity adopted as a standard of measurement.
+       */
+      unit?: unknown
+      /**
+       * value
+       * @description Entry for a property.
+       */
+      value: number
+    }
+    /** Model */
+    siibra__openminds__SANDS__v3__miscellaneous__coordinatePoint__Model: {
+      /**
+       * @Context
+       * @default [object Object]
+       */
+      "@context"?: components["schemas"]["VocabModel"]
+      /**
+       * @Id
+       * @description Metadata node identifier.
+       */
+      "@id": string
+      /** @Type */
+      "@type": string
+      /**
+       * coordinateSpace
+       * @description Two or three dimensional geometric setting.
+       */
+      coordinateSpace: { [key: string]: unknown }
+      /**
+       * Coordinates
+       * @description Structured information on a quantitative value.
+       */
+      coordinates: components["schemas"]["siibra__openminds__SANDS__v3__miscellaneous__coordinatePoint__Coordinates"][]
+    }
+    /** Copyright */
+    siibra__openminds__core__v4__products__datasetVersion__Copyright: {
+      /**
+       * @Context
+       * @default [object Object]
+       */
+      "@context"?: components["schemas"]["VocabModel"]
+      /**
+       * holder
+       * @description Legal person in possession of something.
+       */
+      holder: unknown[]
+      /**
+       * year
+       * @description Cycle in the Gregorian calendar specified by a number and comprised of 365 or 366 days divided into 12 months beginning with January and ending with December.
+       */
+      year: string
+    }
+    /** Model */
+    siibra__openminds__core__v4__products__datasetVersion__Model: {
+      /**
+       * @Context
+       * @default [object Object]
+       */
+      "@context"?: components["schemas"]["VocabModel"]
+      /**
+       * @Id
+       * @description Metadata node identifier.
+       */
+      "@id": string
+      /** @Type */
+      "@type": string
+      /**
+       * accessibility
+       * @description Level to which something is accessible to someone or something.
+       */
+      accessibility: { [key: string]: unknown }
+      /**
+       * author
+       * @description Creator of a literary or creative work, as well as a dataset publication.
+       */
+      author?: unknown[]
+      /** behavioralProtocol */
+      behavioralProtocol?: unknown[]
+      /**
+       * Copyright
+       * @description Structured information on the copyright.
+       */
+      copyright?: components["schemas"]["siibra__openminds__core__v4__products__datasetVersion__Copyright"]
+      /**
+       * custodian
+       * @description The 'custodian' is a legal person who is responsible for the content and quality of the data, metadata, and/or code of a research product.
+       */
+      custodian?: unknown[]
+      /** dataType */
+      dataType: unknown[]
+      /**
+       * description
+       * @description Longer statement or account giving the characteristics of someone or something.
+       */
+      description?: string
+      /**
+       * digitalIdentifier
+       * @description Digital handle to identify objects or legal persons.
+       */
+      digitalIdentifier: { [key: string]: unknown }
+      /**
+       * ethicsAssessment
+       * @description Judgment about the applied principles of conduct governing an individual or a group.
+       */
+      ethicsAssessment: { [key: string]: unknown }
+      /** experimentalApproach */
+      experimentalApproach: unknown[]
+      /**
+       * fullDocumentation
+       * @description Non-abridged instructions, comments, and information for using a particular product.
+       */
+      fullDocumentation: { [key: string]: unknown }
+      /**
+       * fullName
+       * @description Whole, non-abbreviated name of something or somebody.
+       */
+      fullName?: string
+      /**
+       * funding
+       * @description Money provided by a legal person for a particular purpose.
+       */
+      funding?: unknown[]
+      /**
+       * homepage
+       * @description Main website of something or someone.
+       */
+      homepage?: { [key: string]: unknown }
+      /**
+       * howToCite
+       * @description Preferred format for citing a particular object or legal person.
+       */
+      howToCite?: string
+      /**
+       * inputData
+       * @description Data that is put into a process or machine.
+       */
+      inputData?: unknown[]
+      /**
+       * isAlternativeVersionOf
+       * @description Reference to an original form where the essence was preserved, but presented in an alternative form.
+       */
+      isAlternativeVersionOf?: unknown[]
+      /**
+       * isNewVersionOf
+       * @description Reference to a previous (potentially outdated) particular form of something.
+       */
+      isNewVersionOf?: { [key: string]: unknown }
+      /**
+       * keyword
+       * @description Significant word or concept that are representative of something or someone.
+       */
+      keyword?: unknown[]
+      /**
+       * license
+       * @description Grant by a party to another party as an element of an agreement between those parties that permits to do, use, or own something.
+       */
+      license: { [key: string]: unknown }
+      /**
+       * Othercontribution
+       * @description Structured information on the contribution made to a research product.
+       */
+      otherContribution?: components["schemas"]["siibra__openminds__core__v4__products__datasetVersion__OtherContribution"]
+      /** preparationDesign */
+      preparationDesign?: unknown[]
+      /**
+       * relatedPublication
+       * @description Reference to something that was made available for the general public to see or buy.
+       */
+      relatedPublication?: unknown[]
+      /**
+       * releaseDate
+       * Format: date
+       * @description Fixed date on which a product is due to become or was made available for the general public to see or buy
+       */
+      releaseDate: string
+      /**
+       * repository
+       * @description Place, room, or container where something is deposited or stored.
+       */
+      repository?: { [key: string]: unknown }
+      /**
+       * shortName
+       * @description Shortened or fully abbreviated name of something or somebody.
+       */
+      shortName: string
+      /** studiedSpecimen */
+      studiedSpecimen?: unknown[]
+      /**
+       * studyTarget
+       * @description Structure or function that was targeted within a study.
+       */
+      studyTarget?: unknown[]
+      /**
+       * supportChannel
+       * @description Way of communication used to interact with users or customers.
+       */
+      supportChannel?: string[]
+      /**
+       * technique
+       * @description Method of accomplishing a desired aim.
+       */
+      technique: unknown[]
+      /**
+       * versionIdentifier
+       * @description Term or code used to identify the version of something.
+       */
+      versionIdentifier: string
+      /**
+       * versionInnovation
+       * @description Documentation on what changed in comparison to a previously published form of something.
+       */
+      versionInnovation: string
+    }
+    /** OtherContribution */
+    siibra__openminds__core__v4__products__datasetVersion__OtherContribution: {
+      /**
+       * @Context
+       * @default [object Object]
+       */
+      "@context"?: components["schemas"]["VocabModel"]
+      /**
+       * contributionType
+       * @description Distinct class of what was given or supplied as a part or share.
+       */
+      contributionType: unknown[]
+      /**
+       * contributor
+       * @description Legal person that gave or supplied something as a part or share.
+       */
+      contributor: unknown
+    }
+  }
+}
+
+export interface operations {
+  /** Returns all regions for a given parcellation id. */
+  get_all_regions_from_atlas_parc_space_atlases__atlas_id__parcellations__parcellation_id__regions_get: {
+    parameters: {
+      path: {
+        atlas_id: string
+        parcellation_id: string
+      }
+      query: {
+        space_id?: string
+        find?: string
+      }
+    }
+    responses: {
+      /** Successful Response */
+      200: {
+        content: {
+          "application/json": components["schemas"]["siibra__openminds__SANDS__v3__atlas__parcellationEntityVersion__Model"][]
+        }
+      }
+      /** Validation Error */
+      422: {
+        content: {
+          "application/json": components["schemas"]["HTTPValidationError"]
+        }
+      }
+    }
+  }
+  /** Returns all regional features for a region. */
+  get_all_regional_features_for_region_atlases__atlas_id__parcellations__parcellation_id__regions__region_id__features_get: {
+    parameters: {
+      path: {
+        atlas_id: string
+        parcellation_id: string
+        region_id: string
+      }
+      query: {
+        space_id?: string
+        type?: string
+      }
+    }
+    responses: {
+      /** Successful Response */
+      200: {
+        content: {
+          "application/json": (Partial<
+            components["schemas"]["ReceptorDatasetModel"]
+          > &
+            Partial<components["schemas"]["BaseDatasetJsonModel"]> &
+            Partial<components["schemas"]["CorticalCellDistributionModel"]>)[]
+        }
+      }
+      /** Validation Error */
+      422: {
+        content: {
+          "application/json": components["schemas"]["HTTPValidationError"]
+        }
+      }
+    }
+  }
+  /** Returns a feature for a region, as defined by by the modality and feature ID */
+  get_single_detailed_regional_feature_atlases__atlas_id__parcellations__parcellation_id__regions__region_id__features__feature_id__get: {
+    parameters: {
+      path: {
+        atlas_id: string
+        parcellation_id: string
+        region_id: string
+        feature_id: string
+      }
+      query: {
+        space_id?: string
+        gene?: string
+      }
+    }
+    responses: {
+      /** Successful Response */
+      200: {
+        content: {
+          "application/json": Partial<
+            components["schemas"]["ReceptorDatasetModel"]
+          > &
+            Partial<components["schemas"]["BaseDatasetJsonModel"]> &
+            Partial<components["schemas"]["CorticalCellDistributionModel"]>
+        }
+      }
+      /** Validation Error */
+      422: {
+        content: {
+          "application/json": components["schemas"]["HTTPValidationError"]
+        }
+      }
+    }
+  }
+  /** Returns information about a regional map for given region name. */
+  get_regional_map_info_atlases__atlas_id__parcellations__parcellation_id__regions__region_id__regional_map_info_get: {
+    parameters: {
+      path: {
+        atlas_id: string
+        parcellation_id: string
+        region_id: string
+      }
+      query: {
+        space_id?: string
+      }
+    }
+    responses: {
+      /** Successful Response */
+      200: {
+        content: {
+          "application/json": components["schemas"]["NiiMetadataModel"]
+        }
+      }
+      /** Validation Error */
+      422: {
+        content: {
+          "application/json": components["schemas"]["HTTPValidationError"]
+        }
+      }
+    }
+  }
+  /** Returns a regional map for given region name. */
+  get_regional_map_file_atlases__atlas_id__parcellations__parcellation_id__regions__region_id__regional_map_map_get: {
+    parameters: {
+      path: {
+        atlas_id: string
+        parcellation_id: string
+        region_id: string
+      }
+      query: {
+        space_id?: string
+      }
+    }
+    responses: {
+      /** Successful Response */
+      200: {
+        content: {
+          "application/json": unknown
+          "application/octet-stream": string
+        }
+      }
+      /** Validation Error */
+      422: {
+        content: {
+          "application/json": components["schemas"]["HTTPValidationError"]
+        }
+      }
+    }
+  }
+  get_regional_volumes_atlases__atlas_id__parcellations__parcellation_id__regions__region_id__volumes_get: {
+    parameters: {
+      path: {
+        atlas_id: string
+        parcellation_id: string
+        region_id: string
+      }
+      query: {
+        space_id?: string
+        type?: string
+        page?: number
+        size?: number
+      }
+    }
+    responses: {
+      /** Successful Response */
+      200: {
+        content: {
+          "application/json": components["schemas"]["Page_VolumeModel_"]
+        }
+      }
+      /** Validation Error */
+      422: {
+        content: {
+          "application/json": components["schemas"]["HTTPValidationError"]
+        }
+      }
+    }
+  }
+  get_single_region_detail_atlases__atlas_id__parcellations__parcellation_id__regions__region_id__get: {
+    parameters: {
+      path: {
+        atlas_id: string
+        parcellation_id: string
+        region_id: string
+      }
+      query: {
+        space_id?: string
+      }
+    }
+    responses: {
+      /** Successful Response */
+      200: {
+        content: {
+          "application/json": components["schemas"]["siibra__openminds__SANDS__v3__atlas__parcellationEntityVersion__Model"]
+        }
+      }
+      /** Validation Error */
+      422: {
+        content: {
+          "application/json": components["schemas"]["HTTPValidationError"]
+        }
+      }
+    }
+  }
+  /** Returns all parcellations that are defined in the siibra client for given atlas. */
+  get_all_parcellations_atlases__atlas_id__parcellations_get: {
+    parameters: {
+      path: {
+        atlas_id: string
+      }
+    }
+    responses: {
+      /** Successful Response */
+      200: {
+        content: {
+          "application/json": components["schemas"]["SapiParcellationModel"][]
+        }
+      }
+      /** Validation Error */
+      422: {
+        content: {
+          "application/json": components["schemas"]["HTTPValidationError"]
+        }
+      }
+    }
+  }
+  /** Returns a global feature for a specific modality id. */
+  get_single_detailed_global_feature_atlases__atlas_id__parcellations__parcellation_id__features__feature_id__get: {
+    parameters: {
+      path: {
+        atlas_id: string
+        parcellation_id: string
+        feature_id: string
+      }
+    }
+    responses: {
+      /** Successful Response */
+      200: {
+        content: {
+          "application/json": Partial<
+            components["schemas"]["ConnectivityMatrixDataModel"]
+          > &
+            Partial<components["schemas"]["SerializationErrorModel"]>
+        }
+      }
+      /** Validation Error */
+      422: {
+        content: {
+          "application/json": components["schemas"]["HTTPValidationError"]
+        }
+      }
+    }
+  }
+  /** Returns all global features for a parcellation. */
+  get_all_global_features_for_parcellation_atlases__atlas_id__parcellations__parcellation_id__features_get: {
+    parameters: {
+      path: {
+        atlas_id: string
+        parcellation_id: string
+      }
+      query: {
+        type?: string
+        page?: number
+        size?: number
+      }
+    }
+    responses: {
+      /** Successful Response */
+      200: {
+        content: {
+          "application/json": components["schemas"]["Page_Union_siibra.features.connectivity.ConnectivityMatrixDataModel__app.models.SerializationErrorModel__"]
+        }
+      }
+      /** Validation Error */
+      422: {
+        content: {
+          "application/json": components["schemas"]["HTTPValidationError"]
+        }
+      }
+    }
+  }
+  /** Returns one parcellation for given id. */
+  get_volumes_for_parcellation_atlases__atlas_id__parcellations__parcellation_id__volumes_get: {
+    parameters: {
+      path: {
+        atlas_id: string
+        parcellation_id: string
+      }
+    }
+    responses: {
+      /** Successful Response */
+      200: {
+        content: {
+          "application/json": components["schemas"]["VolumeModel"][]
+        }
+      }
+      /** Validation Error */
+      422: {
+        content: {
+          "application/json": components["schemas"]["HTTPValidationError"]
+        }
+      }
+    }
+  }
+  /** Returns one parcellation for given id. */
+  get_single_parcellation_detail_atlases__atlas_id__parcellations__parcellation_id__get: {
+    parameters: {
+      path: {
+        atlas_id: string
+        parcellation_id: string
+      }
+    }
+    responses: {
+      /** Successful Response */
+      200: {
+        content: {
+          "application/json": components["schemas"]["SapiParcellationModel"]
+        }
+      }
+      /** Validation Error */
+      422: {
+        content: {
+          "application/json": components["schemas"]["HTTPValidationError"]
+        }
+      }
+    }
+  }
+  /** Returns all spaces that are defined in the siibra client. */
+  get_all_spaces_atlases__atlas_id__spaces_get: {
+    parameters: {
+      path: {
+        atlas_id: string
+      }
+    }
+    responses: {
+      /** Successful Response */
+      200: {
+        content: {
+          "application/json": components["schemas"]["SapiSpaceModel"][]
+        }
+      }
+      /** Validation Error */
+      422: {
+        content: {
+          "application/json": components["schemas"]["HTTPValidationError"]
+        }
+      }
+    }
+  }
+  /** Returns a template for a given space id. */
+  get_template_by_space_id_atlases__atlas_id__spaces__space_id__templates_get: {
+    parameters: {
+      path: {
+        atlas_id: string
+        space_id: string
+      }
+    }
+    responses: {
+      /** Successful Response */
+      200: {
+        content: {
+          "application/json": unknown
+          "application/octet-stream": string
+        }
+      }
+      /** Validation Error */
+      422: {
+        content: {
+          "application/json": components["schemas"]["HTTPValidationError"]
+        }
+      }
+    }
+  }
+  /** Returns all parcellation maps for a given space id. */
+  get_parcellation_map_for_space_atlases__atlas_id__spaces__space_id__parcellation_maps_get: {
+    parameters: {
+      path: {
+        atlas_id: string
+        space_id: string
+      }
+    }
+    responses: {
+      /** Successful Response */
+      200: {
+        content: {
+          "application/json": unknown
+          "application/octet-stream": string
+        }
+      }
+      /** Validation Error */
+      422: {
+        content: {
+          "application/json": components["schemas"]["HTTPValidationError"]
+        }
+      }
+    }
+  }
+  /**
+   * Get a detailed view on a single spatial feature.
+   * A parcellation id and region id can be provided optional to get more details.
+   */
+  get_single_detailed_spatial_feature_atlases__atlas_id__spaces__space_id__features__feature_id__get: {
+    parameters: {
+      path: {
+        feature_id: string
+        atlas_id: string
+        space_id: string
+      }
+      query: {
+        parcellation_id?: string
+        region?: string
+        bbox?: string
+      }
+    }
+    responses: {
+      /** Successful Response */
+      200: {
+        content: {
+          "application/json": Partial<
+            components["schemas"]["IEEGSessionModel"]
+          > &
+            Partial<components["schemas"]["VOIDataModel"]>
+        }
+      }
+      /** Validation Error */
+      422: {
+        content: {
+          "application/json": components["schemas"]["HTTPValidationError"]
+        }
+      }
+    }
+  }
+  /** Return all possible feature names and links to get more details */
+  get_all_spatial_features_for_space_atlases__atlas_id__spaces__space_id__features_get: {
+    parameters: {
+      path: {
+        atlas_id: string
+        space_id: string
+      }
+      query: {
+        parcellation_id?: string
+        region?: string
+        bbox?: string
+      }
+    }
+    responses: {
+      /** Successful Response */
+      200: {
+        content: {
+          "application/json": (Partial<
+            components["schemas"]["IEEGSessionModel"]
+          > &
+            Partial<components["schemas"]["VOIDataModel"]>)[]
+        }
+      }
+      /** Validation Error */
+      422: {
+        content: {
+          "application/json": components["schemas"]["HTTPValidationError"]
+        }
+      }
+    }
+  }
+  get_volumes_for_space_atlases__atlas_id__spaces__space_id__volumes_get: {
+    parameters: {
+      path: {
+        atlas_id: string
+        space_id: string
+      }
+    }
+    responses: {
+      /** Successful Response */
+      200: {
+        content: {
+          "application/json": components["schemas"]["VolumeModel"][]
+        }
+      }
+      /** Validation Error */
+      422: {
+        content: {
+          "application/json": components["schemas"]["HTTPValidationError"]
+        }
+      }
+    }
+  }
+  /** Returns one space for given id, with links to further resources */
+  get_single_space_detail_atlases__atlas_id__spaces__space_id__get: {
+    parameters: {
+      path: {
+        atlas_id: string
+        space_id: string
+      }
+    }
+    responses: {
+      /** Successful Response */
+      200: {
+        content: {
+          "application/json": components["schemas"]["SapiSpaceModel"]
+        }
+      }
+      /** Validation Error */
+      422: {
+        content: {
+          "application/json": components["schemas"]["HTTPValidationError"]
+        }
+      }
+    }
+  }
+  /** Get all atlases known by siibra. */
+  get_all_atlases_atlases_get: {
+    responses: {
+      /** Successful Response */
+      200: {
+        content: {
+          "application/json": components["schemas"]["SapiAtlasModel"][]
+        }
+      }
+    }
+  }
+  /** Get more information for a specific atlas with links to further objects. */
+  get_atlas_by_id_atlases__atlas_id__get: {
+    parameters: {
+      path: {
+        atlas_id: string
+      }
+    }
+    responses: {
+      /** Successful Response */
+      200: {
+        content: {
+          "application/json": components["schemas"]["SapiAtlasModel"]
+        }
+      }
+      /** Validation Error */
+      422: {
+        content: {
+          "application/json": components["schemas"]["HTTPValidationError"]
+        }
+      }
+    }
+  }
+  /** Return all genes (name, acronym) in siibra */
+  get_gene_names_genes_get: {
+    responses: {
+      /** Successful Response */
+      200: {
+        content: {
+          "application/json": unknown
+        }
+      }
+    }
+  }
+  /** Return all possible modalities */
+  get_all_available_modalities_modalities_get: {
+    responses: {
+      /** Successful Response */
+      200: {
+        content: {
+          "application/json": unknown
+        }
+      }
+    }
+  }
+  /**
+   * Get all details for one feature by id.
+   * Since the feature id is unique, no atlas concept is required.
+   *
+   * Further optional params can extend the result.
+   * :param feature_id:
+   * :param atlas_id:
+   * :param space_id:
+   * :param parcellation_id:
+   * :param region_id:
+   * :return: FeatureModels
+   */
+  get_feature_details_features__feature_id__get: {
+    parameters: {
+      path: {
+        feature_id: string
+      }
+      query: {
+        atlas_id?: string
+        space_id?: string
+        parcellation_id?: string
+        region_id?: string
+      }
+    }
+    responses: {
+      /** Successful Response */
+      200: {
+        content: {
+          "application/json": Partial<
+            components["schemas"]["ReceptorDatasetModel"]
+          > &
+            Partial<components["schemas"]["BaseDatasetJsonModel"]> &
+            Partial<components["schemas"]["CorticalCellDistributionModel"]> &
+            Partial<components["schemas"]["IEEGSessionModel"]> &
+            Partial<components["schemas"]["VOIDataModel"]> &
+            Partial<components["schemas"]["ConnectivityMatrixDataModel"]> &
+            Partial<components["schemas"]["SerializationErrorModel"]>
+        }
+      }
+      /** Validation Error */
+      422: {
+        content: {
+          "application/json": components["schemas"]["HTTPValidationError"]
+        }
+      }
+    }
+  }
+}
+
+export interface external {}
diff --git a/src/atlasComponents/sapi/stories.base.ts b/src/atlasComponents/sapi/stories.base.ts
new file mode 100644
index 0000000000000000000000000000000000000000..51fde4f0cd5695f6055398e8bd9ec39ed469c541
--- /dev/null
+++ b/src/atlasComponents/sapi/stories.base.ts
@@ -0,0 +1,151 @@
+import { SAPI, SapiAtlasModel, SapiParcellationModel, SapiRegionalFeatureModel, SapiRegionModel, SapiSpaceModel } from "."
+import { cleanIeegSessionDatasets, SapiParcellationFeatureModel, SapiSpatialFeatureModel, SxplrCleanedFeatureModel } from "./type"
+import addons from '@storybook/addons';
+import { DARKTHEME } from "src/util/injectionTokens";
+import { APP_INITIALIZER, NgZone } from "@angular/core";
+import { DOCUMENT } from "@angular/common";
+import { BehaviorSubject } from "rxjs";
+
+export function addAddonEventListener(eventName: string, callback: (...args: any[]) => void){
+  const channel = addons.getChannel()
+  channel.on(eventName, callback)
+  return () => channel.off(eventName, callback)
+}
+
+export const provideDarkTheme = [{
+  provide: DARKTHEME,
+  useFactory: (zone: NgZone, document: Document) => {
+    const useDarkTheme = document.body.getAttribute('darktheme') === "true"
+    const sub = new BehaviorSubject(useDarkTheme)
+    addAddonEventListener("DARK_MODE", flag => {
+      zone.run(() => {
+        sub.next(flag)
+      })
+    })
+    return sub
+  },
+  deps: [ NgZone, DOCUMENT ]
+}, {
+  provide: APP_INITIALIZER,
+  multi: true,
+  useFactory: (document: Document) => {
+    addAddonEventListener("DARK_MODE", flag => {
+      document.body.setAttribute('darktheme', flag.toString())
+    })
+    return () => Promise.resolve()
+  }, 
+  deps: [ DOCUMENT ]
+}]
+
+export const atlasId = {
+  human: 'juelich/iav/atlas/v1.0.0/1',
+  rat: "minds/core/parcellationatlas/v1.0.0/522b368e-49a3-49fa-88d3-0870a307974a",
+  mouse: "juelich/iav/atlas/v1.0.0/2",
+  monkey: "juelich/iav/atlas/v1.0.0/monkey"
+}
+
+export const spaceId = {
+  human: {
+    mni152: 'minds/core/referencespace/v1.0.0/dafcffc5-4826-4bf1-8ff6-46b8a31ff8e2',
+    bigbrain: 'minds/core/referencespace/v1.0.0/a1655b99-82f1-420f-a3c2-fe80fd4c8588'
+  },
+  rat: {
+    waxholm: "minds/core/referencespace/v1.0.0/d5717c4a-0fa1-46e6-918c-b8003069ade8"
+  }
+}
+
+export const parcId = {
+  human: {
+    jba29: "minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579-290",
+    longBundle: "juelich/iav/atlas/v1.0.0/5",
+    difumo256: "minds/core/parcellationatlas/v1.0.0/141d510f-0342-4f94-ace7-c97d5f160235",
+    corticalLayers: "juelich/iav/atlas/v1.0.0/3"
+  },
+  rat: {
+    v4: 'minds/core/parcellationatlas/v1.0.0/ebb923ba-b4d5-4b82-8088-fa9215c2e1fe-v4'
+  }
+}
+
+export async function getAtlases(): Promise<SapiAtlasModel[]> {
+  return await (await fetch(`${SAPI.bsEndpoint}/atlases`)).json() as SapiAtlasModel[]
+}
+
+export async function getAtlas(id: string): Promise<SapiAtlasModel>{
+  return await (await fetch(`${SAPI.bsEndpoint}/atlases/${id}`)).json()
+}
+
+export async function getParc(atlasId: string, id: string): Promise<SapiParcellationModel>{
+  return await (await fetch(`${SAPI.bsEndpoint}/atlases/${atlasId}/parcellations/${id}`)).json()
+}
+export async function getParcRegions(atlasId: string, id: string, spaceId: string): Promise<SapiRegionModel[]>{
+  return await (await fetch(`${SAPI.bsEndpoint}/atlases/${atlasId}/parcellations/${id}/regions?space_id=${encodeURIComponent(spaceId)}`)).json()
+}
+
+export async function getSpace(atlasId: string, id: string): Promise<SapiSpaceModel> {
+  return await (await fetch(`${SAPI.bsEndpoint}/atlases/${atlasId}/spaces/${id}`)).json()
+}
+
+export async function getHumanAtlas(): Promise<SapiAtlasModel> {
+  return getAtlas(atlasId.human)
+}
+
+export async function getMni152(): Promise<SapiSpaceModel> {
+  return await (await fetch(`${SAPI.bsEndpoint}/atlases/${atlasId.human}/spaces/${spaceId.human.mni152}`)).json()
+}
+
+export async function getJba29(): Promise<SapiParcellationModel> {
+  return await getParc(atlasId.human, parcId.human.jba29)
+}
+
+export async function getJba29Regions(): Promise<SapiRegionModel[]> {
+  return await getParcRegions(atlasId.human, parcId.human.jba29, spaceId.human.mni152)
+}
+
+export async function getHoc1Right(spaceId=null): Promise<SapiRegionModel> {
+  if (!spaceId) {
+    return await (await fetch(`${SAPI.bsEndpoint}/atlases/${atlasId.human}/parcellations/${parcId.human.jba29}/regions/hoc1%20right`)).json()
+  }
+  return await (await fetch(`${SAPI.bsEndpoint}/atlases/${atlasId.human}/parcellations/${parcId.human.jba29}/regions/hoc1%20right?space_id=${encodeURIComponent(spaceId)}`)).json()
+}
+
+export async function get44Left(spaceId=null): Promise<SapiRegionModel> {
+  if (!spaceId) {
+    return await (await fetch(`${SAPI.bsEndpoint}/atlases/${atlasId.human}/parcellations/${parcId.human.jba29}/regions/area%2044%20left`)).json()
+  }
+  return await (await fetch(`${SAPI.bsEndpoint}/atlases/${atlasId.human}/parcellations/${parcId.human.jba29}/regions/area%2044%20left?space_id=${encodeURIComponent(spaceId)}`)).json()
+}
+
+export async function getHoc1RightSpatialFeatures(): Promise<SxplrCleanedFeatureModel[]> {
+  const json: SapiSpatialFeatureModel[] = await (await fetch(`${SAPI.bsEndpoint}/atlases/${atlasId.human}/spaces/${spaceId.human.mni152}/features?parcellation_id=2.9&region=hoc1%20right`)).json()
+  return cleanIeegSessionDatasets(json.filter(it => it['@type'] === "siibra/features/ieegSession"))
+}
+
+export async function getHoc1RightFeatures(): Promise<SapiRegionalFeatureModel[]> {
+  return await (await fetch(`${SAPI.bsEndpoint}/atlases/${atlasId.human}/parcellations/${parcId.human.jba29}/regions/hoc1%20right/features`)).json()
+}
+
+export async function getHoc1RightFeatureDetail(featId: string): Promise<SapiRegionalFeatureModel>{
+  return await (await fetch(`${SAPI.bsEndpoint}/atlases/${atlasId.human}/parcellations/${parcId.human.jba29}/regions/hoc1%20right/features/${encodeURIComponent(featId)}`)).json()
+}
+
+export async function getJba29Features(): Promise<SapiParcellationFeatureModel[]> {
+  return await (await fetch(`${SAPI.bsEndpoint}/atlases/${atlasId.human}/parcellations/${parcId.human.jba29}/features`)).json()
+}
+
+export async function getBigbrainSpatialFeatures(): Promise<SapiSpatialFeatureModel[]>{
+  const bbox = [
+    [-1000, -1000, -1000],
+    [1000, 1000, 1000]
+  ]
+  const url = new URL(`${SAPI.bsEndpoint}/atlases/${atlasId.human}/spaces/${spaceId.human.bigbrain}/features`)
+  url.searchParams.set(`bbox`, JSON.stringify(bbox))
+  return await (await fetch(url.toString())).json()
+}
+
+export async function getMni152SpatialFeatureHoc1Right(): Promise<SapiSpatialFeatureModel[]>{
+  
+  const url = new URL(`${SAPI.bsEndpoint}/atlases/${atlasId.human}/spaces/${spaceId.human.mni152}/features`)
+  url.searchParams.set(`parcellation_id`, parcId.human.jba29)
+  url.searchParams.set("region", 'hoc1 right')
+  return await (await fetch(url.toString())).json()
+}
diff --git a/src/atlasComponents/sapi/type.ts b/src/atlasComponents/sapi/type.ts
new file mode 100644
index 0000000000000000000000000000000000000000..905c0dee7f004cba521b96860e44c4da6e801d10
--- /dev/null
+++ b/src/atlasComponents/sapi/type.ts
@@ -0,0 +1,121 @@
+import { OperatorFunction } from "rxjs"
+import { map } from "rxjs/operators"
+import { components, operations, paths } from "./schema"
+
+export type IdName = {
+  id: string
+  name: string
+}
+
+export type SapiModalityModel = {
+  name: string
+  types: string[]
+}
+
+type Point = [number, number, number]
+
+export type BoundingBoxConcept = [Point, Point]
+
+export type SapiAtlasModel = components["schemas"]["SapiAtlasModel"]
+export type SapiSpaceModel = components["schemas"]["SapiSpaceModel"]
+export type SapiParcellationModel = components["schemas"]["SapiParcellationModel"]
+export type SapiRegionModel = components["schemas"]["siibra__openminds__SANDS__v3__atlas__parcellationEntityVersion__Model"]
+export type OpenMINDSCoordinatePoint = components['schemas']['siibra__openminds__SANDS__v3__miscellaneous__coordinatePoint__Model']
+export type SxplrCoordinatePointExtension = {
+  openminds: OpenMINDSCoordinatePoint
+  name: string
+  description: string
+  color: string
+  '@id': string // should match the id of opendminds specs
+}
+
+export type SapiRegionMapInfoModel = components["schemas"]["NiiMetadataModel"]
+export type SapiVOIDataResponse = components["schemas"]["VOIDataModel"]
+export type SapiVolumeModel = components["schemas"]["VolumeModel"]
+export type SapiDatasetModel = components["schemas"]["DatasetJsonModel"]
+export type SpyNpArrayDataModel = components["schemas"]["NpArrayDataModel"]
+export type SapiIeegSessionModel = components["schemas"]["IEEGSessionModel"]
+
+/**
+ * utility types
+ */
+type PathReturn<T extends keyof paths> = Required<paths[T]["get"]["responses"][200]["content"]["application/json"]>
+export type PaginatedResponse<T> = {
+  items: T[]
+  total: number
+  page: number
+  size: number
+}
+
+/**
+ * serialization error type
+ */
+export type SapiSerializationErrorModel = components["schemas"]["SerializationErrorModel"]
+
+/**
+ * datafeatures from operations
+ */
+
+export type SapiRegionalFeatureModel = PathReturn<"/atlases/{atlas_id}/parcellations/{parcellation_id}/regions/{region_id}/features/{feature_id}">
+export type SapiParcellationFeatureModel = PathReturn<"/atlases/{atlas_id}/parcellations/{parcellation_id}/features/{feature_id}">
+export type SapiSpatialFeatureModel = PathReturn<"/atlases/{atlas_id}/spaces/{space_id}/features/{feature_id}">
+
+export type SapiFeatureModel = SapiRegionalFeatureModel | SapiSpatialFeatureModel | SapiParcellationFeatureModel
+
+/**
+ * specific data features
+ */
+
+export type SapiRegionalFeatureReceptorModel = components["schemas"]["ReceptorDatasetModel"]
+export type SapiParcellationFeatureMatrixModel = components["schemas"]["ConnectivityMatrixDataModel"]
+
+
+export const CLEANED_IEEG_DATASET_TYPE = 'sxplr/cleanedIeegDataset'
+export type CleanedIeegDataset = Required<
+  Omit<SapiDatasetModel, "@type"> & {
+    '@type': 'sxplr/cleanedIeegDataset'
+    sessions: Record<string, Omit<SapiIeegSessionModel, "dataset">>
+  }
+>
+
+export function cleanIeegSessionDatasets(ieegSessions: SapiIeegSessionModel[]): CleanedIeegDataset[]{
+  const returnArr: CleanedIeegDataset[] = []
+  for (const sess of ieegSessions) {
+    const { dataset, ...itemToAppend } = sess
+    const existing = returnArr.find(it => it["@id"] === dataset["@id"])
+    if (!existing) {
+      returnArr.push({
+        ...dataset,
+        '@type': CLEANED_IEEG_DATASET_TYPE,
+        sessions: {
+          [sess.sub_id]: itemToAppend
+        }
+      })
+      continue
+    }
+    existing.sessions[sess.sub_id] = itemToAppend
+  }
+  return returnArr
+}
+
+export type SxplrCleanedFeatureModel = CleanedIeegDataset
+
+export function guardPipe<
+  InputType,
+  GuardType extends InputType
+>(
+    guardFn: (input: InputType) => input is GuardType
+): OperatorFunction<InputType, GuardType> {
+  return src => src.pipe(
+    map(val => {
+      if (guardFn(val)) {
+        return val
+      }
+      throw new Error(`TypeGuard Error`)
+    })
+  )
+}
+
+export type SapiQueryPriorityArg = {
+  priority: number
+}
diff --git a/src/atlasComponents/sapiViews/core/atlas/dropdownAtlasSelector/dropdownAtlasSelector.component.ts b/src/atlasComponents/sapiViews/core/atlas/dropdownAtlasSelector/dropdownAtlasSelector.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..815a4d45ed29cad974c1465e8a8cca5a0af6bcee
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/atlas/dropdownAtlasSelector/dropdownAtlasSelector.component.ts
@@ -0,0 +1,56 @@
+import { Component, OnDestroy } from "@angular/core";
+import { Store, select } from "@ngrx/store";
+import { Observable, Subscription } from "rxjs";
+import { ARIA_LABELS } from 'common/constants'
+import { atlasSelection, generalActions } from "src/state"
+import { SAPI, SapiAtlasModel } from "src/atlasComponents/sapi";
+
+@Component({
+  selector: 'sxplr-sapiviews-core-atlas-dropdown-selector',
+  templateUrl: './dropdownAtlasSelector.template.html',
+  styleUrls: [
+    './dropdownAtlasSelector.style.css'
+  ]
+})
+
+export class SapiViewsCoreAtlasAtlasDropdownSelector implements OnDestroy{
+
+  private subs: Subscription[] = []
+  private fetchedAtlases: SapiAtlasModel[] = []
+  public fetchedAtlases$: Observable<SapiAtlasModel[]> = this.sapi.atlases$
+  public selectedAtlas$: Observable<SapiAtlasModel> = this.store$.pipe(
+    select(atlasSelection.selectors.selectedAtlas)
+  )
+
+  public SELECT_ATLAS_ARIA_LABEL = ARIA_LABELS.SELECT_ATLAS
+
+  constructor(
+    private store$: Store<any>,
+    private sapi: SAPI,
+  ){
+    this.subs.push(
+      this.fetchedAtlases$.subscribe(val => this.fetchedAtlases = val)
+    )
+  }
+
+  ngOnDestroy(): void {
+    this.subs.pop().unsubscribe()
+  }
+
+  handleChangeAtlas({ value }) {
+    const found = this.fetchedAtlases.find(atlas => atlas["@id"] === value)
+    if (found) {
+      this.store$.dispatch(
+        atlasSelection.actions.selectAtlas({
+          atlas: found
+        })
+      )
+    } else {
+      this.store$.dispatch(
+        generalActions.generalActionError({
+          message: `Atlas with id ${value} not found.`
+        })
+      )
+    }
+  }
+}
diff --git a/src/atlasComponents/sapiViews/core/atlas/dropdownAtlasSelector/dropdownAtlasSelector.stories.ts b/src/atlasComponents/sapiViews/core/atlas/dropdownAtlasSelector/dropdownAtlasSelector.stories.ts
new file mode 100644
index 0000000000000000000000000000000000000000..027fa024eb8bf497fa6600558b59098bab837d54
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/atlas/dropdownAtlasSelector/dropdownAtlasSelector.stories.ts
@@ -0,0 +1,49 @@
+import { CommonModule } from "@angular/common"
+import { HttpClientModule } from "@angular/common/http"
+import { Meta, moduleMetadata, Story } from "@storybook/angular"
+import { SAPI } from "src/atlasComponents/sapi"
+import { provideDarkTheme } from "src/atlasComponents/sapi/stories.base"
+import { SapiViewsCoreAtlasAtlasDropdownSelector } from "./dropdownAtlasSelector.component"
+import { SapiViewsCoreAtlasModule } from "../module"
+import { atlasSelection } from "src/state"
+import { StoreModule } from "@ngrx/store"
+
+export default {
+  component: SapiViewsCoreAtlasAtlasDropdownSelector,
+  decorators: [
+    moduleMetadata({
+      imports: [
+        CommonModule,
+        HttpClientModule,
+        SapiViewsCoreAtlasModule,
+
+        StoreModule.forRoot({
+          [atlasSelection.nameSpace]: atlasSelection.reducer
+        }),
+      ],
+      providers: [
+        SAPI,
+        ...provideDarkTheme,
+      ],
+      declarations: []
+    })
+  ],
+} as Meta
+
+const Template: Story<SapiViewsCoreAtlasAtlasDropdownSelector> = (args: SapiViewsCoreAtlasAtlasDropdownSelector, { parameters }) => {
+  /**
+   * TODO can't seem to hook in handlechangeAtlas action
+   * always results in maximum call stack reached
+   * perhaps related: https://github.com/storybookjs/storybook/issues/13238
+   */
+  return ({
+    props: {
+    },
+  })
+}
+
+
+export const Default = Template.bind({})
+Default.args = {
+
+}
\ No newline at end of file
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/ieeg/ieegCmp/ieeg.style.css b/src/atlasComponents/sapiViews/core/atlas/dropdownAtlasSelector/dropdownAtlasSelector.style.css
similarity index 100%
rename from src/atlasComponents/regionalFeatures/bsFeatures/ieeg/ieegCmp/ieeg.style.css
rename to src/atlasComponents/sapiViews/core/atlas/dropdownAtlasSelector/dropdownAtlasSelector.style.css
diff --git a/src/atlasComponents/uiSelectors/atlasDropdown/atlasDropdown.template.html b/src/atlasComponents/sapiViews/core/atlas/dropdownAtlasSelector/dropdownAtlasSelector.template.html
similarity index 87%
rename from src/atlasComponents/uiSelectors/atlasDropdown/atlasDropdown.template.html
rename to src/atlasComponents/sapiViews/core/atlas/dropdownAtlasSelector/dropdownAtlasSelector.template.html
index 4b5ddc4b0309e62ff937c8e6e5ffefeadfc55a6a..f3922fea6c44412fc83137f6c3ed71db7efd26e7 100644
--- a/src/atlasComponents/uiSelectors/atlasDropdown/atlasDropdown.template.html
+++ b/src/atlasComponents/sapiViews/core/atlas/dropdownAtlasSelector/dropdownAtlasSelector.template.html
@@ -4,7 +4,7 @@
   </mat-label>
   <mat-select
     [aria-label]="SELECT_ATLAS_ARIA_LABEL"
-    [value]="selectedAtlas$ | async | getProperty"
+    [value]="(selectedAtlas$ | async)?.['@id']"
     (selectionChange)="handleChangeAtlas($event)">
     <mat-option
       *ngFor="let atlas of (fetchedAtlases$ | async)"
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegSummary/kgRegSummary.style.css b/src/atlasComponents/sapiViews/core/atlas/index.ts
similarity index 100%
rename from src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegSummary/kgRegSummary.style.css
rename to src/atlasComponents/sapiViews/core/atlas/index.ts
diff --git a/src/atlasComponents/sapiViews/core/atlas/module.ts b/src/atlasComponents/sapiViews/core/atlas/module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..691d41303c80c52229423586c9243d27fc419479
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/atlas/module.ts
@@ -0,0 +1,35 @@
+import { CommonModule } from "@angular/common";
+import { NgModule } from "@angular/core";
+import { SpinnerModule } from "src/components/spinner";
+import { AngularMaterialModule } from "src/sharedModules";
+import { QuickTourModule } from "src/ui/quickTour";
+import { SapiViewsUtilModule } from "../../util";
+import { SapiViewsCoreParcellationModule } from "../parcellation";
+import { SapiViewsCoreSpaceModule } from "../space";
+import { SapiViewsCoreAtlasAtlasDropdownSelector } from "./dropdownAtlasSelector/dropdownAtlasSelector.component";
+import { SapiViewsCoreAtlasSplashScreen } from "./splashScreen/splashScreen.component";
+import { SapiViewsCoreAtlasAtlasTmplParcSelector } from "./tmplParcSelector/tmplParcSelector.component";
+
+@NgModule({
+  imports: [
+    CommonModule,
+    AngularMaterialModule,
+    SapiViewsCoreSpaceModule,
+    SapiViewsCoreParcellationModule,
+    QuickTourModule,
+    SpinnerModule,
+    SapiViewsUtilModule,
+  ],
+  declarations: [
+    SapiViewsCoreAtlasAtlasDropdownSelector,
+    SapiViewsCoreAtlasAtlasTmplParcSelector,
+    SapiViewsCoreAtlasSplashScreen,
+  ],
+  exports: [
+    SapiViewsCoreAtlasAtlasDropdownSelector,
+    SapiViewsCoreAtlasAtlasTmplParcSelector,
+    SapiViewsCoreAtlasSplashScreen,
+  ]
+})
+
+export class SapiViewsCoreAtlasModule{}
\ No newline at end of file
diff --git a/src/atlasComponents/sapiViews/core/atlas/splashScreen/splashScreen.component.ts b/src/atlasComponents/sapiViews/core/atlas/splashScreen/splashScreen.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..056e3660c4243ee41bff9e5090354bd6e6fb2deb
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/atlas/splashScreen/splashScreen.component.ts
@@ -0,0 +1,38 @@
+import { ChangeDetectionStrategy, Component } from "@angular/core";
+import { Store } from "@ngrx/store";
+import { tap } from 'rxjs/operators'
+import { SAPI } from "src/atlasComponents/sapi/sapi.service";
+import { SapiAtlasModel } from "src/atlasComponents/sapi/type";
+import { atlasSelection } from "src/state"
+
+@Component({
+  selector : 'ui-splashscreen',
+  templateUrl : './splashScreen.template.html',
+  styleUrls : [
+    `./splashScreen.style.css`,
+  ],
+  changeDetection: ChangeDetectionStrategy.OnPush,
+})
+
+export class SapiViewsCoreAtlasSplashScreen {
+
+  public finishedLoading: boolean = false
+
+  public atlases$ = this.sapiSvc.atlases$.pipe(
+    tap(() => this.finishedLoading = true)
+  )
+
+  constructor(
+    private store: Store<any>,
+    private sapiSvc: SAPI,
+  ) {
+  }
+
+  public selectAtlas(atlas: SapiAtlasModel){
+    this.store.dispatch(
+      atlasSelection.actions.selectAtlas({
+        atlas
+      })
+    )
+  }
+}
diff --git a/src/atlasComponents/splashScreen/splashScreen/splashScreen.style.css b/src/atlasComponents/sapiViews/core/atlas/splashScreen/splashScreen.style.css
similarity index 100%
rename from src/atlasComponents/splashScreen/splashScreen/splashScreen.style.css
rename to src/atlasComponents/sapiViews/core/atlas/splashScreen/splashScreen.style.css
diff --git a/src/atlasComponents/splashScreen/splashScreen/splashScreen.template.html b/src/atlasComponents/sapiViews/core/atlas/splashScreen/splashScreen.template.html
similarity index 87%
rename from src/atlasComponents/splashScreen/splashScreen/splashScreen.template.html
rename to src/atlasComponents/sapiViews/core/atlas/splashScreen/splashScreen.template.html
index 6236bd010201f3b334dc28c58df491acdd3017d2..ab4954689175ea3d66d2333d659c952cb1a8604c 100644
--- a/src/atlasComponents/splashScreen/splashScreen/splashScreen.template.html
+++ b/src/atlasComponents/sapiViews/core/atlas/splashScreen/splashScreen.template.html
@@ -7,7 +7,7 @@
     <mat-card
       (click)="selectAtlas(atlas)"
       matRipple
-      *ngFor="let atlas of loadedAtlases$ | async | filterArray : filterNullFn"
+      *ngFor="let atlas of atlases$ | async"
       class="m-3 col-md-12 col-lg-12 pe-all">
       <mat-card-header>
         <mat-card-title class="text-nowrap font-stretch">
@@ -22,7 +22,7 @@
     </mat-card>
 
     <ng-template [ngIf]="!finishedLoading">
-      <div class="d-flex align-items-center p-4">
+      <div class="d-flex align-items-center sxplr-p-4">
         <h1 class="mat-h1">
           <spinner-cmp></spinner-cmp>
         </h1>
diff --git a/src/atlasComponents/sapiViews/core/atlas/tmplParcSelector/tmplParcSelector.component.ts b/src/atlasComponents/sapiViews/core/atlas/tmplParcSelector/tmplParcSelector.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..58a2151732937cd390d516ace5a2bfab2de87f7b
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/atlas/tmplParcSelector/tmplParcSelector.component.ts
@@ -0,0 +1,217 @@
+import { animate, state, style, transition, trigger } from "@angular/animations";
+import { ChangeDetectionStrategy, Component, ElementRef, HostBinding, QueryList, ViewChild, ViewChildren } from "@angular/core";
+import { select, Store } from "@ngrx/store";
+import { combineLatest, forkJoin, merge, Observable, Subject, Subscription } from "rxjs";
+import { distinctUntilChanged, map, mapTo, shareReplay, switchMap, take } from "rxjs/operators";
+import { SAPI } from "src/atlasComponents/sapi";
+import { atlasSelection } from "src/state";
+import { fromRootStore } from "src/state/atlasSelection";
+import { IQuickTourData } from "src/ui/quickTour";
+import { ARIA_LABELS, CONST, QUICKTOUR_DESC } from 'common/constants'
+import { MatMenuTrigger } from "@angular/material/menu";
+import { SapiParcellationModel, SapiSpaceModel } from "src/atlasComponents/sapi/type";
+import { InterSpaceCoordXformSvc } from "src/atlasComponents/sapi/core/space/interSpaceCoordXform.service"
+
+@Component({
+  selector: `sxplr-sapiviews-core-atlas-tmplparcselector`,
+  templateUrl: './tmplParcSelector.template.html',
+  styleUrls: [
+    `./tmplParcSelector.style.css`
+  ],
+  exportAs: 'sxplrSapiViewsCoreAtlasTmplparcselector',
+  animations: [
+    trigger('toggleAtlasLayerSelector', [
+      state('false', style({
+        transform: 'scale(0)',
+        opacity: 0,
+        transformOrigin: '0% 100%'
+      })),
+      state('true', style({
+        transform: 'scale(1)',
+        opacity: 1,
+        transformOrigin: '0% 100%'
+      })),
+      transition('false => true', [
+        animate('200ms cubic-bezier(0.35, 0, 0.25, 1)')
+      ]),
+      transition('true => false', [
+        animate('200ms cubic-bezier(0.35, 0, 0.25, 1)')
+      ])
+    ])
+  ],
+  changeDetection: ChangeDetectionStrategy.OnPush
+})
+
+export class SapiViewsCoreAtlasAtlasTmplParcSelector {
+
+  public ARIA_LABELS = ARIA_LABELS
+  public CONST = CONST
+
+  @ViewChildren(MatMenuTrigger)
+  matMenuTriggers: QueryList<MatMenuTrigger>
+
+  @ViewChild('selectorPanelTmpl', { read: ElementRef })
+  selectorPanelTemplateRef: ElementRef
+
+  private atp$ = this.store$.pipe(
+    fromRootStore.distinctATP()
+  )
+
+  public availableParcellations$ = this.store$.pipe(
+    fromRootStore.allAvailParcs(this.sapi),
+    shareReplay(1),
+  )
+
+  public availableTemplates$ = this.store$.pipe(
+    fromRootStore.allAvailSpaces(this.sapi),
+  )
+
+  public selectedTemplate$ = this.atp$.pipe(
+    map(({ template }) => template)
+  )
+
+  public selectedParcellation$ = this.atp$.pipe(
+    map(({ parcellation }) => parcellation)
+  )
+
+  public parcsAvailableInCurrentTmpl$: Observable<SapiParcellationModel[]> = combineLatest([
+    this.atp$,
+    this.availableParcellations$,
+  ]).pipe(
+    switchMap(([{ atlas, template }, parcs]) => 
+      forkJoin(
+        parcs.map(
+          parc => this.sapi.getParcellation(atlas["@id"], parc["@id"]).getVolumes().pipe(
+            map(
+              volumes => {
+                return {
+                  parcellation: parc,
+                  volumes
+                }
+              }
+            )
+          )
+        )
+      ).pipe(
+        map(arr => 
+          arr.filter(
+            item => item.volumes.find(vol => vol.data.space["@id"] === template["@id"])
+          ).map(
+            ({ parcellation }) => parcellation
+          )
+        )
+      )
+    )
+  )
+
+  private showOverlayIntentByTemplate$ = new Subject()
+  private showOverlayIntentByParcellation$ = new Subject()
+  public showLoadingOverlay$ = merge(
+    this.showOverlayIntentByTemplate$.pipe(
+      mapTo(true)
+    ),
+    this.selectedTemplate$.pipe(
+      mapTo(false)
+    ),
+    this.showOverlayIntentByParcellation$.pipe(
+      mapTo(true)
+    ),
+    this.selectedParcellation$.pipe(
+      mapTo(false)
+    )
+  ).pipe(
+    distinctUntilChanged(),
+  )
+
+  private subscriptions: Subscription[] = []
+
+  @HostBinding('attr.data-opened')
+  public selectorExpanded: boolean = false
+
+  public quickTourData: IQuickTourData = {
+    order: 4,
+    description: QUICKTOUR_DESC.LAYER_SELECTOR,
+  }
+
+
+  constructor(
+    private store$: Store,
+    private sapi: SAPI,
+    private interSpaceCoordXformSvc: InterSpaceCoordXformSvc,
+  ) {
+
+  }
+  ngOnDestroy() {
+    while (this.subscriptions.length) this.subscriptions.pop().unsubscribe()
+  }
+
+
+  toggleSelector() {
+    this.selectorExpanded = !this.selectorExpanded
+    /**
+     * on selector open, call transform end point
+     * this caches the result, and will not bottleneck when the user selects a new space
+     */
+    if (this.selectorExpanded) {
+      forkJoin({
+        availableTemplates: this.availableTemplates$.pipe(
+          take(1)
+        ),
+        selectedTemplate: this.selectedTemplate$.pipe(
+          take(1)
+        ),
+        navigation: this.store$.pipe(
+          select(atlasSelection.selectors.navigation),
+          take(1)
+        )
+      }).pipe(
+
+      ).subscribe(({ availableTemplates, selectedTemplate, navigation }) => {
+        for (const avail of availableTemplates) {
+          this.interSpaceCoordXformSvc.transform(
+            InterSpaceCoordXformSvc.TmplIdToValidSpaceName(selectedTemplate["@id"]),
+            InterSpaceCoordXformSvc.TmplIdToValidSpaceName(avail["@id"]),
+            navigation.position as [number, number, number]
+          ).subscribe()
+        }
+      })
+    }
+  }
+
+  closeSelector() {
+    this.selectorExpanded = false
+  }
+
+  openSelector() {
+    this.selectorExpanded = true
+  }
+
+  selectTemplate(tmpl: SapiSpaceModel) {
+    this.showOverlayIntentByTemplate$.next(true)
+
+    this.store$.dispatch(
+      atlasSelection.actions.selectTemplate({
+        template: tmpl
+      })
+    )
+  }
+
+  selectParcellation(parc: SapiParcellationModel) {
+    this.showOverlayIntentByParcellation$.next(true)
+
+    this.store$.dispatch(
+      atlasSelection.actions.selectParcellation({
+        parcellation: parc
+      })
+    )
+  }
+
+  collapseExpandedGroup() {
+    this.matMenuTriggers.forEach(t => t.menuOpen && t.closeMenu())
+  }
+
+
+  trackTmpl(t:SapiSpaceModel) {
+    return t['@id']
+  }
+}
\ No newline at end of file
diff --git a/src/atlasComponents/sapiViews/core/atlas/tmplParcSelector/tmplParcSelector.stories.ts b/src/atlasComponents/sapiViews/core/atlas/tmplParcSelector/tmplParcSelector.stories.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3f18f737ec57e70b77c688c95f7fdbdc84c9e95b
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/atlas/tmplParcSelector/tmplParcSelector.stories.ts
@@ -0,0 +1,138 @@
+import { CommonModule } from "@angular/common"
+import { HttpClientModule } from "@angular/common/http"
+import { provideMockStore } from "@ngrx/store/testing"
+import { action } from "@storybook/addon-actions"
+import { Meta, moduleMetadata, Story } from "@storybook/angular"
+import { SAPI } from "src/atlasComponents/sapi"
+import { InterSpaceCoordXformSvc } from "src/atlasComponents/sapi/core/space/interSpaceCoordXform.service"
+import { spaceId, provideDarkTheme, getHumanAtlas, getMni152, getJba29, getSpace, atlasId, getParc, parcId } from "src/atlasComponents/sapi/stories.base"
+import { AngularMaterialModule } from "src/sharedModules"
+import { atlasSelection } from "src/state"
+import { SapiViewsCoreAtlasModule } from "../module"
+import { SapiViewsCoreAtlasAtlasTmplParcSelector } from "./tmplParcSelector.component"
+
+const actionsData = {
+  selectTemplate: action('selectTemplate'),
+  selectParcellation: action('selectParcellation')
+}
+
+export default {
+  component: SapiViewsCoreAtlasAtlasTmplParcSelector,
+  decorators: [
+    moduleMetadata({
+      imports: [
+        CommonModule,
+        HttpClientModule,
+        SapiViewsCoreAtlasModule,
+        AngularMaterialModule,
+      ],
+      providers: [
+        SAPI,
+        InterSpaceCoordXformSvc,
+        ...provideDarkTheme,
+      ],
+      declarations: [
+      ]
+    })
+  ],
+} as Meta
+
+const Template: Story<SapiViewsCoreAtlasAtlasTmplParcSelector> = (args: SapiViewsCoreAtlasAtlasTmplParcSelector, { loaded }) => {
+  const { 
+    atlas,
+    template,
+    parcellation,
+  } = loaded
+
+  return ({
+    props: {
+      selectTemplate: actionsData.selectTemplate,
+      selectParcellation: actionsData.selectParcellation
+    },
+    moduleMetadata: {
+      providers: [
+        provideMockStore({
+          initialState: {
+            [atlasSelection.nameSpace]: {
+              ...atlasSelection.defaultState,
+              selectedAtlas: atlas,
+              selectedTemplate: template,
+              selectedParcellation: parcellation,
+            }
+          }
+        })
+      ]
+    }
+  })
+}
+Template.loaders = [
+
+]
+
+export const MNI152JBA29 = Template.bind({})
+MNI152JBA29.args = {
+  
+}
+MNI152JBA29.loaders = [
+  async () => {
+    const atlas = await getHumanAtlas()
+    const template = await getMni152()
+    const parcellation = await getJba29()
+    return {
+      atlas,
+      template,
+      parcellation,
+    }
+  }
+]
+
+export const BigBrainJBA29 = Template.bind({})
+BigBrainJBA29.args = {
+  
+}
+BigBrainJBA29.loaders = [
+  async () => {
+    const atlas = await getHumanAtlas()
+    const template = await getSpace(atlasId.human, spaceId.human.bigbrain)
+    const parcellation = await getJba29()
+    return {
+      atlas,
+      template,
+      parcellation,
+    }
+  }
+]
+
+export const BigBrainCorticalLayers = Template.bind({})
+BigBrainCorticalLayers.args = {
+  
+}
+BigBrainCorticalLayers.loaders = [
+  async () => {
+    const atlas = await getHumanAtlas()
+    const template = await getSpace(atlasId.human, spaceId.human.bigbrain)
+    const parcellation = await getParc(atlasId.human, parcId.human.corticalLayers)
+    return {
+      atlas,
+      template,
+      parcellation,
+    }
+  }
+]
+
+export const MNI152LongBundle = Template.bind({})
+MNI152LongBundle.args = {
+  
+}
+MNI152LongBundle.loaders = [
+  async () => {
+    const atlas = await getHumanAtlas()
+    const template = await getMni152()
+    const parcellation = await getParc(atlasId.human, parcId.human.longBundle)
+    return {
+      atlas,
+      template,
+      parcellation,
+    }
+  }
+]
\ No newline at end of file
diff --git a/src/atlasComponents/uiSelectors/atlasLayerSelector/atlasLayerSelector.style.css b/src/atlasComponents/sapiViews/core/atlas/tmplParcSelector/tmplParcSelector.style.css
similarity index 64%
rename from src/atlasComponents/uiSelectors/atlasLayerSelector/atlasLayerSelector.style.css
rename to src/atlasComponents/sapiViews/core/atlas/tmplParcSelector/tmplParcSelector.style.css
index 425a45a5e252f904956712930e5996740733d60f..49568298b447009881affb039d95cdd967cd675f 100644
--- a/src/atlasComponents/uiSelectors/atlasLayerSelector/atlasLayerSelector.style.css
+++ b/src/atlasComponents/sapiViews/core/atlas/tmplParcSelector/tmplParcSelector.style.css
@@ -1,15 +1,9 @@
 .selector-container
 {
-    overflow-y:scroll;
-    max-height: 80vh;
-    width: 21rem;
-    bottom: 4rem;
-    z-index: 5;
-}
-
-:host-context([darktheme="true"]) .loading-overlay
-{
-    background-color: rgba(10, 10, 10, 0.8);
+  overflow-y:scroll;
+  max-height: 80vh;
+  width: 21rem;
+  bottom: 4rem;
 }
 
 .loading-overlay
@@ -19,7 +13,7 @@
 
 .loading-overlay
 {
-    position: absolute;
+    position: fixed;
     width: 100%;
     height: 100%;
     top: 0;
@@ -38,3 +32,10 @@
     grid-column: 2;
     grid-row: 2;
 }
+
+/* necessary to align the tiles to the start of grid tile */
+sxplr-sapiviews-core-space-tile,
+sxplr-sapiviews-core-parcellation-tile
+{
+    height: 100%;
+}
\ No newline at end of file
diff --git a/src/atlasComponents/sapiViews/core/atlas/tmplParcSelector/tmplParcSelector.template.html b/src/atlasComponents/sapiViews/core/atlas/tmplParcSelector/tmplParcSelector.template.html
new file mode 100644
index 0000000000000000000000000000000000000000..db9ba08d8bf273f3726bda3fe2ae8d19b94f5b0a
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/atlas/tmplParcSelector/tmplParcSelector.template.html
@@ -0,0 +1,103 @@
+<!-- selector panel when expanded -->
+
+<mat-card class="selector-container m-2 position-absolute"
+  [ngClass]="{'pe-all': selectorExpanded}"
+  [@toggleAtlasLayerSelector]="selectorExpanded"
+  (@toggleAtlasLayerSelector.done)="atlasSelectorTour?.attachTo(selectorExpanded ? selectorPanelTemplateRef : null)"
+  #selectorPanelTmpl>
+
+  <mat-card-content>
+
+    <!-- templates -->
+    <mat-card-subtitle>
+      {{ CONST.ATLAS_SELECTOR_LABEL_SPACES }}
+    </mat-card-subtitle>
+
+    <!-- template grid and tiles -->
+    <mat-grid-list cols="3"
+      rowHeight="2:3"
+      gutterSize="16">
+
+      <mat-grid-tile *ngFor="let template of availableTemplates$ | async; trackBy: trackTmpl"
+        [attr.aria-checked]="(selectedTemplate$ | async)?.['@id']  === template['@id']">
+        
+        <sxplr-sapiviews-core-space-tile
+          [ngClass]="{
+            'sxplr-extra-muted': !(template | spaceSupportedInCurrentParcellation | async)
+          }"
+          [sxplr-sapiviews-core-space-tile-space]="template"
+          [sxplr-sapiviews-core-space-tile-selected]="(selectedTemplate$ | async)?.['@id'] === template['@id']"
+          (click)="selectTemplate(template)">
+        </sxplr-sapiviews-core-space-tile>
+      </mat-grid-tile>
+    </mat-grid-list>
+
+    <mat-divider></mat-divider>
+
+    <!-- parcellations -->
+    <mat-card-subtitle class="mt-2">
+      {{ CONST.ATLAS_SELECTOR_LABEL_PARC_MAPS }}
+    </mat-card-subtitle>
+
+    <mat-grid-list cols="3"
+        rowHeight="2:3"
+        gutterSize="16">
+
+        <!-- using single parc template, since it's reused in non individual parcellation and tmpl for grp parcellation -->
+        <ng-template #singleParcTmpl let-parc>
+          <sxplr-sapiviews-core-parcellation-tile
+            [ngClass]="{
+              'sxplr-extra-muted': !(parc | parcellationSupportedInCurrentSpace | async)
+            }"
+            [sxplr-sapiviews-core-parcellation-tile-selected]="(selectedParcellation$ | async)?.['@id'] === parc['@id']"
+            [sxplr-sapiviews-core-parcellation-tile-parcellation]="parc"
+            (sxplr-sapiviews-core-parcellation-tile-onclick-parc)="selectParcellation($event)">
+
+          </sxplr-sapiviews-core-parcellation-tile>
+        </ng-template>
+
+        <mat-grid-tile *ngFor="let parc of availableParcellations$ | async | filterUnsupportedParc | filterGroupedParcs">
+          <ng-template
+            [ngTemplateOutlet]="singleParcTmpl"
+            [ngTemplateOutletContext]="{ $implicit: parc }">
+          </ng-template>
+        </mat-grid-tile>
+
+        <mat-grid-tile *ngFor="let group of availableParcellations$ | async | filterUnsupportedParc | filterGroupedParcs : true | filterUnsupportedParc">
+          <sxplr-sapiviews-core-parcellation-tile
+            [sxplr-sapiviews-core-parcellation-tile-groupmenu-parc-tmpl]="singleParcTmpl"
+            [sxplr-sapiviews-core-parcellation-tile-parcellation]="group"
+            (sxplr-sapiviews-core-parcellation-tile-onclick-parc)="selectParcellation($event)">
+
+          </sxplr-sapiviews-core-parcellation-tile>
+        </mat-grid-tile>
+    </mat-grid-list>
+
+  </mat-card-content>
+
+  <div [ngClass]="{
+    'sxplr-d-none': !(showLoadingOverlay$ | async)
+  }"
+    class="loading-overlay">
+    <spinner-cmp class="spinner"></spinner-cmp>
+  </div>
+  
+
+</mat-card>
+
+<!-- place holder when not expanded -->
+<div class="position-relative m-2 cursor-pointer scale-up-bl pe-all sxplr-d-inline-block"
+  quick-tour
+  [quick-tour-description]="quickTourData.description"
+  [quick-tour-order]="quickTourData.order"
+  #atlasSelectorTour="quickTour">
+  <!-- TODO check when do we disable atlas selector -->
+  <button color="primary"
+    *ngIf="true"
+    matTooltip="Select layer"
+    mat-mini-fab
+    [attr.aria-label]="ARIA_LABELS.TOGGLE_ATLAS_LAYER_SELECTOR"
+    (click)="toggleSelector()">
+    <i class="fas fa-layer-group"></i>
+  </button>
+</div>
diff --git a/src/atlasComponents/sapiViews/core/datasets/dataset/dataset.component.ts b/src/atlasComponents/sapiViews/core/datasets/dataset/dataset.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..88614d6194880e18d3e802de65c1660f75557b13
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/datasets/dataset/dataset.component.ts
@@ -0,0 +1,28 @@
+import { Component, Input, OnChanges, SimpleChanges } from "@angular/core";
+import { SapiDatasetModel } from "src/atlasComponents/sapi";
+import { CONST } from "common/constants"
+
+const RESTRICTED_ACCESS_ID = "https://nexus.humanbrainproject.org/v0/data/minds/core/embargostatus/v1.0.0/3054f80d-96a8-4dce-9b92-55c68a8b5efd"
+
+@Component({
+  selector: `sxplr-sapiviews-core-datasets-dataset`,
+  templateUrl: './dataset.template.html',
+  styleUrls: [
+    `./dataset.style.css`
+  ]
+})
+
+export class DatasetView implements OnChanges{
+  @Input('sxplr-sapiviews-core-datasets-dataset-input')
+  dataset: SapiDatasetModel
+
+  public isRestricted = false
+  public CONST = CONST
+
+  ngOnChanges(changes: SimpleChanges): void {
+    const { dataset } = changes
+    if (dataset) {
+      this.isRestricted = (dataset.currentValue as SapiDatasetModel)?.metadata?.accessibility?.["@id"] === RESTRICTED_ACCESS_ID
+    }
+  }
+}
diff --git a/src/atlasComponents/sapiViews/core/datasets/dataset/dataset.stories.ts b/src/atlasComponents/sapiViews/core/datasets/dataset/dataset.stories.ts
new file mode 100644
index 0000000000000000000000000000000000000000..acec7a744768e383bdb511d15b4a8d537b9e0633
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/datasets/dataset/dataset.stories.ts
@@ -0,0 +1,57 @@
+import { CommonModule } from "@angular/common"
+import { HttpClientModule } from "@angular/common/http"
+import { Meta, moduleMetadata, Story } from "@storybook/angular"
+import { SAPI } from "src/atlasComponents/sapi"
+import { getHoc1RightFeatureDetail, getHoc1RightFeatures, provideDarkTheme } from "src/atlasComponents/sapi/stories.base"
+import { SapiViewsCoreDatasetModule } from ".."
+import { DatasetView } from "./dataset.component"
+
+export default {
+  component: DatasetView,
+  decorators: [
+    moduleMetadata({
+      imports: [
+        CommonModule,
+        HttpClientModule,
+        SapiViewsCoreDatasetModule,
+      ],
+      providers: [
+        SAPI,
+        ...provideDarkTheme,
+      ],
+      declarations: []
+    })
+  ],
+} as Meta
+
+const Template: Story<DatasetView> = (args: DatasetView, { loaded }) => {
+  const { feature } = loaded
+  return ({
+    props: {
+      ...args,
+      dataset: feature,
+    },
+  })
+}
+
+const loadFeat = async () => {
+  const features = await getHoc1RightFeatures()
+  const receptorfeat = features.find(f => f['@type'] === "siibra/core/dataset")
+  const feature = await getHoc1RightFeatureDetail(receptorfeat["@id"])
+  return {
+    feature
+  }
+}
+
+export const ReceptorDataset = Template.bind({})
+ReceptorDataset.args = {
+
+}
+ReceptorDataset.loaders = [
+  async () => {
+    const { feature } = await loadFeat()
+    return {
+      feature
+    }
+  }
+]
\ No newline at end of file
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/receptor/profile/profile.style.css b/src/atlasComponents/sapiViews/core/datasets/dataset/dataset.style.css
similarity index 100%
rename from src/atlasComponents/regionalFeatures/bsFeatures/receptor/profile/profile.style.css
rename to src/atlasComponents/sapiViews/core/datasets/dataset/dataset.style.css
diff --git a/src/atlasComponents/sapiViews/core/datasets/dataset/dataset.template.html b/src/atlasComponents/sapiViews/core/datasets/dataset/dataset.template.html
new file mode 100644
index 0000000000000000000000000000000000000000..f9c37f9907d70750b2de1db334ac2c15949db7ca
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/datasets/dataset/dataset.template.html
@@ -0,0 +1,47 @@
+<ng-template #headerTmpl>
+  <ng-content select="[header]"></ng-content>
+</ng-template>
+
+<mat-card *ngIf="!dataset">
+
+  <ng-template [ngTemplateOutlet]="headerTmpl"></ng-template>
+  <span>
+    Dataset not specified.
+  </span>
+</mat-card>
+
+<mat-card *ngIf="dataset"
+  class="mat-elevation-z4 sxplr-z-4">
+  <mat-card-title>
+    <ng-template [ngTemplateOutlet]="headerTmpl"></ng-template>
+    <span>
+      {{ dataset.metadata.fullName }}
+    </span>
+  </mat-card-title>
+
+  <mat-card-subtitle class="sxplr-d-inline-flex sxplr-align-items-stretch">
+    <mat-icon class="sxplr-m-a" fontSet="fas" fontIcon="fa-database"></mat-icon>
+    <span class="sxplr-m-a">
+      EBRAINS dataset
+    </span>
+
+    <button *ngIf="isRestricted"
+      [matTooltip]="CONST.GDPR_TEXT"
+      mat-icon-button color="warn">
+      <i class="fas fa-exclamation-triangle"></i>
+    </button>
+
+    <mat-divider class="sxplr-pl-1" [vertical]="true"></mat-divider>
+
+    <a mat-icon-button *ngFor="let url of dataset.urls" [href]="url.doi | parseDoi" target="_blank">
+      <i class="fas fa-external-link-alt"></i>
+    </a>
+  </mat-card-subtitle>
+</mat-card>
+
+<mat-card *ngIf="dataset" class="sxplr-z-0">
+  <mat-card-content>
+    <markdown-dom class="sxplr-muted" [markdown]="dataset?.metadata?.description">
+    </markdown-dom>
+  </mat-card-content>
+</mat-card>
diff --git a/src/atlasComponents/sapiViews/core/datasets/index.ts b/src/atlasComponents/sapiViews/core/datasets/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d265981874bb6564d2dac8f9738d7069436154e9
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/datasets/index.ts
@@ -0,0 +1,7 @@
+export {
+  SapiViewsCoreDatasetModule
+} from "./module"
+
+export {
+  DatasetView
+} from "./dataset/dataset.component"
\ No newline at end of file
diff --git a/src/atlasComponents/sapiViews/core/datasets/module.ts b/src/atlasComponents/sapiViews/core/datasets/module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b295da6bc25f1a9fdc98031a42810ba907013320
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/datasets/module.ts
@@ -0,0 +1,23 @@
+import { CommonModule } from "@angular/common";
+import { NgModule } from "@angular/core";
+import { MarkdownModule } from "src/components/markdown";
+import { AngularMaterialModule } from "src/sharedModules";
+import { SapiViewsUtilModule } from "../../util/module";
+import { DatasetView } from "./dataset/dataset.component";
+
+@NgModule({
+  imports: [
+    CommonModule,
+    AngularMaterialModule,
+    MarkdownModule,
+    SapiViewsUtilModule
+  ],
+  declarations: [
+    DatasetView,
+  ],
+  exports: [
+    DatasetView
+  ]
+})
+
+export class SapiViewsCoreDatasetModule{}
\ No newline at end of file
diff --git a/src/atlasComponents/sapiViews/core/index.ts b/src/atlasComponents/sapiViews/core/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..cb3d0ffce18ea93d534e44866392f0fc819b0ae8
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/index.ts
@@ -0,0 +1,7 @@
+export {
+  SapiViewsCoreModule
+} from "./module"
+
+export {
+  SapiViewsCoreSpaceBoundingBox
+} from "./space"
\ No newline at end of file
diff --git a/src/atlasComponents/sapiViews/core/module.ts b/src/atlasComponents/sapiViews/core/module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c30a1d83ebe1a72a911acbfdc796729e683431d8
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/module.ts
@@ -0,0 +1,30 @@
+import { CommonModule } from "@angular/common";
+import { NgModule } from "@angular/core";
+import { SapiViewsCoreAtlasModule } from "./atlas/module";
+import { SapiViewsCoreDatasetModule } from "./datasets";
+import { SapiViewsCoreParcellationModule } from "./parcellation/module";
+import { SapiViewsCoreRegionModule } from "./region";
+import { SapiViewsCoreRichModule } from "./rich/module";
+import { SapiViewsCoreSpaceModule } from "./space";
+
+@NgModule({
+  imports: [
+    CommonModule,
+    SapiViewsCoreDatasetModule,
+    SapiViewsCoreRegionModule,
+    SapiViewsCoreAtlasModule,
+    SapiViewsCoreSpaceModule,
+    SapiViewsCoreParcellationModule,
+    SapiViewsCoreRichModule,
+  ],
+  exports: [
+    SapiViewsCoreDatasetModule,
+    SapiViewsCoreRegionModule,
+    SapiViewsCoreAtlasModule,
+    SapiViewsCoreSpaceModule,
+    SapiViewsCoreParcellationModule,
+    SapiViewsCoreRichModule,
+  ]
+})
+
+export class SapiViewsCoreModule{}
\ No newline at end of file
diff --git a/src/atlasComponents/sapiViews/core/parcellation/chip/parcellation.chip.component.ts b/src/atlasComponents/sapiViews/core/parcellation/chip/parcellation.chip.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..13358ff46b1a3eadc2b2e5ddb9fe417675ad94e3
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/parcellation/chip/parcellation.chip.component.ts
@@ -0,0 +1,26 @@
+import { Component, Input, Output, EventEmitter } from "@angular/core";
+import { SapiParcellationModel } from "src/atlasComponents/sapi/type";
+
+@Component({
+  selector: `sxplr-sapiviews-core-parcellation-chip`,
+  templateUrl: './parcellation.chip.template.html',
+  styleUrls: [
+    `./parcellation.chip.style.css`
+  ],
+})
+
+export class SapiViewsCoreParcellationParcellationChip {
+
+  @Input('sxplr-sapiviews-core-parcellation-chip-parcellation')
+  parcellation: SapiParcellationModel
+
+  @Input('sxplr-sapiviews-core-parcellation-chip-color')
+  color: 'default' | 'primary' | 'accent' | 'warn' = "default"
+
+  @Output('sxplr-sapiviews-core-parcellation-chip-onclick')
+  onClick = new EventEmitter<MouseEvent>()
+
+  click(event: MouseEvent) {
+    this.onClick.emit(event)
+  }
+}
diff --git a/src/atlasComponents/sapiViews/core/parcellation/chip/parcellation.chip.stories.ts b/src/atlasComponents/sapiViews/core/parcellation/chip/parcellation.chip.stories.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5e3afe419fc640d609b2f6acbfee703a7e5ae69c
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/parcellation/chip/parcellation.chip.stories.ts
@@ -0,0 +1,120 @@
+import { CommonModule } from "@angular/common"
+import { HttpClientModule } from "@angular/common/http"
+import { provideMockStore } from "@ngrx/store/testing"
+import { Meta, moduleMetadata, Story } from "@storybook/angular"
+import { SAPI, SapiParcellationModel } from "src/atlasComponents/sapi"
+import { atlasId, getAtlas, provideDarkTheme, getParc } from "src/atlasComponents/sapi/stories.base"
+import { AngularMaterialModule } from "src/sharedModules"
+import { SapiViewsCoreParcellationModule } from "../module"
+import { SapiViewsCoreParcellationParcellationChip } from "./parcellation.chip.component"
+
+
+export default {
+  component: SapiViewsCoreParcellationParcellationChip,
+  decorators: [
+    moduleMetadata({
+      imports: [
+        CommonModule,
+        HttpClientModule,
+        SapiViewsCoreParcellationModule,
+        AngularMaterialModule,
+      ],
+      providers: [
+        provideMockStore(),
+        SAPI,
+        ...provideDarkTheme,
+      ],
+      declarations: []
+    })
+  ],
+} as Meta
+
+const Template: Story<SapiViewsCoreParcellationParcellationChip> = (args: SapiViewsCoreParcellationParcellationChip, { loaded, parameters }) => {
+  const { 
+    parcellation
+  } = loaded
+  const {
+    contentProjection
+  } = parameters
+
+  return ({
+    props: {
+      ...args,
+      parcellation
+    },
+    template: `
+    <sxplr-sapiviews-core-parcellation-chip>
+      ${contentProjection || ''}
+    </sxplr-sapiviews-core-parcellation-chip>
+    `
+  })
+}
+Template.loaders = []
+
+const asyncLoader = async (_atlasId: string) => {
+  const parcs: SapiParcellationModel[] = []
+  const atlasDetail = await getAtlas(_atlasId)
+  
+  for (const parc of atlasDetail.parcellations) {
+    const parcDetail = await getParc(atlasDetail['@id'], parc['@id'])
+    parcs.push(parcDetail)
+  }
+  return {
+    parcs
+  }
+}
+
+const getContentProjection = ({ prefix = null, suffix = null }) => {
+  let returnVal = ``
+  if (prefix) {
+    returnVal += `<div prefix>${prefix}</div>`
+  }
+  if (suffix) {
+    returnVal += `<div suffix>${suffix}</div>`
+  }
+  return returnVal
+}
+
+export const Default = Template.bind({})
+Default.loaders = [
+  async () => {
+    const {
+      parcs
+    } = await asyncLoader(atlasId.human)
+    return {
+      parcellation: parcs[0]
+    }
+  }
+]
+
+export const Prefix = Template.bind({})
+Prefix.loaders = [
+  ...Default.loaders
+]
+Prefix.parameters = {
+  contentProjection: getContentProjection({
+    prefix: `PREFIX`,
+  })
+}
+
+export const Suffix = Template.bind({})
+Suffix.loaders = [
+  ...Default.loaders
+]
+Suffix.parameters = {
+  contentProjection: getContentProjection({
+    suffix: `SUFFIX`,
+  })
+}
+
+
+export const PrefixSuffix = Template.bind({})
+PrefixSuffix.loaders = [
+  ...Default.loaders
+]
+PrefixSuffix.parameters = {
+  contentProjection: getContentProjection({
+    prefix: `PREFIX`,
+    suffix: `SUFFIX`,
+  })
+}
diff --git a/src/atlasComponents/regionalFeatures/singleFeatures/iEEGRecordings/iEEGRecordings/iEEGRecordings.style.css b/src/atlasComponents/sapiViews/core/parcellation/chip/parcellation.chip.style.css
similarity index 100%
rename from src/atlasComponents/regionalFeatures/singleFeatures/iEEGRecordings/iEEGRecordings/iEEGRecordings.style.css
rename to src/atlasComponents/sapiViews/core/parcellation/chip/parcellation.chip.style.css
diff --git a/src/atlasComponents/sapiViews/core/parcellation/chip/parcellation.chip.template.html b/src/atlasComponents/sapiViews/core/parcellation/chip/parcellation.chip.template.html
new file mode 100644
index 0000000000000000000000000000000000000000..417575bf67914c3886697a435134eb17852b1676
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/parcellation/chip/parcellation.chip.template.html
@@ -0,0 +1,16 @@
+<mat-chip-list *ngIf="parcellation">
+  <mat-chip [selected]="color !== 'default'"
+    (click)="click($event)"
+    [color]="color">
+
+    <ng-content select="[prefix]">
+    </ng-content>
+
+    <span class="mat-body sxplr-white-space-nowrap">
+      {{ parcellation.name }}
+    </span>
+
+    <ng-content select="[suffix]">
+    </ng-content>
+  </mat-chip>
+</mat-chip-list>
\ No newline at end of file
diff --git a/src/atlasComponents/sapiViews/core/parcellation/filterGroupedParcellations.pipe.ts b/src/atlasComponents/sapiViews/core/parcellation/filterGroupedParcellations.pipe.ts
new file mode 100644
index 0000000000000000000000000000000000000000..62910eab45e5529ee22f137afa92e4978d42cfe4
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/parcellation/filterGroupedParcellations.pipe.ts
@@ -0,0 +1,31 @@
+import { Pipe, PipeTransform } from "@angular/core";
+import { SapiParcellationModel } from "src/atlasComponents/sapi";
+import { GroupedParcellation } from "./groupedParcellation";
+
+@Pipe({
+  name: `filterGroupedParcs`,
+  pure: true
+})
+
+export class FilterGroupedParcellationPipe implements PipeTransform{
+  public transform(parcs: SapiParcellationModel[], getGroupsFlag: boolean=false): (SapiParcellationModel|GroupedParcellation)[] {
+    if (!getGroupsFlag) {
+      return parcs.filter(p => !p.modality)
+    }
+    const map: Record<string, SapiParcellationModel[]> = {}
+    for (const parc of parcs.filter(p => !!p.modality)) {
+      if (!map[parc.modality]) {
+        map[parc.modality] = []
+      }
+      map[parc.modality].push(parc)
+    }
+
+    const returnGrps: GroupedParcellation[] = []
+    for (const key in map) {
+      returnGrps.push(
+        new GroupedParcellation(key, map[key])
+      )
+    }
+    return returnGrps
+  }
+}
diff --git a/src/atlasComponents/sapiViews/core/parcellation/filterUnsupportedParc.pipe.ts b/src/atlasComponents/sapiViews/core/parcellation/filterUnsupportedParc.pipe.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3a9d1a30ec5a9bc9ed281cca3e44051cd7b5b163
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/parcellation/filterUnsupportedParc.pipe.ts
@@ -0,0 +1,36 @@
+import { Pipe, PipeTransform } from "@angular/core";
+import { SapiParcellationModel } from "src/atlasComponents/sapi/type";
+import { GroupedParcellation } from "./groupedParcellation";
+
+type Filterables = SapiParcellationModel | GroupedParcellation
+
+const unsupportedIds = [
+  "https://doi.org/10.1016/j.jneumeth.2020.108983/mni152",
+  "https://identifiers.org/neurovault.image:23262"
+]
+
+const hideGroup = [
+  "cytoarchitecture"
+]
+
+@Pipe({
+  name: `filterUnsupportedParc`,
+  pure: true
+})
+
+export class FilterUnsupportedParcPipe implements PipeTransform{
+  public transform<T extends Filterables>(parcs: T[]): T[] {
+    return (parcs || []).filter(p => {
+      if (p instanceof GroupedParcellation) {
+        return hideGroup.indexOf(p.name) < 0
+      }
+      if (unsupportedIds.includes(p["@id"])) {
+        return false
+      }
+      if (p.version) {
+        return !p.version.deprecated
+      }
+      return true
+    })
+  }
+}
diff --git a/src/atlasComponents/sapiViews/core/parcellation/groupedParcellation.ts b/src/atlasComponents/sapiViews/core/parcellation/groupedParcellation.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c66d6e9818a3884cb0c48fc58de56bee4373e113
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/parcellation/groupedParcellation.ts
@@ -0,0 +1,10 @@
+import { SapiParcellationModel } from "src/atlasComponents/sapi"
+
+export class GroupedParcellation{
+  name: string
+  parcellations: SapiParcellationModel[]
+  constructor(name: string, parcellations: SapiParcellationModel[]){
+    this.name = name
+    this.parcellations = parcellations
+  }
+}
diff --git a/src/atlasComponents/sapiViews/core/parcellation/index.ts b/src/atlasComponents/sapiViews/core/parcellation/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..801b1f3e381a6a806c94cd6458e3187a620755c1
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/parcellation/index.ts
@@ -0,0 +1,11 @@
+export {
+  SapiViewsCoreParcellationModule
+} from "./module"
+
+export {
+  FilterGroupedParcellationPipe
+} from "./filterGroupedParcellations.pipe"
+
+export {
+  FilterUnsupportedParcPipe
+} from "./filterUnsupportedParc.pipe"
\ No newline at end of file
diff --git a/src/atlasComponents/sapiViews/core/parcellation/module.ts b/src/atlasComponents/sapiViews/core/parcellation/module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..cb7581d2d88ab9f43518261e87a515b8ceb99456
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/parcellation/module.ts
@@ -0,0 +1,62 @@
+import { CommonModule } from "@angular/common";
+import { APP_INITIALIZER, NgModule } from "@angular/core";
+import { Store } from "@ngrx/store";
+import { ComponentsModule } from "src/components";
+import { AngularMaterialModule } from "src/sharedModules";
+import { atlasAppearance } from "src/state";
+import { UtilModule } from "src/util";
+import { SapiViewsUtilModule } from "../../util";
+import { SapiViewsCoreParcellationParcellationChip } from "./chip/parcellation.chip.component";
+import { FilterGroupedParcellationPipe } from "./filterGroupedParcellations.pipe";
+import { FilterUnsupportedParcPipe } from "./filterUnsupportedParc.pipe";
+import { ParcellationIsBaseLayer } from "./parcellationIsBaseLayer.pipe";
+import { ParcellationVisibilityService } from "./parcellationVis.service";
+import { PreviewParcellationUrlPipe } from "./previewParcellationUrl.pipe";
+import { SapiViewsCoreParcellationParcellationSmartChip } from "./smartChip/parcellation.smartChip.component";
+import { SapiViewsCoreParcellationParcellationTile } from "./tile/parcellation.tile.component";
+
+@NgModule({
+  imports: [
+    CommonModule,
+    ComponentsModule,
+    AngularMaterialModule,
+    UtilModule,
+    SapiViewsUtilModule,
+  ],
+  declarations: [
+    SapiViewsCoreParcellationParcellationTile,
+    SapiViewsCoreParcellationParcellationChip,
+    SapiViewsCoreParcellationParcellationSmartChip,
+    PreviewParcellationUrlPipe,
+    FilterGroupedParcellationPipe,
+    FilterUnsupportedParcPipe,
+    ParcellationIsBaseLayer,
+  ],
+  exports: [
+    SapiViewsCoreParcellationParcellationTile,
+    SapiViewsCoreParcellationParcellationChip,
+    SapiViewsCoreParcellationParcellationSmartChip,
+    FilterGroupedParcellationPipe,
+    FilterUnsupportedParcPipe,
+  ],
+  providers: [
+    ParcellationVisibilityService,
+    {
+      provide: APP_INITIALIZER,
+      useFactory: (store: Store, svc: ParcellationVisibilityService) => {
+        svc.visibility$.subscribe(val => {
+          store.dispatch(
+            atlasAppearance.actions.setShowDelineation({
+              flag: val
+            })
+          )
+        })
+        return () => Promise.resolve()
+      },
+      multi: true,
+      deps: [ Store, ParcellationVisibilityService ]
+    }
+  ]
+})
+
+export class SapiViewsCoreParcellationModule{}
\ No newline at end of file
diff --git a/src/atlasComponents/sapiViews/core/parcellation/parcellationIsBaseLayer.pipe.ts b/src/atlasComponents/sapiViews/core/parcellation/parcellationIsBaseLayer.pipe.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2c3c09a9d9b7307a7a1f8b18c193565137e9bd2f
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/parcellation/parcellationIsBaseLayer.pipe.ts
@@ -0,0 +1,44 @@
+import { Pipe, PipeTransform } from "@angular/core";
+import { SapiParcellationModel } from "src/atlasComponents/sapi/type";
+
+const baseLayerIds = [
+  /**
+   * julich brain
+   */
+  "minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579-290",
+  "minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579-25",
+  "minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579",
+
+  /**
+   * allen mouse
+   */
+  "minds/core/parcellationatlas/v1.0.0/05655b58-3b6f-49db-b285-64b5a0276f83",
+  "minds/core/parcellationatlas/v1.0.0/39a1384b-8413-4d27-af8d-22432225401f",
+
+  /**
+   * waxholm
+   */
+  "minds/core/parcellationatlas/v1.0.0/11017b35-7056-4593-baad-3934d211daba",
+  "minds/core/parcellationatlas/v1.0.0/2449a7f0-6dd0-4b5a-8f1e-aec0db03679d",
+  "minds/core/parcellationatlas/v1.0.0/ebb923ba-b4d5-4b82-8088-fa9215c2e1fe",
+  "minds/core/parcellationatlas/v1.0.0/ebb923ba-b4d5-4b82-8088-fa9215c2e1fe-v4",
+
+  /**
+   * monkey
+   */
+  "minds/core/parcellationatlas/v1.0.0/mebrains-tmp-id",
+]
+
+@Pipe({
+  name: 'parcellationIsBaseLayer',
+  pure: true
+})
+
+export class ParcellationIsBaseLayer implements PipeTransform{
+  public transform(parc: SapiParcellationModel): boolean {
+    /**
+     * currently, the only base layer is cyto maps
+     */
+    return baseLayerIds.includes(parc["@id"])
+  }
+}
diff --git a/src/atlasComponents/sapiViews/core/parcellation/parcellationVersion.pipe.spec.ts b/src/atlasComponents/sapiViews/core/parcellation/parcellationVersion.pipe.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5a7eb1fc32405cd1a4f9128cfda54c018eb45766
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/parcellation/parcellationVersion.pipe.spec.ts
@@ -0,0 +1,52 @@
+import { IDS } from "src/atlasComponents/sapi/constants"
+import { SAPI } from "src/atlasComponents/sapi/sapi.service"
+import { SapiParcellationModel } from "src/atlasComponents/sapi/type"
+import { getTraverseFunctions } from "./parcellationVersion.pipe"
+
+describe("parcellationVersion.pipe.ts", () => {
+  describe("getTraverseFunctions", () => {
+    let julichBrainParcellations: SapiParcellationModel[] = []
+    beforeAll(async () => {
+      const res = await fetch(`${SAPI.bsEndpoint}/atlases/${encodeURIComponent(IDS.ATLAES.HUMAN)}/parcellations`)
+      const arr: SapiParcellationModel[] = await res.json()
+      julichBrainParcellations = arr.filter(it => /Julich-Brain Cytoarchitectonic Maps/.test(it.name))
+    })
+    it("> should be at least 3 parcellations", () => {
+      expect(julichBrainParcellations.length).toBeGreaterThanOrEqual(3)
+    })
+
+    const scenarios = [{
+      name: "default",
+      inputFlag: undefined,
+      expect25: false
+    },{
+      name: "skipDeprecated set to true",
+      inputFlag: true,
+      expect25: false
+    },{
+      name: "skipDeprecated set to false",
+      inputFlag: false,
+      expect25: true
+    }]
+
+    for (const { name, inputFlag, expect25} of scenarios) {
+      describe(name, () => {
+        it(`expect to find 25: ${expect25}`, () => {
+          const { findNewer, findOldest } = typeof inputFlag === "undefined"
+          ? getTraverseFunctions(julichBrainParcellations)
+          : getTraverseFunctions(julichBrainParcellations, inputFlag)
+          let cursor: SapiParcellationModel = findOldest()
+          let foundFlag: boolean = false
+          while (cursor) {
+            if (cursor.name === "Julich-Brain Cytoarchitectonic Maps 2.5") {
+              if (expect25) foundFlag = true
+              break
+            }
+            cursor = findNewer(cursor)
+          }
+          expect(foundFlag).toEqual(expect25)
+        })
+      })
+    }
+  })
+})
\ No newline at end of file
diff --git a/src/atlasComponents/sapiViews/core/parcellation/parcellationVersion.pipe.ts b/src/atlasComponents/sapiViews/core/parcellation/parcellationVersion.pipe.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a2e5e1f3ff73e2b3ec837188c52c5670e4348e46
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/parcellation/parcellationVersion.pipe.ts
@@ -0,0 +1,92 @@
+import { Pipe, PipeTransform } from "@angular/core";
+import { SapiParcellationModel } from "src/atlasComponents/sapi/type";
+
+export function getTraverseFunctions(parcellations: SapiParcellationModel[], skipDeprecated: boolean = true) {
+
+  const getTraverse = (key: 'prev' | 'next') => {
+
+    const returnFunction = (parc: SapiParcellationModel) => {
+      if (!parc.version) {
+        throw new Error(`parcellation ${parc.name} does not have version defined!`)
+      }
+      if (!parc.version[key]) {
+        return null
+      }
+      const found = parcellations.find(p => p["@id"] === parc.version[key]["@id"])
+      if (!found) {
+        throw new Error(`parcellation ${parc.name} references ${parc.version[key]['@id']} as ${key} version, but it cannot be found.`)
+      }
+      if (skipDeprecated && found.version.deprecated) {
+        return returnFunction(found)
+      }
+      return found
+    }
+
+    return returnFunction
+  }
+  
+  const findNewer = getTraverse('next')
+  const findOlder = getTraverse('prev')
+
+  const getFindMostFn = (findNewest) => {
+    const useFn = findNewest
+      ? findNewer
+      : findOlder
+    return () => {
+      let cursor = parcellations[0]
+      let returnParc: SapiParcellationModel
+      while (cursor) {
+        returnParc = cursor
+        cursor = useFn(cursor)
+      }
+      return returnParc
+    }
+  }
+
+  return {
+    findNewer,
+    findOlder,
+    findNewest: getFindMostFn(true),
+    findOldest: getFindMostFn(false)
+  }
+  
+}
+
+
+@Pipe({
+  name: 'orderParcellationByVersion',
+  pure: true
+})
+
+export class OrderParcellationByVersionPipe implements PipeTransform{
+  public transform(parcellations: SapiParcellationModel[], newestFirst: boolean = true, index: number = 0) {
+    const {
+      findNewer,
+      findOlder
+    } = getTraverseFunctions(parcellations)
+
+    const findMostFn = newestFirst ? findNewer : findOlder
+    const tranverseFn = newestFirst ? findOlder : findNewer
+
+    const mostParc = (() => {
+      let cursor = parcellations[0]
+      let returnParc: SapiParcellationModel
+      while (cursor) {
+        returnParc = cursor
+        cursor = findMostFn(cursor)
+      }
+      return returnParc
+    })()
+
+    let idx = 0
+    let cursor = mostParc
+    while (idx < index) {
+      cursor = tranverseFn(cursor)
+      if (!cursor) {
+        throw new Error(`index out of bound`)
+      }
+      idx ++
+    }
+    return cursor
+  }
+}
diff --git a/src/atlasComponents/sapiViews/core/parcellation/parcellationVis.service.ts b/src/atlasComponents/sapiViews/core/parcellation/parcellationVis.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3eb329a9b6be3b9ec42afdb82986f460f01b9d15
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/parcellation/parcellationVis.service.ts
@@ -0,0 +1,21 @@
+import { Injectable } from "@angular/core";
+import { BehaviorSubject } from "rxjs";
+
+@Injectable({
+  providedIn: 'root'
+})
+
+export class ParcellationVisibilityService {
+  private _visibility$ = new BehaviorSubject<boolean>(true)
+  public readonly visibility$ = this._visibility$.asObservable()
+
+  setVisibility(flag: boolean) {
+    this._visibility$.next(flag)
+  }
+
+  toggleVisibility(){
+    this.setVisibility(
+      !this._visibility$.getValue()
+    )
+  }
+}
diff --git a/src/atlasComponents/parcellation/getParcPreviewUrl.pipe.ts b/src/atlasComponents/sapiViews/core/parcellation/previewParcellationUrl.pipe.ts
similarity index 69%
rename from src/atlasComponents/parcellation/getParcPreviewUrl.pipe.ts
rename to src/atlasComponents/sapiViews/core/parcellation/previewParcellationUrl.pipe.ts
index 3f70d60e90bdb1645d44f74edbe86ce7b6aec463..7c1a543002a3f533271b778e209c9b39f241b0d8 100644
--- a/src/atlasComponents/parcellation/getParcPreviewUrl.pipe.ts
+++ b/src/atlasComponents/sapiViews/core/parcellation/previewParcellationUrl.pipe.ts
@@ -1,12 +1,14 @@
-import { Pipe, PipeTransform } from "@angular/core"
+import { Pipe, PipeTransform } from "@angular/core";
+import { SapiParcellationModel } from "src/atlasComponents/sapi";
+import { GroupedParcellation } from "./groupedParcellation";
 
 const previewImgMap = new Map([
   
   ['minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579', 'cytoarchitectonic-maps.png'],
   ['juelich/iav/atlas/v1.0.0/3', 'cortical-layers.png'],
   ['juelich/iav/atlas/v1.0.0/4', 'grey-white-matter.png'],
-  ['juelich/iav/atlas/v1.0.0/5', 'firbe-long.png'],
-  ['juelich/iav/atlas/v1.0.0/6', 'firbe-short.png'],
+  ['juelich/iav/atlas/v1.0.0/5', 'fibre-long.png'],
+  ['juelich/iav/atlas/v1.0.0/6', 'fibre-short.png'],
   ['minds/core/parcellationatlas/v1.0.0/d80fbab2-ce7f-4901-a3a2-3c8ef8a3b721', 'difumo-64.png'],
   ['minds/core/parcellationatlas/v1.0.0/73f41e04-b7ee-4301-a828-4b298ad05ab8', 'difumo-128.png'],
   ['minds/core/parcellationatlas/v1.0.0/141d510f-0342-4f94-ace7-c97d5f160235', 'difumo-256.png'],
@@ -30,20 +32,22 @@ const previewImgMap = new Map([
  * used for directories
  */
 const previewNameToPngMap = new Map([
-  ['fibre architecture', 'firbe-long.png'],
+  ['fibre architecture', 'fibre-long.png'],
   ['functional modes', 'difumo-128.png']
 ])
 
 @Pipe({
-  name: 'getParcPreviewUrl',
+  name: 'previewParcellationUrl',
   pure: true
 })
 
-export class GetParcPreviewUrlPipe implements PipeTransform{
-  public transform(tile: any){
-    const filename = tile['@id']
-      ? previewImgMap.get(tile['@id'])
-      : previewNameToPngMap.get(tile['name'])
+export class PreviewParcellationUrlPipe implements PipeTransform{
+  public transform(tile: SapiParcellationModel | GroupedParcellation): string {
+    if (tile instanceof GroupedParcellation) {
+      const filename = previewNameToPngMap.get(tile.name)
+      return filename && `assets/images/atlas-selection/${filename}`
+    }
+    const filename = previewImgMap.get(tile['@id'])
     return filename && `assets/images/atlas-selection/${filename}`
   }
 }
diff --git a/src/atlasComponents/sapiViews/core/parcellation/smartChip/parcellation.smartChip.component.ts b/src/atlasComponents/sapiViews/core/parcellation/smartChip/parcellation.smartChip.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..27d4a53cc941a95164407b86f61825c01df1527f
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/parcellation/smartChip/parcellation.smartChip.component.ts
@@ -0,0 +1,104 @@
+import { Component, EventEmitter, Input, OnChanges, Output, SimpleChange, SimpleChanges } from "@angular/core";
+import { BehaviorSubject, concat, Observable, of, timer } from "rxjs";
+import { SapiParcellationModel } from "src/atlasComponents/sapi/type";
+import { ParcellationVisibilityService } from "../parcellationVis.service";
+import { ARIA_LABELS } from "common/constants"
+import { getTraverseFunctions } from "../parcellationVersion.pipe";
+import { mapTo, shareReplay, switchMap } from "rxjs/operators";
+
+@Component({
+  selector: `sxplr-sapiviews-core-parcellation-smartchip`,
+  templateUrl: `./parcellation.smartChip.template.html`,
+  styleUrls: [
+    `./parcellation.smartChip.style.css`
+  ]
+})
+
+export class SapiViewsCoreParcellationParcellationSmartChip implements OnChanges{
+
+  public ARIA_LABELS = ARIA_LABELS
+
+  @Input('sxplr-sapiviews-core-parcellation-smartchip-parcellation')
+  parcellation: SapiParcellationModel
+
+  @Input('sxplr-sapiviews-core-parcellation-smartchip-all-parcellations')
+  parcellations: SapiParcellationModel[]
+
+  @Output('sxplr-sapiviews-core-parcellation-smartchip-dismiss-nonbase-layer')
+  onDismiss = new EventEmitter<SapiParcellationModel>()
+
+  @Output('sxplr-sapiviews-core-parcellation-smartchip-select-parcellation')
+  onSelectParcellation = new EventEmitter<SapiParcellationModel>()
+
+  constructor(
+    private svc: ParcellationVisibilityService
+  ){
+
+  }
+
+  otherVersions: SapiParcellationModel[]
+
+  ngOnChanges(changes: SimpleChanges) {
+    const { parcellation } = changes
+    if (parcellation) {
+      this.onDismissClicked$.next(false)
+    }
+    this.otherVersions = []
+    if (!this.parcellation) {
+      return
+    }
+    this.otherVersions = [ this.parcellation ]
+    if (!this.parcellations || this.parcellations.length === 0) {
+      return 
+    }
+    if (!this.parcellation.version) {
+      return 
+    }
+
+    this.otherVersions = []
+
+    const {
+      findNewest,
+      findOlder
+    } = getTraverseFunctions(this.parcellations)
+
+    let cursor: SapiParcellationModel = findNewest()
+    while (cursor) {
+      this.otherVersions.push(cursor)
+      cursor = findOlder(cursor)
+    }
+  }
+
+  loadingParc$: Observable<SapiParcellationModel> = this.onSelectParcellation.pipe(
+    switchMap(parc => concat(
+      of(parc),
+      timer(5000).pipe(
+        mapTo(null)
+      ),
+    )),
+    shareReplay(1),
+  )
+
+  parcellationVisibility$: Observable<boolean> = this.svc.visibility$
+
+  toggleParcellationVisibility(){
+    this.svc.toggleVisibility()
+  }
+
+  dismiss(){
+    if (this.onDismissClicked$.value) return
+    this.onDismissClicked$.next(true)
+    this.onDismiss.emit(this.parcellation)
+  }
+
+  selectParcellation(parc: SapiParcellationModel){
+    if (this.trackByFn(parc) === this.trackByFn(this.parcellation)) return
+    this.onSelectParcellation.emit(parc)
+  }
+
+  trackByFn(parc: SapiParcellationModel){
+    return parc["@id"]
+  }
+
+  onDismissClicked$ = new BehaviorSubject<boolean>(false)
+}
diff --git a/src/atlasComponents/sapiViews/core/parcellation/smartChip/parcellation.smartChip.stories.ts b/src/atlasComponents/sapiViews/core/parcellation/smartChip/parcellation.smartChip.stories.ts
new file mode 100644
index 0000000000000000000000000000000000000000..054c0c70eacd03943a5db1465bd412f4a8353c04
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/parcellation/smartChip/parcellation.smartChip.stories.ts
@@ -0,0 +1,143 @@
+import { CommonModule } from "@angular/common"
+import { HttpClientModule } from "@angular/common/http"
+import { Component, Input } from "@angular/core"
+import { Meta, moduleMetadata, Story } from "@storybook/angular"
+import { action } from "@storybook/addon-actions"
+import { SAPI, SapiParcellationModel } from "src/atlasComponents/sapi"
+import { atlasId, getAtlas, provideDarkTheme, getParc, getAtlases } from "src/atlasComponents/sapi/stories.base"
+import { AngularMaterialModule } from "src/sharedModules"
+import { SapiViewsCoreParcellationModule } from "../module"
+import { provideMockStore } from "@ngrx/store/testing"
+import { within, userEvent } from '@storybook/testing-library';
+import { ARIA_LABELS } from "common/constants"
+import { ParcellationVisibilityService } from "../parcellationVis.service"
+import { of } from "rxjs"
+
+@Component({
+  selector: `parc-smart-chip-wrapper`,
+  template: `
+  <mat-accordion>
+    <mat-expansion-panel *ngFor="let item of parcRecords | keyvalue">
+      <mat-expansion-panel-header>
+        {{ item.key }}
+      </mat-expansion-panel-header>
+
+      <div class="sxplr-of-x-scroll sxplr-white-space-nowrap">
+        <sxplr-sapiviews-core-parcellation-smartchip *ngFor="let parc of item.value | filterUnsupportedParc"
+          [sxplr-sapiviews-core-parcellation-smartchip-parcellation]="parc"
+          [sxplr-sapiviews-core-parcellation-smartchip-all-parcellations]="item.value"
+          (sxplr-sapiviews-core-parcellation-smartchip-select-parcellation)="selectParcellation($event)">
+        </sxplr-sapiviews-core-parcellation-smartchip>
+      </div>
+
+    </mat-expansion-panel>
+  </mat-accordion>
+  `,
+  styles: [
+    `sxplr-sapiviews-core-parcellation-chip { display: block;  }`
+  ]
+})
+
+class ParcSmartChipWrapper{
+  @Input()
+  parcRecords: Record<string, SapiParcellationModel[]> = {}
+
+  selectParcellation(parc: SapiParcellationModel){
+
+  }
+}
+
+
+export default {
+  component: ParcSmartChipWrapper,
+  decorators: [
+    moduleMetadata({
+      imports: [
+        CommonModule,
+        HttpClientModule,
+        SapiViewsCoreParcellationModule,
+        AngularMaterialModule,
+      ],
+      providers: [
+        provideMockStore(),
+        SAPI,
+        ...provideDarkTheme,
+      ],
+      declarations: []
+    })
+  ],
+} as Meta
+
+const Template: Story<ParcSmartChipWrapper> = (args: ParcSmartChipWrapper, { loaded, parameters }) => {
+  const { 
+    parcRecords
+  } = loaded
+  const { providers = [] } = parameters
+
+  return ({
+    props: {
+      ...args,
+      selectParcellation: action("selectParcellation"),
+      parcRecords
+    },
+    moduleMetadata: {
+      providers
+    }
+  })
+}
+Template.loaders = []
+
+const asyncLoader = async () => {
+  const parcRecords: Record<string, SapiParcellationModel[]> = {}
+
+  for (const species in atlasId) {
+
+    const atlasDetail = await getAtlas(atlasId[species])
+    parcRecords[species] = []
+    for (const parc of atlasDetail.parcellations) {
+      const parcDetail = await getParc(atlasDetail['@id'], parc['@id'])
+      parcRecords[species].push(parcDetail)
+    }
+  }
+  
+  return {
+    parcRecords
+  }
+}
+
+export const Default = Template.bind({})
+Default.loaders = [
+  async () => {
+    const {
+      parcRecords
+    } = await asyncLoader()
+    return {
+      parcRecords
+    }
+  }
+]
+
+export const TestInteraction = Template.bind({})
+TestInteraction.loaders = [
+  ...Default.loaders
+]
+TestInteraction.parameters = {
+  providers: [
+    {
+      provide: ParcellationVisibilityService,
+      useValue: {
+        setVisibility: action('setVisibility'),
+        toggleVisibility: action('toggleVisibility'),
+        visibility$: of(true)
+      }
+    }
+  ]
+}
+TestInteraction.play = async ({ args, canvasElement }) => {
+  const canvas = within(canvasElement)
+
+  await userEvent.click(canvas.getByText("human"))
+  const allEye = canvas.getAllByText(ARIA_LABELS.TOGGLE_DELINEATION)
+  await userEvent.hover(allEye[0])
+  await userEvent.click(allEye[0])
+}
diff --git a/src/atlasComponents/sapiViews/core/parcellation/smartChip/parcellation.smartChip.style.css b/src/atlasComponents/sapiViews/core/parcellation/smartChip/parcellation.smartChip.style.css
new file mode 100644
index 0000000000000000000000000000000000000000..e8d0ca357ccbfe577e65a2f9d78c1589227cbf33
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/parcellation/smartChip/parcellation.smartChip.style.css
@@ -0,0 +1,23 @@
+.otherversion-wrapper
+{
+  position: relative;
+  overflow: hidden;
+  margin: 0.5rem;
+}
+
+.otherversion-wrapper.loading > .spinner-container
+{
+  position: absolute;
+  left: 0;
+  top: 0;
+  width: 100%;
+  height: 100%;
+
+  display: flex;
+  align-items: center;
+}
+
+.otherversion-wrapper.loading > .spinner-container > spinner-cmp
+{
+  margin: 0.5rem;
+}
diff --git a/src/atlasComponents/sapiViews/core/parcellation/smartChip/parcellation.smartChip.template.html b/src/atlasComponents/sapiViews/core/parcellation/smartChip/parcellation.smartChip.template.html
new file mode 100644
index 0000000000000000000000000000000000000000..e56b6b69b9fc9405c4efb29900f9a448921839cf
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/parcellation/smartChip/parcellation.smartChip.template.html
@@ -0,0 +1,78 @@
+<mat-menu #otherParcMenu="matMenu"
+  [hasBackdrop]="false"
+  class="parc-smart-chip-menu-panel sxplr-bg-none sxplr-of-x-hidden sxplr-box-shadow-none sxplr-mxw-80vw">
+  <div (iav-outsideClick)="menuTrigger.closeMenu()">
+
+    <div *ngFor="let parc of otherVersions"
+      class="otherversion-wrapper"
+      [ngClass]="{
+        'loading': (loadingParc$ | async) === parc
+      }">
+
+
+      <sxplr-sapiviews-core-parcellation-chip
+        [ngClass]="{
+          'sxplr-blink': (loadingParc$ | async) === parc
+        }"
+        [sxplr-sapiviews-core-parcellation-chip-parcellation]="parc"
+        [sxplr-sapiviews-core-parcellation-chip-color]="(parcellation | equality : parc : trackByFn) ? 'primary' : 'default'"
+        (sxplr-sapiviews-core-parcellation-chip-onclick)="selectParcellation(parc)">
+
+      </sxplr-sapiviews-core-parcellation-chip>
+
+      <div class="spinner-container" *ngIf="(loadingParc$ | async) === parc">
+        <spinner-cmp>
+        </spinner-cmp>
+      </div>
+    </div>
+  </div>
+  
+</mat-menu>
+
+<sxplr-sapiviews-core-parcellation-chip
+  [ngClass]="{
+    'sxplr-muted': !(parcellationVisibility$ | async),
+    'sxplr-blink': onDismissClicked$ | async
+  }"
+  class="sxplr-d-inline-block"
+  [sxplr-sapiviews-core-parcellation-chip-parcellation]="parcellation"
+  [sxplr-sapiviews-core-parcellation-chip-color]="(parcellation | parcellationIsBaseLayer) ? 'default' : 'primary'"
+  (sxplr-sapiviews-core-parcellation-chip-onclick)="menuTrigger.toggleMenu()"
+  [matMenuTriggerFor]="otherParcMenu"
+  #menuTrigger="matMenuTrigger"
+  >
+
+  <div prefix class="sxplr-scale-70">
+    <button mat-mini-fab
+      [color]="(parcellationVisibility$ | async) ? 'primary' : 'default'"
+      [matTooltip]="ARIA_LABELS.TOGGLE_DELINEATION"
+      iav-stop="mousedown click"
+      [iav-key-listener]="[{'type': 'keydown', 'key': 'q', 'capture': true, 'target': 'document' }]"
+      (iav-key-event)="toggleParcellationVisibility()"
+      (click)="toggleParcellationVisibility()">
+      <i class="fas"
+        [ngClass]="(parcellationVisibility$ | async) ? 'fa-eye': 'fa-eye-slash'"
+        aria-hidden="true">
+      </i>
+      <span class="sr-only">
+        {{ ARIA_LABELS.TOGGLE_DELINEATION }}
+      </span>
+    </button>
+  </div>
+
+  <div *ngIf="!(parcellation | parcellationIsBaseLayer)"
+    class="sxplr-scale-70"
+    suffix>
+    <button mat-mini-fab
+      color="primary"
+      iav-stop="mousedown click"
+      (click)="dismiss()">
+
+      <spinner-cmp class="sxplr-w-100 sxplr-h-100" *ngIf="onDismissClicked$ | async; else defaultDismissIcon"></spinner-cmp>
+      <ng-template #defaultDismissIcon>
+        <i class="fas fa-times"></i>
+      </ng-template>
+
+    </button>
+  </div>
+</sxplr-sapiviews-core-parcellation-chip>
diff --git a/src/atlasComponents/sapiViews/core/parcellation/tile/parcellation.tile.component.ts b/src/atlasComponents/sapiViews/core/parcellation/tile/parcellation.tile.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..839e8791ff2df8090de142deedf0e39046cfec32
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/parcellation/tile/parcellation.tile.component.ts
@@ -0,0 +1,57 @@
+import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges, TemplateRef } from "@angular/core";
+import { SapiParcellationModel } from "src/atlasComponents/sapi";
+import { GroupedParcellation } from "../groupedParcellation";
+
+const lightthemeId = [
+  'juelich/iav/atlas/v1.0.0/3',
+  'juelich/iav/atlas/v1.0.0/4',
+]
+
+@Component({
+  selector: `sxplr-sapiviews-core-parcellation-tile`,
+  templateUrl: './parcellation.tile.template.html',
+  styleUrls: [
+    `./parcellation.tile.style.css`
+  ],
+})
+
+export class SapiViewsCoreParcellationParcellationTile implements OnChanges{
+  @Input('sxplr-sapiviews-core-parcellation-tile-groupmenu-parc-tmpl')
+  singleParcTmpl: TemplateRef<any>
+
+  private _parcellation: SapiParcellationModel | GroupedParcellation
+  @Input('sxplr-sapiviews-core-parcellation-tile-parcellation')
+  set parcellation(val: SapiParcellationModel | GroupedParcellation) {
+    this._parcellation = val
+    this.ngOnChanges()
+  }
+  get parcellation(){
+    return this._parcellation
+  }
+
+  @Input('sxplr-sapiviews-core-parcellation-tile-selected')
+  selected: boolean = false
+
+  @Output('sxplr-sapiviews-core-parcellation-tile-onclick-parc')
+  onClickOnParcellation = new EventEmitter<SapiParcellationModel>()
+
+  public gutterSize = "2"
+  public rowHeight = "6:11"
+
+  public darktheme = false
+  public pureParc: SapiParcellationModel
+  public dirParc: GroupedParcellation
+
+  ngOnChanges(): void {
+    if (this.parcellation instanceof GroupedParcellation) {
+      this.dirParc = this.parcellation
+    } else {
+      this.pureParc = this.parcellation
+    }
+    this.darktheme = !!this.dirParc || lightthemeId.indexOf(this.parcellation['@id']) < 0
+  }
+
+  clickOnParcellation(parc: SapiParcellationModel){
+    this.onClickOnParcellation.emit(parc)
+  }
+}
diff --git a/src/atlasComponents/sapiViews/core/parcellation/tile/parcellation.tile.stories.ts b/src/atlasComponents/sapiViews/core/parcellation/tile/parcellation.tile.stories.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1fad5b1cd9332700829f662798829e919691b956
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/parcellation/tile/parcellation.tile.stories.ts
@@ -0,0 +1,160 @@
+import { CommonModule } from "@angular/common"
+import { HttpClientModule } from "@angular/common/http"
+import { Component, Input, Output, EventEmitter } from "@angular/core"
+import { provideMockStore } from "@ngrx/store/testing"
+import { Meta, moduleMetadata, Story } from "@storybook/angular"
+import { SAPI, SapiParcellationModel } from "src/atlasComponents/sapi"
+import { atlasId, parcId, getAtlas, provideDarkTheme, getParc } from "src/atlasComponents/sapi/stories.base"
+import { AngularMaterialModule } from "src/sharedModules"
+import { SapiViewsCoreParcellationModule } from "../module"
+
+@Component({
+  selector: `parc-tile-wrapper`,
+  template: `
+  <ng-template #grpParcTmpl let-parc>
+    {{ parc.name }}
+  </ng-template>
+
+  <mat-accordion>
+    <mat-expansion-panel *ngFor="let item of parcs | keyvalue">
+
+      <mat-expansion-panel-header>
+        {{ item.key }}
+      </mat-expansion-panel-header>
+
+      <ng-template matExpansionPanelContent>
+        <div class="sxplr-d-inline-flex align-items-start">
+          <sxplr-sapiviews-core-parcellation-tile
+            *ngFor="let parc of item.value"
+            [sxplr-sapiviews-core-parcellation-tile-parcellation]="parc"
+            [sxplr-sapiviews-core-parcellation-tile-selected]="parc['@id'] === selected"
+            class="sxplr-m-2"
+            (sxplr-sapiviews-core-parcellation-tile-onclick-parc)="parcClicked.emit($event)">
+          </sxplr-sapiviews-core-parcellation-tile>
+        </div>
+      </ng-template>
+
+    </mat-expansion-panel>
+
+    <mat-expansion-panel>
+      <mat-expansion-panel-header>
+        > grouped
+      </mat-expansion-panel-header>
+
+      <ng-template matExpansionPanelContent>
+        <ng-container *ngFor="let item of parcs | keyvalue">
+          <sxplr-sapiviews-core-parcellation-tile
+            *ngFor="let parc of (item.value | filterGroupedParcs : true)"
+            [sxplr-sapiviews-core-parcellation-tile-parcellation]="parc"
+            class="sxplr-m-2"
+            (sxplr-sapiviews-core-parcellation-tile-onclick-parc)="parcClicked.emit($event)">
+          </sxplr-sapiviews-core-parcellation-tile>
+        </ng-container>
+      </ng-template>
+    </mat-expansion-panel>
+
+    <mat-expansion-panel>
+      <mat-expansion-panel-header>
+        > grouped tmpl
+      </mat-expansion-panel-header>
+
+      <ng-template matExpansionPanelContent>
+        <ng-container *ngFor="let item of parcs | keyvalue">
+          <sxplr-sapiviews-core-parcellation-tile
+            *ngFor="let parc of (item.value | filterGroupedParcs : true)"
+            [sxplr-sapiviews-core-parcellation-tile-groupmenu-parc-tmpl]="grpParcTmpl"
+            [sxplr-sapiviews-core-parcellation-tile-parcellation]="parc"
+            class="sxplr-m-2"
+            (sxplr-sapiviews-core-parcellation-tile-onclick-parc)="parcClicked.emit($event)">
+          </sxplr-sapiviews-core-parcellation-tile>
+        </ng-container>
+      </ng-template>
+    </mat-expansion-panel>
+  </mat-accordion>
+  `,
+  styles: [
+    `sxplr-sapiviews-core-parcellation-tile { display: inline-block; max-width: 8rem; }`
+  ]
+})
+
+class ParcTileWrapper{
+  @Input()
+  parcs: Record<string, SapiParcellationModel[]> = {}
+
+  @Input()
+  selected: string = parcId.human.longBundle
+
+  @Output()
+  parcClicked = new EventEmitter()
+}
+
+export default {
+  component: ParcTileWrapper,
+  decorators: [
+    moduleMetadata({
+      imports: [
+        CommonModule,
+        HttpClientModule,
+        SapiViewsCoreParcellationModule,
+        AngularMaterialModule,
+      ],
+      providers: [
+        provideMockStore(),
+        SAPI,
+        ...provideDarkTheme,
+      ],
+      declarations: [
+        ParcTileWrapper
+      ]
+    })
+  ],
+} as Meta
+
+const Template: Story<ParcTileWrapper> = (args: ParcTileWrapper, { loaded }) => {
+  const { 
+    parcs
+  } = loaded
+
+  return ({
+    props: {
+      ...args,
+      parcs
+    }
+  })
+}
+Template.loaders = [
+
+]
+
+const asyncLoader = async () => {
+  const parcs: Record<string, SapiParcellationModel[]> = {}
+  for (const species in atlasId) {
+    const atlasDetail = await getAtlas(atlasId[species])
+    parcs[species] = []
+    
+    for (const parc of atlasDetail.parcellations) {
+      const parcDetail = await getParc(atlasDetail['@id'], parc['@id'])
+      parcs[species].push(parcDetail)
+    }
+  }
+
+  return {
+    parcs
+  }
+}
+
+export const Default = Template.bind({})
+Default.args = {
+  selected: parcId.human.longBundle
+}
+Default.loaders = [
+
+  async () => {
+    const {
+      parcs
+    } = await asyncLoader()
+    return {
+      parcs
+    }
+  }
+]
diff --git a/src/atlasComponents/regionalFeatures/util.ts b/src/atlasComponents/sapiViews/core/parcellation/tile/parcellation.tile.style.css
similarity index 100%
rename from src/atlasComponents/regionalFeatures/util.ts
rename to src/atlasComponents/sapiViews/core/parcellation/tile/parcellation.tile.style.css
diff --git a/src/atlasComponents/sapiViews/core/parcellation/tile/parcellation.tile.template.html b/src/atlasComponents/sapiViews/core/parcellation/tile/parcellation.tile.template.html
new file mode 100644
index 0000000000000000000000000000000000000000..19cd424794e7bc343d775fa541979bf1019cf0ac
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/parcellation/tile/parcellation.tile.template.html
@@ -0,0 +1,69 @@
+<mat-menu hasBackDrop="false" #matMenu="matMenu">
+
+  <ng-template matMenuContent let-subParcellations="subParcellations">
+
+    <div class="sxplr-custom-cmp sxplr-ml-2 sxplr-mr-2">
+      <mat-grid-list
+        cols="1"
+        [rowHeight]="rowHeight"
+        [gutterSize]="gutterSize">
+
+        <mat-grid-tile
+          *ngFor="let parc of subParcellations">
+
+
+          <!-- if parent component injected template, use injected template -->
+          <ng-template
+            [ngIf]="singleParcTmpl"
+            [ngIfElse]="fallbackGrpParcTmpl"
+            [ngTemplateOutlet]="singleParcTmpl"
+            [ngTemplateOutletContext]="{
+              $implicit: parc
+            }">
+          </ng-template>
+
+          <ng-template #fallbackGrpParcTmpl>
+            <tile-cmp *ngIf="parc"
+              class="sxplr-custom-cmp text"
+              [tile-text]="parc.name"
+              [tile-image-src]="parc | previewParcellationUrl"
+              [tile-selected]="selected"
+              [tile-image-darktheme]="darktheme"
+              (click)="clickOnParcellation(parc)">
+      
+            </tile-cmp>
+          </ng-template>
+    
+        </mat-grid-tile>
+      </mat-grid-list>
+    </div>
+  </ng-template>
+</mat-menu>
+
+<ng-template [ngIf]="parcellation">
+
+  <tile-cmp *ngIf="pureParc"
+    [tile-text]="pureParc.name"
+    [tile-image-src]="pureParc | previewParcellationUrl"
+    [tile-selected]="selected"
+    [tile-image-darktheme]="darktheme"
+    (click)="clickOnParcellation(pureParc)"
+    >
+
+  </tile-cmp>
+
+
+  <tile-cmp *ngIf="dirParc"
+    [tile-text]="dirParc.name"
+    [tile-image-src]="dirParc | previewParcellationUrl"
+    [tile-selected]="selected"
+    [tile-image-darktheme]="darktheme"
+    tile-is-dir="true"
+    [matMenuTriggerFor]="matMenu"
+    [matMenuTriggerData]="{
+        subParcellations: dirParc.parcellations || []
+    }"
+    >
+  </tile-cmp>
+
+</ng-template>
\ No newline at end of file
diff --git a/src/atlasComponents/sapiViews/core/parcellation/tile/singleTile.stories.ts b/src/atlasComponents/sapiViews/core/parcellation/tile/singleTile.stories.ts
new file mode 100644
index 0000000000000000000000000000000000000000..920c1cfc82a26550a2710a584f6db9f45b0ea5af
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/parcellation/tile/singleTile.stories.ts
@@ -0,0 +1,90 @@
+import { CommonModule } from "@angular/common"
+import { HttpClientModule } from "@angular/common/http"
+import { provideMockStore } from "@ngrx/store/testing"
+import { Meta, moduleMetadata, Story } from "@storybook/angular"
+import { SAPI, SapiParcellationModel } from "src/atlasComponents/sapi"
+import { parcId, provideDarkTheme, getParc, getHumanAtlas } from "src/atlasComponents/sapi/stories.base"
+import { AngularMaterialModule } from "src/sharedModules"
+import { FilterGroupedParcellationPipe } from "../filterGroupedParcellations.pipe"
+import { GroupedParcellation } from "../groupedParcellation"
+import { SapiViewsCoreParcellationModule } from "../module"
+import { SapiViewsCoreParcellationParcellationTile } from "./parcellation.tile.component"
+
+export default {
+  component: SapiViewsCoreParcellationParcellationTile,
+  decorators: [
+    moduleMetadata({
+      imports: [
+        CommonModule,
+        HttpClientModule,
+        SapiViewsCoreParcellationModule,
+        AngularMaterialModule,
+      ],
+      providers: [
+        provideMockStore(),
+        SAPI,
+        ...provideDarkTheme,
+      ],
+      declarations: [
+        
+      ],
+    }),
+  ],
+} as Meta
+
+const Template: Story<SapiViewsCoreParcellationParcellationTile> = (args: SapiViewsCoreParcellationParcellationTile, { loaded }) => {
+  const { 
+    groups
+  } = loaded
+  
+  const {
+    gutterSize,
+    rowHeight
+  } = args
+  return ({
+    props: {
+      gutterSize,
+      rowHeight,
+      parcellation: groups[1]
+    },
+    styles: [
+      `sxplr-sapiviews-core-parcellation-tile { display: inline-block; max-width: 8rem; }`
+    ]
+  })
+}
+Template.loaders = [
+
+]
+
+const asyncLoader = async () => {
+  const parcs: SapiParcellationModel[] = []
+
+  const atlasDetail = await getHumanAtlas()
+  
+  for (const parc of atlasDetail.parcellations) {
+    const parcDetail = await getParc(atlasDetail['@id'], parc['@id'])
+    parcs.push(parcDetail)
+  }
+
+  const pipe = new FilterGroupedParcellationPipe()
+  const groups = pipe.transform(parcs, true) as GroupedParcellation[]
+  return {
+    groups
+  }
+}
+
+export const Default = Template.bind({})
+Default.args = {
+  selected: parcId.human.longBundle
+}
+Default.loaders = [
+
+  async () => {
+    const {
+      groups
+    } = await asyncLoader()
+    return {
+      groups
+    }
+  }
+]
diff --git a/src/atlasComponents/sapiViews/core/region/index.ts b/src/atlasComponents/sapiViews/core/region/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8b76cf0a9a6de3c8d738f9fb145fb97a7faceb61
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/region/index.ts
@@ -0,0 +1 @@
+export { SapiViewsCoreRegionModule } from "./module"
\ No newline at end of file
diff --git a/src/atlasComponents/sapiViews/core/region/module.ts b/src/atlasComponents/sapiViews/core/region/module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6c30611aa3b4ce8494f21bc60501da87089b9bb6
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/region/module.ts
@@ -0,0 +1,39 @@
+import { CommonModule } from "@angular/common";
+import { NgModule } from "@angular/core";
+import { MarkdownModule } from "src/components/markdown";
+import { SpinnerModule } from "src/components/spinner";
+import { AngularMaterialModule } from "src/sharedModules";
+import { SapiViewsFeaturesModule } from "../../features";
+import { SapiViewsUtilModule } from "../../util/module";
+import { SapiViewsCoreRegionRegionChip } from "./region/chip/region.chip.component";
+import { SapiViewsCoreRegionRegionListItem } from "./region/listItem/region.listItem.component";
+import { SapiViewsCoreRegionRegionBase } from "./region/region.base.directive";
+import { SapiViewsCoreRegionRegionalFeatureDirective } from "./region/region.features.directive";
+import { SapiViewsCoreRegionRegionRich } from "./region/rich/region.rich.component";
+
+@NgModule({
+  imports: [
+    CommonModule,
+    AngularMaterialModule,
+    SapiViewsUtilModule,
+    SapiViewsFeaturesModule,
+    SpinnerModule,
+    MarkdownModule,
+  ],
+  declarations: [
+    SapiViewsCoreRegionRegionListItem,
+    SapiViewsCoreRegionRegionRich,
+    SapiViewsCoreRegionRegionChip,
+    SapiViewsCoreRegionRegionBase,
+    SapiViewsCoreRegionRegionalFeatureDirective,
+  ],
+  exports: [
+    SapiViewsCoreRegionRegionListItem,
+    SapiViewsCoreRegionRegionRich,
+    SapiViewsCoreRegionRegionChip,
+    SapiViewsCoreRegionRegionBase,
+    SapiViewsCoreRegionRegionalFeatureDirective,
+  ]
+})
+
+export class SapiViewsCoreRegionModule{}
diff --git a/src/atlasComponents/sapiViews/core/region/region/chip/region.chip.component.ts b/src/atlasComponents/sapiViews/core/region/region/chip/region.chip.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1852f3e602a60d3739dc5bffa89a190b64fb33f7
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/region/region/chip/region.chip.component.ts
@@ -0,0 +1,20 @@
+import { Component, EventEmitter, Output } from "@angular/core";
+import { SapiViewsCoreRegionRegionBase } from "../region.base.directive";
+
+@Component({
+  selector: `sxplr-sapiviews-core-region-region-chip`,
+  templateUrl: `./region.chip.template.html`,
+  styleUrls: [
+    `./region.chip.style.css`
+  ]
+})
+
+export class SapiViewsCoreRegionRegionChip extends SapiViewsCoreRegionRegionBase {
+  shouldFetchDetail = true
+  @Output('sxplr-sapiviews-core-region-region-chip-clicked')
+  clickEmitter = new EventEmitter<MouseEvent>()
+
+  onClick(event: MouseEvent){
+    this.clickEmitter.emit(event)
+  }
+}
diff --git a/src/atlasComponents/sapiViews/core/region/region/chip/region.chip.stories.ts b/src/atlasComponents/sapiViews/core/region/region/chip/region.chip.stories.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ec74e25aadf0b5f3d00ef164efd2ff19238259bb
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/region/region/chip/region.chip.stories.ts
@@ -0,0 +1,134 @@
+import { CommonModule } from "@angular/common"
+import { HttpClientModule } from "@angular/common/http"
+import { Meta, moduleMetadata, Story } from "@storybook/angular"
+import { SAPI } from "src/atlasComponents/sapi"
+import { provideDarkTheme, getHumanAtlas, getJba29, getMni152, getHoc1Right, get44Left } from "src/atlasComponents/sapi/stories.base"
+import { AngularMaterialModule } from "src/sharedModules"
+import { SapiViewsCoreRegionModule } from "../../module"
+import { SapiViewsCoreRegionRegionChip } from "./region.chip.component"
+
+
+export default {
+  component: SapiViewsCoreRegionRegionChip,
+  decorators: [
+    moduleMetadata({
+      imports: [
+        CommonModule,
+        HttpClientModule,
+        SapiViewsCoreRegionModule,
+        AngularMaterialModule,
+      ],
+      providers: [
+        SAPI,
+        ...provideDarkTheme,
+      ],
+      declarations: []
+    })
+  ],
+} as Meta
+
+const Template: Story<SapiViewsCoreRegionRegionChip> = (args: SapiViewsCoreRegionRegionChip, { loaded, parameters }) => {
+  const { 
+    atlas,
+    parcellation,
+    template,
+    region,
+  } = loaded
+  const {
+    contentProjection
+  } = parameters
+
+  return ({
+    props: {
+      atlas,
+      parcellation,
+      template,
+      region,
+    },
+    template: `
+    <sxplr-sapiviews-core-region-region-chip>
+      ${contentProjection || ''}
+    </sxplr-sapiviews-core-region-region-chip>
+    `
+  })
+}
+Template.loaders = []
+
+const getContentProjection = ({ prefix = null, suffix = null }) => {
+  let returnVal = ``
+  if (prefix) {
+    returnVal += `<div prefix>${prefix}</div>`
+  }
+  if (suffix) {
+    returnVal += `<div suffix>${suffix}</div>`
+  }
+  return returnVal
+}
+
+export const Default = Template.bind({})
+Default.loaders = [
+  async () => {
+    
+    const atlas = await getHumanAtlas()
+    const parcellation = await getJba29()
+    const template = await getMni152()
+    const region = await getHoc1Right()
+
+    return {
+      atlas,
+      parcellation,
+      template,
+      region,
+    }
+  }
+]
+
+export const Dark = Template.bind({})
+Dark.loaders = [
+  async () => {
+    
+    const atlas = await getHumanAtlas()
+    const parcellation = await getJba29()
+    const template = await getMni152()
+    const region = await get44Left()
+
+    return {
+      atlas,
+      parcellation,
+      template,
+      region,
+    }
+  }
+]
+
+export const Prefix = Template.bind({})
+Prefix.loaders = [
+  ...Default.loaders
+]
+Prefix.parameters = {
+  contentProjection: getContentProjection({
+    prefix: `PREFIX`,
+  })
+}
+
+export const Suffix = Template.bind({})
+Suffix.loaders = [
+  ...Default.loaders
+]
+Suffix.parameters = {
+  contentProjection: getContentProjection({
+    suffix: `SUFFIX`,
+  })
+}
+
+
+export const PrefixSuffix = Template.bind({})
+PrefixSuffix.loaders = [
+  ...Default.loaders
+]
+PrefixSuffix.parameters = {
+  contentProjection: getContentProjection({
+    prefix: `PREFIX`,
+    suffix: `SUFFIX`,
+  })
+}
diff --git a/src/atlasComponents/uiSelectors/atlasDropdown/atlasDropdown.style.css b/src/atlasComponents/sapiViews/core/region/region/chip/region.chip.style.css
similarity index 100%
rename from src/atlasComponents/uiSelectors/atlasDropdown/atlasDropdown.style.css
rename to src/atlasComponents/sapiViews/core/region/region/chip/region.chip.style.css
diff --git a/src/atlasComponents/sapiViews/core/region/region/chip/region.chip.template.html b/src/atlasComponents/sapiViews/core/region/region/chip/region.chip.template.html
new file mode 100644
index 0000000000000000000000000000000000000000..9b8262ee0441e16ce49b34d4a6d43ea9b60833b8
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/region/region/chip/region.chip.template.html
@@ -0,0 +1,35 @@
+<ng-template #prefixTmpl>
+  <ng-content select="[prefix]"></ng-content>
+</ng-template>
+
+<ng-template #suffixTmpl>
+  <ng-content select="[suffix]"></ng-content>
+</ng-template>
+
+<div *ngIf="!region">
+  <div class="sxplr-d-inline-block">
+    <ng-template [ngTemplateOutlet]="prefixTmpl"></ng-template>
+  </div>
+  <spinner-cmp class="sxplr-d-inline-block"></spinner-cmp>
+  <div class="sxplr-d-inline-block">
+    <ng-template [ngTemplateOutlet]="suffixTmpl"></ng-template>
+  </div>
+</div>
+
+<mat-chip-list *ngIf="region"
+  [ngClass]="{
+    'darktheme': regionDarkmode,
+    'lighttheme': !regionDarkmode
+  }">
+  <mat-chip
+    (click)="onClick($event)"
+    class="sxplr-custom-cmp text"
+    [style.backgroundColor]="regionRgbString"
+    >
+    <ng-template [ngTemplateOutlet]="prefixTmpl"></ng-template>
+    <span class="mat-body">
+      {{ region.name }}
+    </span>
+    <ng-template [ngTemplateOutlet]="suffixTmpl"></ng-template>
+  </mat-chip>
+</mat-chip-list>
diff --git a/src/atlasComponents/sapiViews/core/region/region/listItem/region.listItem.component.ts b/src/atlasComponents/sapiViews/core/region/region/listItem/region.listItem.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..449b7fa3f4947b07df2c2df7afac1d7e04e4e738
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/region/region/listItem/region.listItem.component.ts
@@ -0,0 +1,15 @@
+import { Component, Input } from "@angular/core";
+import { SapiViewsCoreRegionRegionBase } from "../region.base.directive";
+
+@Component({
+  selector: 'sxplr-sapiviews-core-region-region-list-item',
+  templateUrl: './region.listItem.template.html',
+  styleUrls: [
+    `./region.listItem.style.css`
+  ]
+})
+
+export class SapiViewsCoreRegionRegionListItem extends SapiViewsCoreRegionRegionBase {
+  @Input('sxplr-sapiviews-core-region-region-list-item-ripple')
+  ripple: boolean = false
+}
\ No newline at end of file
diff --git a/src/atlasComponents/sapiViews/core/region/region/listItem/region.listItem.stories.ts b/src/atlasComponents/sapiViews/core/region/region/listItem/region.listItem.stories.ts
new file mode 100644
index 0000000000000000000000000000000000000000..168477a6197b9418f405375288e83b374a728680
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/region/region/listItem/region.listItem.stories.ts
@@ -0,0 +1,122 @@
+import { CommonModule } from "@angular/common"
+import { HttpClientModule } from "@angular/common/http"
+import { Meta, moduleMetadata, Story } from "@storybook/angular"
+import { SAPI } from "src/atlasComponents/sapi"
+import { getHoc1Right, getHumanAtlas, getJba29, getMni152, provideDarkTheme } from "src/atlasComponents/sapi/stories.base"
+import { SapiViewsCoreRegionModule } from "../../module"
+import { SapiViewsCoreRegionRegionListItem } from "./region.listItem.component"
+
+export default {
+  component: SapiViewsCoreRegionRegionListItem,
+  decorators: [
+    moduleMetadata({
+      imports: [
+        CommonModule,
+        HttpClientModule,
+        SapiViewsCoreRegionModule,
+      ],
+      providers: [
+        SAPI,
+        ...provideDarkTheme,
+      ],
+      declarations: []
+    })
+  ],
+} as Meta
+
+const Template: Story<SapiViewsCoreRegionRegionListItem> = (args: SapiViewsCoreRegionRegionListItem, { loaded, parameters }) => {
+  const { 
+    human,
+    mni152,
+    jba29,
+    hoc1left
+  } = loaded
+
+  const { contentProjection } = parameters
+  return ({
+    props: {
+      atlas: human, 
+      template: mni152, 
+      parcellation: jba29, 
+      region:  hoc1left,
+      ripple: true
+    },
+    template: `
+    <sxplr-sapiviews-core-region-region-list-item>
+      ${contentProjection || ''}
+    </sxplr-sapiviews-core-region-region-list-item>
+    `
+  })
+}
+
+const loadRegions = async () => {
+  
+  const human = await getHumanAtlas()
+  const mni152 = await getMni152()
+  const jba29 = await getJba29()
+  const hoc1left = await getHoc1Right(mni152["@id"])
+
+  return {
+    human,
+    mni152,
+    jba29,
+    hoc1left,
+  }
+}
+
+export const HumanMni152Jba29Hoc1Left = Template.bind({})
+HumanMni152Jba29Hoc1Left.args = {
+
+}
+HumanMni152Jba29Hoc1Left.loaders = [
+  async () => {
+    const {
+      human,
+      mni152,
+      jba29,
+      hoc1left,
+    } = await loadRegions()
+    return {
+      human,
+      mni152,
+      jba29,
+      hoc1left,
+    }
+  }
+]
+
+
+const getPrefixSuffix = (prefix: string, suffix: string) => {
+  let returnVal = ``
+  if (prefix) {
+    returnVal += `<div prefix>${prefix}</div>`
+  }
+  if (suffix) {
+    returnVal += `<div suffix>${suffix}</div>`
+  }
+  return returnVal
+}
+
+export const HeaderContentProjectionPrefix = Template.bind({})
+HeaderContentProjectionPrefix.loaders = [
+  ...HumanMni152Jba29Hoc1Left.loaders
+]
+HeaderContentProjectionPrefix.parameters = {
+  contentProjection: getPrefixSuffix(`<i class="far fa-square"></i>`, null)
+}
+
+export const HeaderContentProjectionSuffix = Template.bind({})
+HeaderContentProjectionSuffix.loaders = [
+  ...HumanMni152Jba29Hoc1Left.loaders
+]
+HeaderContentProjectionSuffix.parameters = {
+  contentProjection: getPrefixSuffix(null, `<i class="fab fa-chrome"></i>`)
+}
+
+export const HeaderContentProjectionBox = Template.bind({})
+HeaderContentProjectionBox.loaders = [
+  ...HumanMni152Jba29Hoc1Left.loaders
+]
+HeaderContentProjectionBox.parameters = {
+  contentProjection: getPrefixSuffix(`TEXT`, `<button mat-raised-button>test button</button>`)
+}
diff --git a/src/atlasViewerExports/sampleBox/sampleBox.style.css b/src/atlasComponents/sapiViews/core/region/region/listItem/region.listItem.style.css
similarity index 100%
rename from src/atlasViewerExports/sampleBox/sampleBox.style.css
rename to src/atlasComponents/sapiViews/core/region/region/listItem/region.listItem.style.css
diff --git a/src/atlasComponents/sapiViews/core/region/region/listItem/region.listItem.template.html b/src/atlasComponents/sapiViews/core/region/region/listItem/region.listItem.template.html
new file mode 100644
index 0000000000000000000000000000000000000000..4c833e9218409ff1ed338d4cc781dc852742fd52
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/region/region/listItem/region.listItem.template.html
@@ -0,0 +1,7 @@
+<div *ngIf="region" matRipple [matRippleDisabled]="!ripple" class="sxplr-d-inline-flex">
+  <ng-content select="[prefix]"></ng-content>
+  <span class="mat-body">
+    {{ region.name }}
+  </span>
+  <ng-content select="[suffix]"></ng-content>
+</div>
\ No newline at end of file
diff --git a/src/atlasComponents/sapiViews/core/region/region/region.base.directive.ts b/src/atlasComponents/sapiViews/core/region/region/region.base.directive.ts
new file mode 100644
index 0000000000000000000000000000000000000000..96f374b267ced1fb700333654b7e62a39a9e311a
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/region/region/region.base.directive.ts
@@ -0,0 +1,109 @@
+import { Directive, EventEmitter, Input, OnDestroy, Output } from "@angular/core";
+import { SapiAtlasModel, SapiParcellationModel, SapiRegionModel, SapiSpaceModel } from "src/atlasComponents/sapi";
+import { rgbToHsl } from 'common/util'
+import { SAPI } from "src/atlasComponents/sapi/sapi.service";
+import { BehaviorSubject, Subject } from "rxjs";
+import { SAPIRegion } from "src/atlasComponents/sapi/core";
+
+@Directive({
+  selector: `[sxplr-sapiviews-core-region]`,
+  exportAs: "sapiViewsCoreRegion"
+})
+export class SapiViewsCoreRegionRegionBase {
+
+  @Input('sxplr-sapiviews-core-region-detail-flag')
+  shouldFetchDetail = false
+
+  public fetchInProgress$ = new BehaviorSubject<boolean>(false)
+
+  @Input('sxplr-sapiviews-core-region-atlas')
+  atlas: SapiAtlasModel
+  @Input('sxplr-sapiviews-core-region-template')
+  template: SapiSpaceModel
+  @Input('sxplr-sapiviews-core-region-parcellation')
+  parcellation: SapiParcellationModel
+
+  @Output('sxplr-sapiviews-core-region-navigate-to')
+  onNavigateTo = new EventEmitter<number[]>()
+
+  protected region$ = new Subject<SapiRegionModel>()
+  private _region: SapiRegionModel
+  @Input('sxplr-sapiviews-core-region-region')
+  set region(val: SapiRegionModel) {
+    
+    this.region$.next(val)
+
+    if (!this.shouldFetchDetail || !val) {
+      this._region = val
+      this.setupRegionDarkmode()
+      return
+    }
+    this.fetchInProgress$.next(true)
+    this._region = null
+    
+    this.fetchDetail(val)
+      .then(r => {
+        this._region = r
+      })
+      .catch(e => {
+        console.warn(`populating detail failed.`, e)
+        this._region = val
+      })
+      .finally(() => {
+        this.fetchInProgress$.next(false)
+        this.setupRegionDarkmode()
+      })
+  }
+  get region(){
+    return this._region
+  }
+
+  regionRgbString: string = `rgb(200, 200, 200)`
+  regionDarkmode = false
+  // in mm!!
+  regionPosition: number[] = null
+  dois: string[] = []
+
+  protected setupRegionDarkmode(){
+
+    this.regionRgbString = `rgb(200, 200, 200)`
+    this.regionDarkmode = false
+    this.regionPosition = null
+    this.dois = []
+
+    if (this.region) {
+
+      /**
+       * color
+       */
+      const rgb = SAPIRegion.GetDisplayColor(this.region)
+      this.regionRgbString = `rgb(${rgb.join(',')})`
+      const [_h, _s, l] = rgbToHsl(...rgb)
+      this.regionDarkmode = l < 0.4
+      
+      /**
+       * position
+       */
+      this.regionPosition = this.region.hasAnnotation?.bestViewPoint?.coordinates.map(v => v.value)
+
+      /**
+       * dois
+       */
+      this.dois = (this.region.hasAnnotation?.inspiredBy || [])
+        .map(insp => insp["@id"] as string)
+        .filter(id => /^https?:\/\/doi\.org/.test(id))
+    }
+  }
+
+  navigateTo(position: number[]) {
+    this.onNavigateTo.emit(position.map(v => v*1e6))
+  }
+
+  protected async fetchDetail(region: SapiRegionModel) {
+    return this.sapi.getRegion(this.atlas["@id"],this.parcellation["@id"], region.name).getDetail(this.template["@id"]).toPromise()
+  }
+
+  constructor(protected sapi: SAPI){
+
+  }
+}
diff --git a/src/atlasComponents/sapiViews/core/region/region/region.features.directive.ts b/src/atlasComponents/sapiViews/core/region/region/region.features.directive.ts
new file mode 100644
index 0000000000000000000000000000000000000000..47a4b6912c7a701b116fae6e13c0591c4005ab19
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/region/region/region.features.directive.ts
@@ -0,0 +1,51 @@
+import { Directive, OnChanges, SimpleChanges } from "@angular/core";
+import { BehaviorSubject, Observable } from "rxjs";
+import { switchMap,  filter, startWith, shareReplay, finalize } from "rxjs/operators";
+import { SAPI, SapiAtlasModel, SapiParcellationModel, SapiRegionalFeatureModel, SapiRegionModel, SapiSpaceModel } from "src/atlasComponents/sapi";
+import { SxplrCleanedFeatureModel } from "src/atlasComponents/sapi/type";
+import { SapiViewsCoreRegionRegionBase } from "./region.base.directive";
+
+@Directive({
+  selector: '[sxplr-sapiviews-core-region-regional-feature]',
+  exportAs: 'sapiViewsRegionalFeature'
+})
+
+export class SapiViewsCoreRegionRegionalFeatureDirective extends SapiViewsCoreRegionRegionBase implements OnChanges{
+
+  private ATPR$ = new BehaviorSubject<{
+    atlas: SapiAtlasModel
+    template: SapiSpaceModel
+    parcellation: SapiParcellationModel
+    region: SapiRegionModel
+  }>(null)
+
+  ngOnChanges(sc: SimpleChanges): void {
+    const { atlas, template, parcellation, region } = this
+    this.ATPR$.next({ atlas, template, parcellation, region })
+  }
+
+  constructor(sapi: SAPI){
+    super(sapi)
+  }
+
+  private features$: Observable<(SapiRegionalFeatureModel|SxplrCleanedFeatureModel)[]> = this.ATPR$.pipe(
+    filter(arg => {
+      if (!arg) return false
+      const { atlas, parcellation, region, template } = arg
+      return !!atlas && !!parcellation && !!region && !!template 
+    }),
+    switchMap(({ atlas, parcellation, region, template }) => {
+      this.busy$.next(true)
+      return this.sapi.getRegionFeatures(atlas["@id"], parcellation["@id"], template["@id"], region.name).pipe(
+        finalize(() => this.busy$.next(false))
+      )
+    }),
+  )
+
+  public listOfFeatures$: Observable<(SapiRegionalFeatureModel|SxplrCleanedFeatureModel)[]> = this.features$.pipe(
+    startWith([]),
+    shareReplay(1),
+  )
+
+  public busy$ = new BehaviorSubject<boolean>(false)
+}
diff --git a/src/atlasComponents/sapiViews/core/region/region/rich/region.rich.component.ts b/src/atlasComponents/sapiViews/core/region/region/rich/region.rich.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..79e4d693d5b23cd7a380f26c6cc8e5b922bba450
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/region/region/rich/region.rich.component.ts
@@ -0,0 +1,52 @@
+import { Observable, Subject } from "rxjs";
+import { Component, EventEmitter, Inject, Output } from "@angular/core";
+import { DARKTHEME } from "src/util/injectionTokens";
+import { SapiViewsCoreRegionRegionBase } from "../region.base.directive";
+import { ARIA_LABELS, CONST } from 'common/constants'
+import { SapiRegionalFeatureModel } from "src/atlasComponents/sapi";
+import { SAPI } from "src/atlasComponents/sapi/sapi.service";
+
+@Component({
+  selector: 'sxplr-sapiviews-core-region-region-rich',
+  templateUrl: './region.rich.template.html',
+  styleUrls: [
+    `./region.rich.style.css`
+  ],
+  exportAs: "sapiViewsCoreRegionRich"
+})
+
+export class SapiViewsCoreRegionRegionRich extends SapiViewsCoreRegionRegionBase {
+  
+  shouldFetchDetail = true
+  public ARIA_LABELS = ARIA_LABELS
+  public CONST = CONST
+
+  @Output('sxplr-sapiviews-core-region-region-rich-feature-clicked')
+  featureClicked = new EventEmitter<SapiRegionalFeatureModel>()
+
+  public expandedPanel: string
+
+  constructor(
+    sapi: SAPI,
+    @Inject(DARKTHEME) public darktheme$: Observable<boolean>,
+  ){
+    super(sapi)
+  }
+
+  handleRegionalFeatureClicked(feat: SapiRegionalFeatureModel) {
+    this.featureClicked.emit(feat)
+  }
+
+  // eslint-disable-next-line  @typescript-eslint/no-empty-function
+  handleExpansionPanelClosedEv(title: string){
+    this.expandedPanel = null
+  }
+
+  // eslint-disable-next-line  @typescript-eslint/no-empty-function
+  handleExpansionPanelAfterExpandEv(title: string) {
+    this.expandedPanel = title
+  }
+
+  activePanelTitles$: Observable<string[]> = new Subject()
+
+}
diff --git a/src/atlasComponents/sapiViews/core/region/region/rich/region.rich.stories.ts b/src/atlasComponents/sapiViews/core/region/region/rich/region.rich.stories.ts
new file mode 100644
index 0000000000000000000000000000000000000000..662a98325822bcfe069a3f8a4465dfb251e1af56
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/region/region/rich/region.rich.stories.ts
@@ -0,0 +1,121 @@
+import { CommonModule } from "@angular/common"
+import { HttpClientModule } from "@angular/common/http"
+import { Meta, moduleMetadata, Story } from "@storybook/angular"
+import { SAPI } from "src/atlasComponents/sapi"
+import { getHoc1Right, getHumanAtlas, getJba29, getJba29Regions, getMni152, provideDarkTheme } from "src/atlasComponents/sapi/stories.base"
+import { SapiViewsCoreRegionModule } from "../../module"
+import { SapiViewsCoreRegionRegionRich } from "./region.rich.component"
+import { action } from '@storybook/addon-actions';
+import {CUSTOM_ELEMENTS_SCHEMA} from "@angular/core";
+import {AngularMaterialModule} from "src/sharedModules";
+import {provideMockStore} from "@ngrx/store/testing";
+
+const actionsData = {
+  onNavigateTo: action('onNavigateTo'),
+  onHandleRegionalFeatureClicked: action('onHandleRegionalFeatureClicked')
+}
+
+export default {
+  component: SapiViewsCoreRegionRegionRich,
+  decorators: [
+    moduleMetadata({
+      imports: [
+        CommonModule,
+        HttpClientModule,
+        SapiViewsCoreRegionModule,
+        AngularMaterialModule,
+      ],
+      providers: [
+        SAPI,
+        provideMockStore(),
+        ...provideDarkTheme,
+      ],
+      schemas: [
+        CUSTOM_ELEMENTS_SCHEMA,
+      ],
+      declarations: []
+    })
+  ],
+} as Meta
+
+const Template: Story<SapiViewsCoreRegionRegionRich> = (args: SapiViewsCoreRegionRegionRich, { loaded, parameters }) => {
+  const { 
+    human,
+    mni152,
+    jba29,
+    hoc1left
+  } = loaded
+
+  const { contentProjection } = parameters
+  return ({
+    props: {
+      atlas: human, 
+      template: mni152, 
+      parcellation: jba29, 
+      region:  hoc1left,
+      navigateTo: actionsData.onNavigateTo,
+      handleRegionalFeatureClicked: actionsData.onHandleRegionalFeatureClicked
+    },
+    template: `
+    <sxplr-sapiviews-core-region-region-rich>
+      ${contentProjection || ''}
+    </sxplr-sapiviews-core-region-region-rich>
+    `
+  })
+}
+
+const loadRegions = async () => {
+  
+  const human = await getHumanAtlas()
+  const mni152 = await getMni152()
+  const jba29 = await getJba29()
+  const hoc1left = await getHoc1Right(mni152["@id"])
+  return {
+    human,
+    mni152,
+    jba29,
+    hoc1left,
+  }
+}
+
+export const HumanMni152Jba29Hoc1Left = Template.bind({})
+HumanMni152Jba29Hoc1Left.args = {
+
+}
+HumanMni152Jba29Hoc1Left.loaders = [
+  async () => {
+    const {
+      human,
+      mni152,
+      jba29,
+      hoc1left,
+    } = await loadRegions()
+    return {
+      human,
+      mni152,
+      jba29,
+      hoc1left,
+    }
+  }
+]
+
+
+export const HeaderContentProjection = Template.bind({})
+HeaderContentProjection.loaders = [
+  ...HumanMni152Jba29Hoc1Left.loaders
+]
+HeaderContentProjection.parameters = {
+  contentProjection: `<div header>HEADER CONTENT PROJECTED</div>`
+}
+
+export const InjectionSimpleRegion = Template.bind({})
+InjectionSimpleRegion.loaders = [
+  ...HumanMni152Jba29Hoc1Left.loaders,
+  async () => {
+    const regions = await getJba29Regions()
+    const hoc1left = regions.find(r => /left/i.test(r.name) && /hoc1/i.test(r.name))
+    return {
+      hoc1left
+    }
+  }
+]
diff --git a/src/atlasComponents/sapiViews/core/region/region/rich/region.rich.style.css b/src/atlasComponents/sapiViews/core/region/region/rich/region.rich.style.css
new file mode 100644
index 0000000000000000000000000000000000000000..a897b1ea9ca28e94e43c28f32593fa18fa3426d9
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/region/region/rich/region.rich.style.css
@@ -0,0 +1,12 @@
+.vanishing-border
+{
+  padding: 16px;
+  margin: -16px!important;
+}
+
+.feature-list-container
+{
+  max-height: 16rem;
+  overflow-x: hidden;
+  overflow-y: scroll;
+}
\ No newline at end of file
diff --git a/src/atlasComponents/sapiViews/core/region/region/rich/region.rich.template.html b/src/atlasComponents/sapiViews/core/region/region/rich/region.rich.template.html
new file mode 100644
index 0000000000000000000000000000000000000000..d4a9a79d17698288b49f115c83d895459327bd20
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/region/region/rich/region.rich.template.html
@@ -0,0 +1,187 @@
+<ng-template #headerTmpl>
+  <ng-content select="[header]"></ng-content>
+</ng-template>
+
+<ng-template [ngIf]="region">
+
+  <mat-card class="mat-elevation-z4">
+    <div
+      [style.backgroundColor]="regionRgbString"
+      class="vanishing-border"
+      [ngClass]="{
+        'darktheme': regionDarkmode === true,
+        'lighttheme': regionDarkmode === false
+      }">
+
+      <ng-template [ngTemplateOutlet]="headerTmpl"></ng-template>
+
+      <mat-card-title class="sxplr-custom-cmp text">
+        {{ region.name }}
+      </mat-card-title>
+
+
+      <!-- subtitle on what it is -->
+      <mat-card-subtitle class="d-inline-flex align-items-center flex-wrap">
+        <mat-icon fontSet="fas" fontIcon="fa-brain"></mat-icon>
+        <span>
+          Brain region
+        </span>
+
+        <!-- origin datas format -->
+
+        <mat-divider vertical="true" class="sxplr-pl-2 h-2rem"></mat-divider>
+
+        <!-- position -->
+        <button mat-icon-button *ngIf="regionPosition"
+          (click)="navigateTo(regionPosition)"
+          [matTooltip]="ARIA_LABELS.GO_TO_REGION_CENTROID + ': ' + (regionPosition | numbers | addUnitAndJoin : 'mm')">
+          <mat-icon fontSet="fas" fontIcon="fa-map-marked-alt">
+          </mat-icon>
+        </button>
+
+        <!-- explore doi -->
+        <a *ngFor="let doi of dois"
+          [href]="doi | parseDoi"
+          [matTooltip]="ARIA_LABELS.EXPLORE_DATASET_IN_KG"
+          target="_blank"
+          mat-icon-button>
+          <i class="fas fa-external-link-alt"></i>
+        </a>
+
+      </mat-card-subtitle>
+
+    </div>
+  </mat-card>
+
+
+  <!-- kg regional features list -->
+  <ng-template #kgRegionalFeatureList>
+    <div sxplr-sapiviews-core-region-regional-feature
+      [sxplr-sapiviews-core-region-atlas]="atlas"
+      [sxplr-sapiviews-core-region-template]="template"
+      [sxplr-sapiviews-core-region-parcellation]="parcellation"
+      [sxplr-sapiviews-core-region-region]="region"
+      #rfDir="sapiViewsRegionalFeature"
+      class="feature-list-container"
+      >
+
+      <spinner-cmp *ngIf="rfDir.busy$ | async"></spinner-cmp>
+
+      <sxplr-sapiviews-features-entry-list-item
+        *ngFor="let feat of rfDir.listOfFeatures$ | async | orderFilterFeatures"
+        [sxplr-sapiviews-features-entry-list-item-feature]="feat"
+        (click)="handleRegionalFeatureClicked(feat)">
+      </sxplr-sapiviews-features-entry-list-item>
+    </div>
+    
+  </ng-template>
+
+  <ng-template #regionDesc>
+    <markdown-dom class="sxplr-muted" [markdown]="region?.versionInnovation || 'No description provided.'">
+    </markdown-dom>
+  </ng-template>
+
+  <mat-accordion class="d-block mt-2">
+
+    <ng-container *ngTemplateOutlet="ngMatAccordionTmpl; context: {
+      title: CONST.DESCRIPTION,
+      iconClass: 'fas fa-info',
+      content: regionDesc,
+      desc: '',
+      iconTooltip: 'Description',
+      iavNgIf: !!region?.versionInnovation
+    }">
+
+    </ng-container>
+
+    <ng-container *ngTemplateOutlet="ngMatAccordionTmpl; context: {
+      title: CONST.REGIONAL_FEATURES,
+      iconClass: 'fas fa-database',
+      content: kgRegionalFeatureList,
+      desc: '',
+      iconTooltip: 'Regional Features',
+      iavNgIf: true
+    }">
+    </ng-container>
+
+    <!-- connectivity -->
+    <ng-template #sxplrSapiviewsFeaturesConnectivityBrowser>
+      <sxplr-sapiviews-features-connectivity-browser
+        class="pe-all flex-shrink-1"
+        [region]="region"
+        [types]="hasConnectivityDirective.availableModalities"
+        [defaultProfile]="hasConnectivityDirective.defaultProfile"
+        [sxplr-sapiviews-features-connectivity-browser-atlas]="atlas"
+        [sxplr-sapiviews-features-connectivity-browser-parcellation]="parcellation"
+        [accordionExpanded]="expandedPanel === CONST.CONNECTIVITY"
+      >
+      </sxplr-sapiviews-features-connectivity-browser>
+    </ng-template>
+
+    <ng-container *ngTemplateOutlet="ngMatAccordionTmpl; context: {
+      title: CONST.CONNECTIVITY,
+      iconClass: 'fab fa-connectdevelop',
+      content: sxplrSapiviewsFeaturesConnectivityBrowser,
+      desc: hasConnectivityDirective.connectivityNumber,
+      iconTooltip: hasConnectivityDirective.connectivityNumber + 'Connections',
+      iavNgIf: hasConnectivityDirective.hasConnectivity
+    }">
+    </ng-container>
+
+    <div sxplr-sapiviews-features-connectivity-check
+        [sxplr-sapiviews-features-connectivity-check-atlas]="atlas"
+        [sxplr-sapiviews-features-connectivity-check-parcellation]="parcellation"
+        [region]="region"
+        #hasConnectivityDirective="hasConnectivityDirective">
+    </div>
+
+  </mat-accordion>
+
+</ng-template>
+
+<!-- expansion tmpl -->
+<ng-template #ngMatAccordionTmpl
+  let-title="title"
+  let-desc="desc"
+  let-iconClass="iconClass"
+  let-iconTooltip="iconTooltip"
+  let-iavNgIf="iavNgIf"
+  let-content="content">
+  
+  <mat-expansion-panel
+    [expanded]="activePanelTitles$ | async | includes : title"
+    [attr.data-opened]="expansionPanel.expanded"
+    [attr.data-mat-expansion-title]="title"
+    (closed)="handleExpansionPanelClosedEv(title)"
+    (afterExpand)="handleExpansionPanelAfterExpandEv(title)"
+    hideToggle
+    *ngIf="iavNgIf"
+    #expansionPanel="matExpansionPanel">
+
+    <mat-expansion-panel-header>
+
+      <!-- title -->
+      <mat-panel-title>
+        {{ title }}
+      </mat-panel-title>
+
+      <!-- desc + icon -->
+      <mat-panel-description class="sxplr-d-flex sxplr-align-items-center sxplr-justify-content-end"
+        [matTooltip]="iconTooltip">
+        <span class="mr-3">{{ desc }}</span>
+        <span class="accordion-icon d-inline-flex justify-content-center">
+          <i [class]="iconClass"></i>
+        </span>
+      </mat-panel-description>
+
+    </mat-expansion-panel-header>
+
+    <!-- content -->
+    <ng-template matExpansionPanelContent>
+      <ng-container *ngTemplateOutlet="content; context: {
+        expansionPanel: expansionPanel
+      }">
+      </ng-container>
+    </ng-template>
+  </mat-expansion-panel>
+</ng-template>
diff --git a/src/components/markdown/markdown.style.css b/src/atlasComponents/sapiViews/core/rich/index.ts
similarity index 100%
rename from src/components/markdown/markdown.style.css
rename to src/atlasComponents/sapiViews/core/rich/index.ts
diff --git a/src/atlasComponents/sapiViews/core/rich/module.ts b/src/atlasComponents/sapiViews/core/rich/module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8f946335ee020a073f3ff6f74124360513db6123
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/rich/module.ts
@@ -0,0 +1,34 @@
+import { CommonModule } from "@angular/common";
+import { NgModule } from "@angular/core";
+import { ReactiveFormsModule } from "@angular/forms";
+import { SxplrFlatHierarchyModule } from "src/components/flatHierarchy";
+import { AngularMaterialModule } from "src/sharedModules";
+import { UtilModule } from "src/util";
+import { SapiViewsUtilModule } from "../../util";
+import { SapiViewsCoreRegionModule } from "../region";
+import { HighlightPipe } from "./regionsHierarchy/highlight.pipe";
+import { SapiViewsCoreRichRegionsHierarchy } from "./regionsHierarchy/regionsHierarchy.component";
+import { SapiViewsCoreRichRegionListSearch } from "./regionsListSearch/regionListSearch.component";
+
+@NgModule({
+  imports: [
+    CommonModule,
+    AngularMaterialModule,
+    ReactiveFormsModule,
+    SapiViewsCoreRegionModule,
+    SxplrFlatHierarchyModule,
+    SapiViewsUtilModule,
+    UtilModule,
+  ],
+  declarations: [
+    SapiViewsCoreRichRegionListSearch,
+    SapiViewsCoreRichRegionsHierarchy,
+    HighlightPipe,
+  ],
+  exports: [
+    SapiViewsCoreRichRegionListSearch,
+    SapiViewsCoreRichRegionsHierarchy,
+  ]
+})
+
+export class SapiViewsCoreRichModule{}
\ No newline at end of file
diff --git a/src/atlasComponents/parcellation/regionHierachy/filterNameBySearch.pipe.ts b/src/atlasComponents/sapiViews/core/rich/regionsHierarchy/filterByRegex.pipe.ts
similarity index 77%
rename from src/atlasComponents/parcellation/regionHierachy/filterNameBySearch.pipe.ts
rename to src/atlasComponents/sapiViews/core/rich/regionsHierarchy/filterByRegex.pipe.ts
index f421f0b659d60be9fece6ef87e45b3bb9fb42168..5b1aaa4d71d1c4791cb09d83dbc5b00100523502 100644
--- a/src/atlasComponents/parcellation/regionHierachy/filterNameBySearch.pipe.ts
+++ b/src/atlasComponents/sapiViews/core/rich/regionsHierarchy/filterByRegex.pipe.ts
@@ -1,15 +1,16 @@
 import { Pipe, PipeTransform } from "@angular/core";
 
 @Pipe({
-  name : 'filterNameBySearch',
+  name : 'filterByRegex',
+  pure: true,
 })
 
-export class FilterNameBySearch implements PipeTransform {
+export class FilterByRegexPipe implements PipeTransform {
   public transform(searchFields: string[], searchTerm: string) {
     try {
       return searchFields.some(searchField => new RegExp(searchTerm, 'i').test(searchField))
     } catch (e) {
-      // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions
+      // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping
       // CC0 or MIT
       return searchFields.some(searchField => new RegExp(searchTerm.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).test(searchField))
     }
diff --git a/src/atlasComponents/sapiViews/core/rich/regionsHierarchy/highlight.pipe.ts b/src/atlasComponents/sapiViews/core/rich/regionsHierarchy/highlight.pipe.ts
new file mode 100644
index 0000000000000000000000000000000000000000..73f3e3ddced7b1ab08f3c53e31e25b11f30d93d1
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/rich/regionsHierarchy/highlight.pipe.ts
@@ -0,0 +1,28 @@
+import { Pipe, PipeTransform, SecurityContext } from "@angular/core";
+import { DomSanitizer, SafeHtml } from "@angular/platform-browser";
+
+@Pipe({
+  name: 'hightlightPipe',
+  pure: true
+})
+
+export class HighlightPipe implements PipeTransform {
+  
+  constructor(private sanitizer: DomSanitizer){}
+
+  transform(input: string, highlight: string = ''): SafeHtml {
+    let regex: RegExp
+    if (highlight === '') return input
+    try {
+      regex = new RegExp(highlight, 'i')
+    } catch (e) {
+      // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping
+      // CC0 or MIT
+      regex = new RegExp(highlight.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))
+    }
+    return this.sanitizer.sanitize(
+      SecurityContext.HTML,
+      input.replace(regex, s => `<mark>${s}</mark>`)
+    )
+  }
+}
\ No newline at end of file
diff --git a/src/atlasComponents/sapiViews/core/rich/regionsHierarchy/regionTreeFilter.pipe.ts b/src/atlasComponents/sapiViews/core/rich/regionsHierarchy/regionTreeFilter.pipe.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b125458a82355e482fd9340ca3631d9aa198fded
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/rich/regionsHierarchy/regionTreeFilter.pipe.ts
@@ -0,0 +1,17 @@
+import { Pipe, PipeTransform } from "@angular/core";
+
+@Pipe({
+  name : 'regionTreeFilter',
+  pure: true
+})
+
+export class RegionTreeFilterPipe implements PipeTransform {
+  public transform<T>(array: T[], filterFn: (item: T) => boolean, getChildren: (item: T) => T[]): T[] {
+    const transformSingle = (item: T): boolean =>
+      filterFn(item) || (getChildren(item) || []).some(transformSingle)
+      
+    return array
+      ? array.filter(transformSingle)
+      : []
+  }
+}
diff --git a/src/atlasComponents/sapiViews/core/rich/regionsHierarchy/regionsHierarchy.component.ts b/src/atlasComponents/sapiViews/core/rich/regionsHierarchy/regionsHierarchy.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e47751ef496636389b5e6865da40dee2251066bf
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/rich/regionsHierarchy/regionsHierarchy.component.ts
@@ -0,0 +1,98 @@
+import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, Output, ViewChild } from "@angular/core";
+import { FormControl } from "@angular/forms";
+import { Subscription } from "rxjs";
+import { debounceTime, distinctUntilChanged, filter, startWith } from "rxjs/operators";
+import { SapiRegionModel } from "src/atlasComponents/sapi/type";
+import { SxplrFlatHierarchyTreeView } from "src/components/flatHierarchy/treeView/treeView.component";
+import { FilterByRegexPipe } from "./filterByRegex.pipe";
+import { RegionTreeFilterPipe } from "./regionTreeFilter.pipe";
+
+const regionTreeFilterPipe = new RegionTreeFilterPipe()
+const filterByRegexPipe = new FilterByRegexPipe()
+
+@Component({
+  selector: `sxplr-sapiviews-core-rich-regionshierarchy`,
+  templateUrl: './regionsHierarchy.template.html',
+  styleUrls: [
+    `./regionsHierarchy.style.css`
+  ],
+  changeDetection: ChangeDetectionStrategy.OnPush
+})
+
+export class SapiViewsCoreRichRegionsHierarchy {
+
+  static IsParent(region: SapiRegionModel, parentRegion: SapiRegionModel) {
+    return region.hasParent?.some(parent => parent['@id'] === parentRegion["@id"])
+  }
+
+  static FilterRegions(regions: SapiRegionModel[], searchTerm: string): SapiRegionModel[]{
+    if (searchTerm === '' || !searchTerm) {
+      return regions
+    }
+    return regionTreeFilterPipe.transform(
+      regions,
+      region => filterByRegexPipe.transform([ region.name ], searchTerm),
+      region => regions.filter(child => SapiViewsCoreRichRegionsHierarchy.IsParent(child, region))
+    )
+  }
+
+  @Input('sxplr-sapiviews-core-rich-regionshierarchy-accent-regions')
+  accentedRegions: SapiRegionModel[] = []
+
+  @Input('sxplr-sapiviews-core-rich-regionshierarchy-placeholder')
+  placeholderText: string = 'Search all regions'
+
+  passedRegions: SapiRegionModel[] = []
+
+  private _regions: SapiRegionModel[] = []
+  get regions(){
+    return this._regions
+  }
+  @Input('sxplr-sapiviews-core-rich-regionshierarchy-regions')
+  set regions(val: SapiRegionModel[]){
+    this._regions = val
+    this.passedRegions = SapiViewsCoreRichRegionsHierarchy.FilterRegions(
+      this._regions,
+      this.searchTerm
+    )
+  }
+
+  @Output('sxplr-sapiviews-core-rich-regionshierarchy-region-select')
+  nodeClicked = new EventEmitter<SapiRegionModel>()
+
+  @ViewChild(SxplrFlatHierarchyTreeView)
+  treeView: SxplrFlatHierarchyTreeView<SapiRegionModel>
+
+  isParent = SapiViewsCoreRichRegionsHierarchy.IsParent
+
+  searchFormControl = new FormControl()
+  
+  searchTerm: string
+
+  constructor(
+    private cdr: ChangeDetectorRef
+  ){
+    this.subs.push(
+      this.searchFormControl.valueChanges.pipe(
+        startWith(''),
+        distinctUntilChanged(),
+        debounceTime(320),
+        /**
+         * empty string should trigger search
+         * showing all regions
+         */
+        filter(val => val === '' || !!val)
+      ).subscribe(val => {
+        this.searchTerm = val
+        this.passedRegions = SapiViewsCoreRichRegionsHierarchy.FilterRegions(
+          this._regions,
+          this.searchTerm
+        )
+        this.cdr.markForCheck()
+      })
+    )
+  }
+
+  private subs: Subscription[] = []
+  
+}
diff --git a/src/atlasComponents/sapiViews/core/rich/regionsHierarchy/regionsHierarchy.stories.ts b/src/atlasComponents/sapiViews/core/rich/regionsHierarchy/regionsHierarchy.stories.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8dbfce6e38bba1201cdd7d94adc310c0381cf1de
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/rich/regionsHierarchy/regionsHierarchy.stories.ts
@@ -0,0 +1,141 @@
+import { CommonModule } from "@angular/common"
+import { HttpClientModule } from "@angular/common/http"
+import { Component, EventEmitter, TemplateRef, ViewChild } from "@angular/core"
+import { MatDialog } from "@angular/material/dialog"
+import { action } from "@storybook/addon-actions"
+import { Meta, moduleMetadata, Story } from "@storybook/angular"
+import { atlasId, provideDarkTheme, getParcRegions, getAtlas, getParc } from "src/atlasComponents/sapi/stories.base"
+import { SapiRegionModel } from "src/atlasComponents/sapi/type"
+import { AngularMaterialModule } from "src/sharedModules"
+import { SapiViewsCoreRichModule } from "../module"
+
+@Component({
+  selector: `region-tree-hierarchy-wrapper`,
+  template: `
+  <ng-template #matDialog let-data>
+    <div class="sxplr-w-100 sxplr-h-100">
+      <sxplr-sapiviews-core-rich-regionshierarchy
+        class="sxplr-w-100 sxplr-h-100"
+        [sxplr-sapiviews-core-rich-regionshierarchy-placeholder]="data.placeholder"
+        [sxplr-sapiviews-core-rich-regionshierarchy-regions]="data.regions"
+        (sxplr-sapiviews-core-rich-regionshierarchy-region-select)="nodeClicked.emit($event)"
+        >
+      </sxplr-sapiviews-core-rich-regionshierarchy>
+    </div>
+  </ng-template>
+  <mat-accordion>
+    <mat-expansion-panel *ngFor="let item of regionsDict | keyvalue">
+
+      <mat-expansion-panel-header>
+        {{ item.key }}
+      </mat-expansion-panel-header>
+
+      <ng-template matExpansionPanelContent>
+      <button mat-button
+        (click)="openDialog(moreItem.key, moreItem.value)"
+        *ngFor="let moreItem of item.value | keyvalue">
+        {{ moreItem.key }}
+      </button>
+      </ng-template>
+
+    </mat-expansion-panel>
+  </mat-accordion>
+  `,
+  styles: [
+    ``
+  ]
+})
+
+class RegionTreeHierarchyWrapper {
+  regionsDict: Record<string, Record<string, SapiRegionModel[]>> = {}
+  nodeClicked = new EventEmitter()
+
+  @ViewChild('matDialog', { read: TemplateRef })
+  dialogTmpl: TemplateRef<any>
+
+  openDialog(parcellationName: string, regions: SapiRegionModel[]){
+    this.dialog.open(this.dialogTmpl, {
+      height: '90vh',
+      width: '90vw',
+      data: {
+        placeholder: `Search regions in ${parcellationName} ...`,
+        regions
+      }
+    })
+  }
+
+  constructor(private dialog: MatDialog){
+
+  }
+}
+
+export default {
+  component: RegionTreeHierarchyWrapper,
+  decorators: [
+    moduleMetadata({
+      imports: [
+        CommonModule,
+        HttpClientModule,
+        SapiViewsCoreRichModule,
+        AngularMaterialModule,
+      ],
+      providers: [
+        ...provideDarkTheme,
+      ],
+      declarations: []
+    })
+  ],
+} as Meta
+
+const Template: Story<RegionTreeHierarchyWrapper> = (args: RegionTreeHierarchyWrapper, { loaded, parameters }) => {
+  const { 
+    regionsDict
+  } = loaded
+  
+  const {
+    
+  } = parameters
+
+  const {
+    
+  } = args
+
+  return ({
+    props: {
+      regionsDict,
+      nodeClicked: {
+        emit: action("nodeClicked")
+      }
+    },
+  })
+}
+Template.loaders = []
+
+const asyncLoader = async () => {
+  const regionsDict: Record<string, Record<string, SapiRegionModel[]>> = {}
+  for (const species in atlasId) {
+    const atlasDetail = await getAtlas(atlasId[species])
+    regionsDict[species] = {}
+    
+    for (const parc of atlasDetail.parcellations) {
+      const parcDetail = await getParc(atlasDetail['@id'], parc['@id'])
+      regionsDict[species][parcDetail.name] = await getParcRegions(atlasDetail['@id'], parc['@id'], atlasDetail.spaces[0]["@id"] )
+    }
+  }
+
+  return {
+    regionsDict
+  }
+}
+
+export const Default = Template.bind({})
+Default.loaders = [
+  async () => {
+    const {
+      regionsDict
+    } = await asyncLoader()
+    return {
+      regionsDict
+    }
+  }
+]
diff --git a/src/atlasComponents/sapiViews/core/rich/regionsHierarchy/regionsHierarchy.style.css b/src/atlasComponents/sapiViews/core/rich/regionsHierarchy/regionsHierarchy.style.css
new file mode 100644
index 0000000000000000000000000000000000000000..669b600a34e848c7a12a1c1657f38db8617d7553
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/rich/regionsHierarchy/regionsHierarchy.style.css
@@ -0,0 +1,16 @@
+.region-tmpl
+{
+  text-overflow: clip;
+  white-space: nowrap; 
+}
+
+:host
+{
+  display: flex;
+  flex-direction: column;
+}
+
+sxplr-flat-hierarchy-tree-view
+{
+  flex: 0px 1 1;
+}
diff --git a/src/atlasComponents/sapiViews/core/rich/regionsHierarchy/regionsHierarchy.template.html b/src/atlasComponents/sapiViews/core/rich/regionsHierarchy/regionsHierarchy.template.html
new file mode 100644
index 0000000000000000000000000000000000000000..9c085e07cef508f4de0506fb740705c341616790
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/rich/regionsHierarchy/regionsHierarchy.template.html
@@ -0,0 +1,35 @@
+<form class="sxplr-custom-cmp text sxplr-w-100">
+  <mat-form-field class="sxplr-w-100">
+    <input
+      [placeholder]="placeholderText"
+      type="text"
+      matInput
+      name="searchTerm"
+      [formControl]="searchFormControl"
+      autocomplete="off">
+
+    <!-- search input suffix -->
+    <div matSuffix>
+      <ng-content select="[search-input-suffix]"></ng-content>
+    </div>
+
+  </mat-form-field>
+</form>
+
+<ng-template #tmplRef let-region>
+  <div class="mat-body sxplr-d-flex sxplr-align-items-center sxplr-h-100 region-tmpl"
+    [ngClass]="{
+      'sxplr-custom-cmp accent': accentedRegions | includes : region
+    }"
+    [innerHTML]="region.name | hightlightPipe : searchTerm">
+  </div>
+</ng-template>
+
+<sxplr-flat-hierarchy-tree-view
+  [sxplr-flat-hierarchy-nodes]="passedRegions"
+  [sxplr-flat-hierarchy-is-parent]="isParent"
+  [sxplr-flat-hierarchy-render-node-tmpl]="tmplRef"
+  [sxplr-flat-hierarchy-tree-view-expand-on-init]="true"
+  sxplr-flat-hierarchy-tree-view-lineheight="24"
+  (sxplr-flat-hierarchy-tree-view-node-clicked)="nodeClicked.emit($event)">
+</sxplr-flat-hierarchy-tree-view>
\ No newline at end of file
diff --git a/src/atlasComponents/sapiViews/core/rich/regionsListSearch/regionListSearch.component.ts b/src/atlasComponents/sapiViews/core/rich/regionsListSearch/regionListSearch.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..58be89382e45d671cca1997a32e214c1bf7bb21c
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/rich/regionsListSearch/regionListSearch.component.ts
@@ -0,0 +1,77 @@
+import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output, TemplateRef } from "@angular/core";
+import { SapiRegionModel } from "src/atlasComponents/sapi/type";
+import { ARIA_LABELS } from "common/constants"
+import { FormControl } from "@angular/forms";
+import { debounceTime, distinctUntilChanged, map, startWith } from "rxjs/operators";
+import { MatAutocompleteSelectedEvent } from "@angular/material/autocomplete";
+
+/**
+ * Filter function, which determines whether the region will be included in the list of autocompleted search.
+ * Ideally, only the selectable regions are included in the result.
+ * 
+ * @param region input region
+ * @returns {boolean} whether or not to include the region in the list search
+ */
+const filterRegionForListSearch = (region: SapiRegionModel): boolean => {
+  const visualizedIn = region.hasAnnotation?.visualizedIn
+  return !!visualizedIn
+}
+
+const filterRegionViaSearch = (searchTerm: string) => (region:SapiRegionModel) => {
+  return region.name.toLocaleLowerCase().includes(searchTerm.toLocaleLowerCase())
+}
+
+@Component({
+  selector: `sxplr-sapiviews-core-rich-regionlistsearch`,
+  templateUrl: './regionListSearch.template.html',
+  styleUrls: [
+    `./regionListSearch.style.css`
+  ],
+  changeDetection: ChangeDetectionStrategy.OnPush
+})
+
+export class SapiViewsCoreRichRegionListSearch {
+
+  ARIA_LABELS = ARIA_LABELS
+
+  private _regions: SapiRegionModel[] = []
+  get regions(){
+    return this._regions
+  }
+  @Input('sxplr-sapiviews-core-rich-regionlistsearch-regions')
+  set regions(val: SapiRegionModel[]) {
+    this._regions = val.filter(filterRegionForListSearch)
+  }
+
+  @Input('sxplr-sapiviews-core-rich-regionlistsearch-region-template-ref')
+  regionTemplateRef: TemplateRef<any>
+
+  @Input('sxplr-sapiviews-core-rich-regionlistsearch-current-search')
+  currentSearch: string = ''
+
+  @Output('sxplr-sapiviews-core-rich-regionlistsearch-region-select')
+  onOptionSelected = new EventEmitter<SapiRegionModel>()
+
+  public searchFormControl = new FormControl()
+
+  public autocompleteList$ = this.searchFormControl.valueChanges.pipe(
+    startWith(''),
+    distinctUntilChanged(),
+    debounceTime(160),
+    map((searchTerm: string | SapiRegionModel) => {
+      if (typeof searchTerm === "string") {
+        return this.regions.filter(filterRegionViaSearch(searchTerm)).slice(0,5)
+      }
+      return []
+    })
+  )
+
+  displayFn(region: SapiRegionModel){
+    return region?.name || ''
+  }
+
+  optionSelected(opt: MatAutocompleteSelectedEvent) {
+    const selectedRegion = opt.option.value as SapiRegionModel
+    this.onOptionSelected.emit(selectedRegion)
+  }
+}
diff --git a/src/atlasComponents/sapiViews/core/rich/regionsListSearch/regionListSearch.stories.ts b/src/atlasComponents/sapiViews/core/rich/regionsListSearch/regionListSearch.stories.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2c63c54de2b32ef0bae180e56ab59639bafa0fa6
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/rich/regionsListSearch/regionListSearch.stories.ts
@@ -0,0 +1,139 @@
+import { HttpClientModule } from "@angular/common/http"
+import { Meta, moduleMetadata, Story } from "@storybook/angular"
+import { action } from "@storybook/addon-actions"
+import { SAPI } from "src/atlasComponents/sapi"
+import { atlasId, provideDarkTheme, getParcRegions, parcId, spaceId } from "src/atlasComponents/sapi/stories.base"
+import { SapiViewsCoreRichModule } from "../module"
+import { SapiViewsCoreRichRegionListSearch } from "./regionListSearch.component"
+import { Pipe, PipeTransform, SecurityContext } from "@angular/core"
+import { SapiRegionModel } from "src/atlasComponents/sapi/type"
+import { DomSanitizer, SafeHtml } from "@angular/platform-browser"
+
+@Pipe({
+  name: 'regionTmplPipe',
+  pure: true
+})
+
+class RegionTmplPipe implements PipeTransform {
+  constructor(private sanitizer: DomSanitizer){
+
+  }
+  public transform(region: SapiRegionModel) {
+    console.log(
+      region,
+      (region?.name || '').indexOf("left") >= 0
+    )
+    return this.sanitizer.bypassSecurityTrustHtml(
+      (region?.name || '').includes("left")
+        ? `<i class="fas fa-square"></i>`
+        : `<i class="fas fa-check-square"></i>`
+    )
+  }
+}
+
+export default {
+  component: SapiViewsCoreRichRegionListSearch,
+  decorators: [
+    moduleMetadata({
+      imports: [
+        HttpClientModule,
+        SapiViewsCoreRichModule,
+      ],
+      providers: [
+        SAPI,
+        ...provideDarkTheme,
+      ],
+      declarations: [
+        RegionTmplPipe,
+      ]
+    })
+  ],
+} as Meta
+
+const actionData = {
+  onOptionSelected: { emit: action('onOptionSelected')}
+}
+
+const Template: Story<SapiViewsCoreRichRegionListSearch> = (args: SapiViewsCoreRichRegionListSearch, { loaded, parameters }) => {
+  const { 
+    regions
+  } = loaded
+  const {
+    contentProjection,
+    tmplRef
+  } = parameters
+  const template = `
+  ${tmplRef ? ('<ng-template #tmplref let-region>' + tmplRef + '</ng-template>') : ''}
+  <sxplr-sapiviews-core-rich-regionlistsearch
+    ${tmplRef ? '[sxplr-sapiviews-core-rich-regionlistsearch-region-template-ref]="tmplref"' : ''}
+    >
+    ${contentProjection || ''}
+  </sxplr-sapiviews-core-rich-regionlistsearch>
+  `
+  
+  return ({
+    props: {
+      regions,
+      onOptionSelected: actionData.onOptionSelected
+    },
+    template
+  })
+}
+Template.loaders = []
+
+const asyncLoader = async (atlasId: string, parcId: string, spaceId: string) => {
+  const regions = await getParcRegions(atlasId, parcId, spaceId)
+  return {
+    regions
+  }
+}
+
+const getContentProjection = ({ searchInputSuffix }) => {
+  let returnVal = ''
+  if (searchInputSuffix) {
+    returnVal += `<span search-input-suffix>${searchInputSuffix}</span>`
+  }
+  return returnVal
+}
+
+export const Default = Template.bind({})
+Default.loaders = [
+  async () => {
+    const { regions } = await asyncLoader(atlasId.human, parcId.human.jba29, spaceId.human.mni152)
+    return { regions }
+  }
+]
+
+export const InputSuffix = Template.bind({})
+InputSuffix.loaders = [
+  async () => {
+    const { regions } = await asyncLoader(atlasId.human, parcId.human.jba29, spaceId.human.mni152)
+    return { regions }
+  }
+]
+InputSuffix.parameters = {
+  contentProjection: getContentProjection({
+    searchInputSuffix: `SUFFIX`
+  })
+}
+
+export const TemplatedRegionSuffix = Template.bind({})
+TemplatedRegionSuffix.loaders = [
+  async () => {
+    const { regions } = await asyncLoader(atlasId.human, parcId.human.jba29, spaceId.human.mni152)
+    return { regions }
+  }
+]
+TemplatedRegionSuffix.parameters = {
+  tmplRef: `<span [innerHTML]="region | regionTmplPipe"></span>`
+}
+
+
+export const Waxholm = Template.bind({})
+Waxholm.loaders = [
+  async () => {
+    const { regions } = await asyncLoader(atlasId.rat, parcId.rat.v4, spaceId.rat.waxholm)
+    return { regions }
+  }
+]
+
diff --git a/src/plugin/pluginCsp/pluginCsp.style.css b/src/atlasComponents/sapiViews/core/rich/regionsListSearch/regionListSearch.style.css
similarity index 100%
rename from src/plugin/pluginCsp/pluginCsp.style.css
rename to src/atlasComponents/sapiViews/core/rich/regionsListSearch/regionListSearch.style.css
diff --git a/src/atlasComponents/sapiViews/core/rich/regionsListSearch/regionListSearch.template.html b/src/atlasComponents/sapiViews/core/rich/regionsListSearch/regionListSearch.template.html
new file mode 100644
index 0000000000000000000000000000000000000000..379454ff524357eb9a5717253349a17dac8df6ff
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/rich/regionsListSearch/regionListSearch.template.html
@@ -0,0 +1,52 @@
+<form class="sxplr-custom-cmp text sxplr-w-100">
+  <mat-form-field
+    class="sxplr-w-100"
+    floatLabel="never">
+    <input
+      placeholder="Search for regions"
+      [value]="currentSearch"
+      #trigger="matAutocompleteTrigger"
+      type="text"
+      matInput
+      name="searchTerm"
+      [attr.aria-label]="ARIA_LABELS.TEXT_INPUT_SEARCH_REGION"
+      [formControl]="searchFormControl"
+      [matAutocomplete]="auto">
+
+    <!-- search input suffix -->
+    <div matSuffix iav-stop="click">
+      <ng-content select="[search-input-suffix]"></ng-content>
+    </div>
+
+  </mat-form-field>
+</form>
+
+
+<mat-autocomplete
+  panelWidth="auto"
+  (optionSelected)="optionSelected($event)"
+  autoActiveFirstOption
+  #auto="matAutocomplete"
+  [displayWith]="displayFn">
+  <mat-option
+    *ngFor="let region of autocompleteList$ | async"
+    [value]="region">
+
+    <div class="sxplr-d-flex sxplr-justify-content-space-between">
+
+      <sxplr-sapiviews-core-region-region-list-item
+        [sxplr-sapiviews-core-region-region]="region">
+      </sxplr-sapiviews-core-region-region-list-item>
+      
+      <ng-template
+        [ngIf]="regionTemplateRef"
+        [ngTemplateOutlet]="regionTemplateRef"
+        [ngTemplateOutletContext]="{
+          $implicit: region
+        }">
+        
+      </ng-template>
+    </div>
+
+  </mat-option>
+</mat-autocomplete>
\ No newline at end of file
diff --git a/src/atlasComponents/sapiViews/core/space/boundingBox.directive.ts b/src/atlasComponents/sapiViews/core/space/boundingBox.directive.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3d592f3974c5db9b91fb6172fdf05df516b8ad63
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/space/boundingBox.directive.ts
@@ -0,0 +1,81 @@
+import { Directive, Input, OnChanges, SimpleChanges } from "@angular/core";
+import { BehaviorSubject, Observable } from "rxjs";
+import { distinctUntilChanged } from "rxjs/operators";
+import { BoundingBoxConcept, SapiAtlasModel, SapiSpaceModel } from "src/atlasComponents/sapi/type";
+
+function validateBbox(input: any): boolean {
+  if (!Array.isArray(input)) return false
+  if (input.length !== 2) return false
+  return input.every(el => Array.isArray(el) && el.length === 3 && el.every(val => typeof val === "number"))
+}
+
+@Directive({
+  selector: '[sxplr-sapiviews-core-space-boundingbox]',
+  exportAs: 'sxplrSapiViewsCoreSpaceBoundingBox'
+})
+
+export class SapiViewsCoreSpaceBoundingBox implements OnChanges{
+  @Input('sxplr-sapiviews-core-space-boundingbox-atlas')
+  atlas: SapiAtlasModel
+
+  @Input('sxplr-sapiviews-core-space-boundingbox-space')
+  space: SapiSpaceModel
+
+  private _bbox: BoundingBoxConcept
+  @Input('sxplr-sapiviews-core-space-boundingbox-spec')
+  set bbox(val: string | BoundingBoxConcept ) {
+
+    if (typeof val === "string") {
+      try {
+        const [min, max] = JSON.parse(val)
+        this._bbox = [min, max]
+      } catch (e) {
+        console.warn(`Parse bbox input error`)
+      }
+      return
+    }
+    if (!validateBbox(val)) {
+      // console.warn(`Bbox is not string, and validate error`)
+      return
+    }
+    this._bbox = val
+  }
+  get bbox(): BoundingBoxConcept {
+    return this._bbox
+  }
+
+  private _bbox$: BehaviorSubject<{
+    atlas: SapiAtlasModel
+    space: SapiSpaceModel
+    bbox: BoundingBoxConcept
+  }> = new BehaviorSubject({
+    atlas: null,
+    space: null,
+    bbox: null
+  })
+
+  public bbox$: Observable<{
+    atlas: SapiAtlasModel
+    space: SapiSpaceModel
+    bbox: BoundingBoxConcept
+  }> = this._bbox$.asObservable().pipe(
+    distinctUntilChanged(
+      (prev, curr) => prev.atlas?.["@id"] === curr.atlas?.['@id']
+        && prev.space?.["@id"] === curr.space?.["@id"]
+        && JSON.stringify(prev.bbox) === JSON.stringify(curr.bbox)
+    )
+  )
+
+  ngOnChanges(): void {
+    const {
+      atlas,
+      space,
+      bbox
+    } = this
+    this._bbox$.next({
+      atlas,
+      space,
+      bbox
+    })
+  }
+}
diff --git a/src/atlasComponents/sapiViews/core/space/index.ts b/src/atlasComponents/sapiViews/core/space/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..26c7eed07b1e454d4eac3bcbdf0c77bb9fe4f162
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/space/index.ts
@@ -0,0 +1,4 @@
+export { SapiViewsCoreSpaceModule } from "./module"
+export {
+  SapiViewsCoreSpaceBoundingBox
+} from "./boundingBox.directive"
\ No newline at end of file
diff --git a/src/atlasComponents/sapiViews/core/space/module.ts b/src/atlasComponents/sapiViews/core/space/module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..35b96ce90dd005580407c05411d923986e2ad9d4
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/space/module.ts
@@ -0,0 +1,24 @@
+import { CommonModule } from "@angular/common";
+import { NgModule } from "@angular/core";
+import { ComponentsModule } from "src/components";
+import { SapiViewsCoreSpaceBoundingBox } from "./boundingBox.directive";
+import { PreviewSpaceUrlPipe } from "./previewSpaceUrl.pipe";
+import { SapiViewsCoreSpaceSpaceTile } from "./tile/space.tile.component";
+
+@NgModule({
+  imports: [
+    CommonModule,
+    ComponentsModule,
+  ],
+  declarations: [
+    SapiViewsCoreSpaceSpaceTile,
+    PreviewSpaceUrlPipe,
+    SapiViewsCoreSpaceBoundingBox,
+  ],
+  exports: [
+    SapiViewsCoreSpaceSpaceTile,
+    SapiViewsCoreSpaceBoundingBox,
+  ]
+})
+
+export class SapiViewsCoreSpaceModule{}
\ No newline at end of file
diff --git a/src/atlasComponents/template/getTemplatePreviewUrl.pipe.ts b/src/atlasComponents/sapiViews/core/space/previewSpaceUrl.pipe.ts
similarity index 84%
rename from src/atlasComponents/template/getTemplatePreviewUrl.pipe.ts
rename to src/atlasComponents/sapiViews/core/space/previewSpaceUrl.pipe.ts
index c2071c8af1b686baee241c47608695b7a709667d..8bbecdf23e0cf041912d5293c3be12eef5d5f7b6 100644
--- a/src/atlasComponents/template/getTemplatePreviewUrl.pipe.ts
+++ b/src/atlasComponents/sapiViews/core/space/previewSpaceUrl.pipe.ts
@@ -1,4 +1,5 @@
 import { Pipe, PipeTransform } from "@angular/core"
+import { SapiSpaceModel } from "src/atlasComponents/sapi"
 
 const previewImgMap = new Map([
   ['minds/core/referencespace/v1.0.0/a1655b99-82f1-420f-a3c2-fe80fd4c8588', 'bigbrain.png'],
@@ -18,12 +19,12 @@ const previewImgMap = new Map([
 ])
 
 @Pipe({
-  name: 'getTemplatePreviewUrl',
+  name: 'previewSpaceUrl',
   pure: true
 })
 
-export class GetTemplatePreviewUrlPipe implements PipeTransform{
-  public transform(tile: any){
+export class PreviewSpaceUrlPipe implements PipeTransform{
+  public transform(tile: SapiSpaceModel){
     const filename = previewImgMap.get(tile['@id'])
     return filename && `assets/images/atlas-selection/${filename}`
   }
diff --git a/src/atlasComponents/sapiViews/core/space/tile/space.tile.component.ts b/src/atlasComponents/sapiViews/core/space/tile/space.tile.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e9aaab3bebbdce796155a754857d9b0ff67ce090
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/space/tile/space.tile.component.ts
@@ -0,0 +1,19 @@
+import { Component, Input } from "@angular/core";
+import { SapiSpaceModel } from "src/atlasComponents/sapi";
+
+@Component({
+  selector: `sxplr-sapiviews-core-space-tile`,
+  templateUrl: `./space.tile.template.html`,
+  styleUrls: [
+    `./space.tile.style.css`
+  ]
+})
+
+export class SapiViewsCoreSpaceSpaceTile {
+  @Input('sxplr-sapiviews-core-space-tile-space')
+  space: SapiSpaceModel
+
+  @Input('sxplr-sapiviews-core-space-tile-selected')
+  selected: boolean = false
+
+}
\ No newline at end of file
diff --git a/src/atlasComponents/sapiViews/core/space/tile/space.tile.stories.ts b/src/atlasComponents/sapiViews/core/space/tile/space.tile.stories.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fd385d0f599b21101fdc6021714ad78e921d5178
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/space/tile/space.tile.stories.ts
@@ -0,0 +1,116 @@
+import { CommonModule } from "@angular/common"
+import { HttpClientModule } from "@angular/common/http"
+import { Component, Input } from "@angular/core"
+import { Meta, moduleMetadata, Story } from "@storybook/angular"
+import { SAPI, SapiSpaceModel } from "src/atlasComponents/sapi"
+import { atlasId, spaceId, getAtlas, getSpace, provideDarkTheme } from "src/atlasComponents/sapi/stories.base"
+import { AngularMaterialModule } from "src/sharedModules"
+import { SapiViewsCoreSpaceModule } from "../module"
+import { SapiViewsCoreSpaceSpaceTile } from "./space.tile.component"
+
+@Component({
+  selector: `space-tile-wrapper`,
+  template: `
+  <mat-accordion>
+    <mat-expansion-panel *ngFor="let item of spaces | keyvalue">
+
+      <mat-expansion-panel-header>
+        {{ item.key }}
+      </mat-expansion-panel-header>
+
+      <ng-template matExpansionPanelContent>
+        <div class="sxplr-d-inline-flex align-items-start">
+          <sxplr-sapiviews-core-space-tile
+            *ngFor="let spc of item.value"
+            [sxplr-sapiviews-core-space-tile-space]="spc"
+            [sxplr-sapiviews-core-space-tile-selected]="spc['@id'] === selected"
+            class="sxplr-m-2">
+          </sxplr-sapiviews-core-space-tile>
+        </div>
+      </ng-template>
+
+    </mat-expansion-panel>
+  </mat-accordion>
+  `,
+  styles: [
+    `sxplr-sapiviews-core-space-tile { display: inline-block; max-width: 8rem; }`
+  ]
+})
+
+class SpaceTileWrapper{
+  @Input()
+  spaces: Record<string, SapiSpaceModel[]> = {}
+
+  @Input()
+  selected: string = spaceId.human.mni152
+}
+
+export default {
+  component: SpaceTileWrapper,
+  decorators: [
+    moduleMetadata({
+      imports: [
+        CommonModule,
+        HttpClientModule,
+        SapiViewsCoreSpaceModule,
+        AngularMaterialModule,
+      ],
+      providers: [
+        SAPI,
+        ...provideDarkTheme,
+      ],
+      declarations: [
+        SpaceTileWrapper
+      ]
+    })
+  ],
+} as Meta
+
+const Template: Story<SapiViewsCoreSpaceSpaceTile> = (args: SapiViewsCoreSpaceSpaceTile, { loaded }) => {
+  const { 
+    spaces
+  } = loaded
+
+  return ({
+    props: {
+      ...args,
+      spaces
+    }
+  })
+}
+Template.loaders = [
+
+]
+
+const asyncLoader = async () => {
+  const spaces: Record<string, SapiSpaceModel[]> = {}
+  for (const species in atlasId) {
+    const atlasDetail = await getAtlas(atlasId[species])
+    spaces[species] = []
+    
+    for (const spc of atlasDetail.spaces) {
+      const spcDetail = await getSpace(atlasDetail['@id'], spc['@id'])
+      spaces[species].push(spcDetail)
+    }
+  }
+
+  return {
+    spaces
+  }
+}
+
+export const Default = Template.bind({})
+Default.args = {
+  selected: spaceId.human.mni152
+}
+Default.loaders = [
+
+  async () => {
+    const {
+      spaces
+    } = await asyncLoader()
+    return {
+      spaces
+    }
+  }
+]
diff --git a/src/plugin/pluginUnit/pluginUnit.template.html b/src/atlasComponents/sapiViews/core/space/tile/space.tile.style.css
similarity index 100%
rename from src/plugin/pluginUnit/pluginUnit.template.html
rename to src/atlasComponents/sapiViews/core/space/tile/space.tile.style.css
diff --git a/src/atlasComponents/sapiViews/core/space/tile/space.tile.template.html b/src/atlasComponents/sapiViews/core/space/tile/space.tile.template.html
new file mode 100644
index 0000000000000000000000000000000000000000..74ba27c9a7384a0f470b083f4aa5a1df4489401a
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/space/tile/space.tile.template.html
@@ -0,0 +1,7 @@
+<tile-cmp
+  *ngIf="space"
+  [tile-text]="space.fullName"
+  [tile-image-src]="space | previewSpaceUrl"
+  [tile-selected]="selected">
+
+</tile-cmp>
\ No newline at end of file
diff --git a/src/atlasComponents/connectivity/connectivityBrowser/connectivityBrowser.component.spec.ts b/src/atlasComponents/sapiViews/features/connectivity/connectivityBrowser/connectivityBrowser.component.spec.ts
similarity index 66%
rename from src/atlasComponents/connectivity/connectivityBrowser/connectivityBrowser.component.spec.ts
rename to src/atlasComponents/sapiViews/features/connectivity/connectivityBrowser/connectivityBrowser.component.spec.ts
index 6a7449787611327577ac114c5060b7abffb6a9d8..3889c4c3f20621280a66f92f3e37f2a4521414d0 100644
--- a/src/atlasComponents/connectivity/connectivityBrowser/connectivityBrowser.component.spec.ts
+++ b/src/atlasComponents/sapiViews/features/connectivity/connectivityBrowser/connectivityBrowser.component.spec.ts
@@ -6,9 +6,9 @@ import {CUSTOM_ELEMENTS_SCHEMA, Directive, Input} from "@angular/core";
 import {provideMockActions} from "@ngrx/effects/testing";
 import {MockStore, provideMockStore} from "@ngrx/store/testing";
 import {Observable, of} from "rxjs";
-import { viewerStateAllRegionsFlattenedRegionSelector, viewerStateOverwrittenColorMapSelector } from "src/services/state/viewerState/selectors";
-import { ngViewerSelectorClearViewEntries } from "src/services/state/ngViewerState.store.helper";
 import {BS_ENDPOINT} from "src/util/constants";
+import {SAPI} from "src/atlasComponents/sapi";
+import {AngularMaterialModule} from "src/sharedModules";
 
 /**
  * injecting databrowser module is bad idea
@@ -41,17 +41,19 @@ describe('ConnectivityComponent', () => {
 
     let datasetList = [
         {
-            ['@id']: 'id1',
-            src_name: 'id1',
-            src_info: 'd1',
+            '@id': 'id1',
+            name: 'id1',
+            description: 'd1',
             kgId: 'kgId1',
-            kgschema: 'kgschema1'
+            kgschema: 'kgschema1',
+            items: []
         }, {
-            ['@id']: 'id2',
-            src_name: 'id2',
-            src_info: 'd2',
+            '@id': 'id2',
+            name: 'id2',
+            description: 'd2',
             kgId: 'kgId2',
-            kgschema: 'kgschema2'
+            kgschema: 'kgschema2',
+            items: []
         }
     ]
 
@@ -59,6 +61,7 @@ describe('ConnectivityComponent', () => {
         TestBed.configureTestingModule({
             imports: [
                 HttpClientModule,
+                AngularMaterialModule
             ],
             providers: [
                 provideMockActions(() => actions$),
@@ -66,6 +69,15 @@ describe('ConnectivityComponent', () => {
                 {
                     provide: BS_ENDPOINT,
                     useValue: MOCK_BS_ENDPOINT
+                },
+                {
+                    provide: SAPI,
+                    useValue: {
+                        atlases$: of([]),
+                        getSpaceDetail: jasmine.createSpy('getSpaceDetail'),
+                        getParcDetail: jasmine.createSpy('getParcDetail'),
+                        getParcRegions: jasmine.createSpy('getParcRegions'),
+                    }
                 }
             ],
             declarations: [
@@ -81,9 +93,8 @@ describe('ConnectivityComponent', () => {
 
     beforeEach(() => {
         const mockStore = TestBed.inject(MockStore)
-        mockStore.overrideSelector(viewerStateAllRegionsFlattenedRegionSelector, [])
-        mockStore.overrideSelector(viewerStateOverwrittenColorMapSelector, null)
-        mockStore.overrideSelector(ngViewerSelectorClearViewEntries, [])
+        // mockStore.overrideSelector(viewerStateOverwrittenColorMapSelector, null)
+        // mockStore.overrideSelector(ngViewerSelectorClearViewEntries, [])
     })
 
     it('> component can be created', async () => {
@@ -97,17 +108,15 @@ describe('ConnectivityComponent', () => {
         fixture = TestBed.createComponent(ConnectivityBrowserComponent)
         component = fixture.componentInstance
 
-        component.datasetList = datasetList
-
-        component.changeDataset({value: 'id1'})
+        component.defaultProfile = {selectedDataset: datasetList[0]}
 
-        expect(component.selectedDataset).toEqual('id1')
-        expect(component.selectedDatasetDescription).toEqual('d1')
+        expect(component.selectedDataset['@id']).toEqual('id1')
+        expect(component.selectedDataset.description).toEqual('d1')
 
-        component.changeDataset({value: 'id2'})
+        component.defaultProfile = {selectedDataset: datasetList[1]}
 
-        expect(component.selectedDataset).toEqual('id2')
-        expect(component.selectedDatasetDescription).toEqual('d2')
+        expect(component.selectedDataset['@id']).toEqual('id2')
+        expect(component.selectedDataset.description).toEqual('d2')
     })
 
 });
diff --git a/src/atlasComponents/sapiViews/features/connectivity/connectivityBrowser/connectivityBrowser.component.ts b/src/atlasComponents/sapiViews/features/connectivity/connectivityBrowser/connectivityBrowser.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..23d89f519a537c5a296ba90bd1dfd771bc17a3d5
--- /dev/null
+++ b/src/atlasComponents/sapiViews/features/connectivity/connectivityBrowser/connectivityBrowser.component.ts
@@ -0,0 +1,335 @@
+import {AfterViewInit, Component, ElementRef, OnDestroy, ViewChild, Input, ChangeDetectorRef} from "@angular/core";
+import {select, Store} from "@ngrx/store";
+import {fromEvent, Subscription, BehaviorSubject} from "rxjs";
+import {catchError, take} from "rxjs/operators";
+import {SAPI, SapiAtlasModel, SapiParcellationModel, SapiRegionModel} from "src/atlasComponents/sapi";
+import { atlasAppearance, atlasSelection } from "src/state";
+import {PARSE_TYPEDARRAY} from "src/atlasComponents/sapi/sapi.service";
+import {SapiModalityModel, SapiParcellationFeatureMatrixModel} from "src/atlasComponents/sapi/type";
+import { of } from "rxjs";
+import {CustomLayer} from "src/state/atlasAppearance";
+
+@Component({
+  selector: 'sxplr-sapiviews-features-connectivity-browser',
+  templateUrl: './connectivityBrowser.template.html',
+})
+export class ConnectivityBrowserComponent implements AfterViewInit, OnDestroy {
+
+    @Input('sxplr-sapiviews-features-connectivity-browser-atlas')
+    atlas: SapiAtlasModel
+
+    @Input('sxplr-sapiviews-features-connectivity-browser-parcellation')
+    parcellation: SapiParcellationModel
+
+    /**
+     * accordion expansion should only toggle the clearviewqueue state
+     * which should be the single source of truth
+     * setcolormaps$ is set by the presence/absence of clearviewqueue[CONNECTIVITY_NAME_PLATE]
+     */
+    private _isFirstUpdate = true
+    
+    private accordionIsExpanded = false
+
+    @Input()
+    set accordionExpanded(flag: boolean) {
+      /**
+         * ignore first update
+         */
+      if (this._isFirstUpdate) {
+        this._isFirstUpdate = false
+        return
+      }
+      this.accordionIsExpanded = flag
+
+      if (flag) {
+        if (this.allRegions.length) {
+          this.setCustomLayer()
+        } else {
+          this.setCustomLayerOnLoad = true
+        }
+      } else {
+        this.removeCustomLayer()
+      }
+
+    }
+
+    @Input() types: SapiModalityModel[] = []
+
+    private _defaultProfile
+    @Input() set defaultProfile(val: any) {
+      this._defaultProfile = val
+      this.selectedType = this.types.find(t => t.types.includes(val.type))?.name
+      this.pageNumber = 1
+      this.selectedDataset = this.fixDatasetFormat(val.selectedDataset)
+      if (val.matrix) this.setMatrixData(val.matrix)
+      this.numberOfDatasets = val.numberOfDatasets
+    }
+
+    get defaultProfile() {
+      return this._defaultProfile
+    }
+
+    public selectedType: any
+
+    @Input()
+    set region(val) {
+      const newRegionName = val && val.name
+
+      if (val.status
+          && !val.name.includes('left hemisphere')
+          && !val.name.includes('right hemisphere')) {
+        this.regionHemisphere = val.status
+      }
+
+      this.regionName = newRegionName
+    }
+
+    public regionName: string
+    public regionHemisphere: string = null
+    public selectedDataset: any
+    public connectionsString: string
+    public pureConnections: { [key: string]: number }
+    public connectedAreas: BehaviorSubject<ConnectedArea[]> = new BehaviorSubject([])
+    public noConnectivityForRegion: boolean
+    private subscriptions: Subscription[] = []
+    public allRegions = []
+    private regionIndexInMatrix = -1
+    public defaultColorMap: Map<string, Map<number, { red: number, green: number, blue: number }>>
+    public matrixString: string
+    public fetching: boolean
+    public numberOfDatasets: number
+    public connectivityLayerId = 'connectivity-colormap-id'
+    private setCustomLayerOnLoad = false
+    public pageNumber: number
+    private customLayerEnabled: boolean
+
+    public logDisabled: boolean = true
+    public logChecked: boolean = true
+
+    @ViewChild('connectivityComponent', {read: ElementRef}) public connectivityComponentElement: ElementRef<any>
+    @ViewChild('fullConnectivityGrid') public fullConnectivityGridElement: ElementRef<any>
+
+    constructor(
+        private store$: Store,
+        private sapi: SAPI,
+        private changeDetectionRef: ChangeDetectorRef,
+    ) {}
+
+    public ngAfterViewInit(): void {
+      this.subscriptions.push(
+
+        this.store$.pipe(
+          select(atlasSelection.selectors.selectedParcAllRegions)
+        ).subscribe(flattenedRegions => {
+          this.defaultColorMap = null
+          this.allRegions = flattenedRegions
+          if (this.setCustomLayerOnLoad) {
+            this.setCustomLayer()
+            this.setCustomLayerOnLoad = false
+          }
+        }),
+      )
+
+      this.subscriptions.push(
+        fromEvent(this.connectivityComponentElement?.nativeElement, 'customToolEvent', {capture: true})
+          .subscribe((e: CustomEvent) => {
+            if (e.detail.name === 'export csv') {
+              // ToDo Fix in future to use component
+              const a = document.querySelector('hbp-connectivity-matrix-row');
+              (a as any).downloadCSV()
+            }
+          }),
+        fromEvent(this.connectivityComponentElement?.nativeElement, 'connectedRegionClicked', {capture: true})
+          .subscribe((e: CustomEvent) => {
+            this.navigateToRegion(this.getRegionWithName(e.detail.name))
+          }),
+      )
+    }
+
+    setCustomLayer() {
+      if (this.customLayerEnabled) {
+        this.removeCustomLayer()
+      }
+      const map = new Map<SapiRegionModel, number[]>()
+      const areas = this.connectedAreas.value
+      for (const region of this.allRegions) {
+        const area = areas.find(a => a.name === region.name)
+        if (area) {
+          map.set(region, Object.values(area.color))
+        } else {
+          map.set(region, [255,255,255,0.1])
+        }
+      }
+      this.customLayerEnabled = true
+      const customLayer: CustomLayer = {
+        clType: 'customlayer/colormap',
+        id: this.connectivityLayerId,
+        colormap: map
+      }
+
+      this.store$.dispatch(
+        atlasAppearance.actions.addCustomLayer({customLayer})
+      )
+    }
+
+    removeCustomLayer() {
+      this.store$.dispatch(
+        atlasAppearance.actions.removeCustomLayer({
+          id: this.connectivityLayerId
+        })
+      )
+    }
+
+    selectType(typeName) {
+      this.selectedType = typeName
+      this.pageNumber = 1
+      this.loadDataset()
+    }
+
+    datasetSliderChanged(pageNumber) {
+      this.pageNumber = pageNumber
+      this.loadDataset()
+    }
+
+    loadDataset() {
+      this.fetching = true
+      const type = this.types.find(t => t.name === this.selectedType).types[0]
+      return this.sapi.getParcellation(this.atlas["@id"], this.parcellation["@id"])
+        .getFeatures({type, page: this.pageNumber, size: 1},)
+        .pipe(
+          take(1),
+          catchError(() => {
+            this.fetching = false
+            return of(null)
+          })
+        ).subscribe((res: any) => {
+          if (res && res.items) {
+            if (res.total !== this.numberOfDatasets) {
+              this.numberOfDatasets = res.total
+            }
+            this.selectedDataset = this.fixDatasetFormat(res.items[0])
+            this.fetchConnectivity()
+          }
+        })
+    }
+
+    // ToDo this temporary fix is for the bug existing on siibra api https://github.com/FZJ-INM1-BDA/siibra-api/issues/100
+    private fixDatasetFormat = (ds) =>  ds.name.includes('{')? ({
+      ...ds,
+      name: ds.name.substr(0, ds.name.indexOf('{')),
+      dataset: JSON.parse(ds.name.substring(ds.name.indexOf('{')).replace(/'/g, '"'))
+    }) : ds
+    
+
+    fetchConnectivity() {
+      this.sapi.getParcellation(this.atlas["@id"], this.parcellation["@id"]).getFeatureInstance(this.selectedDataset['@id'])
+        .pipe(catchError(() => {
+          this.fetching = false
+          return of(null)
+        }))
+        .subscribe(ds=> {
+          this.setMatrixData(ds)
+          this.fetching = false
+        })
+    }
+    
+    setMatrixData(data) {
+      const matrixData = data as SapiParcellationFeatureMatrixModel
+      this.regionIndexInMatrix = (matrixData.columns as Array<string>).findIndex(md => md === this.regionName)
+      if (this.regionIndexInMatrix < 0) {
+        this.fetching = false
+        this.noConnectivityForRegion = true
+        this.changeDetectionRef.detectChanges()
+        return
+      } else if (this.noConnectivityForRegion) {
+        this.noConnectivityForRegion = false
+      }
+      this.sapi.processNpArrayData<PARSE_TYPEDARRAY.RAW_ARRAY>(matrixData.matrix, PARSE_TYPEDARRAY.RAW_ARRAY)
+        .then(matrix => {
+          const regionProfile = matrix.rawArray[this.regionIndexInMatrix]
+
+          const maxStrength = Math.max(...regionProfile)  
+          this.logChecked = maxStrength > 1
+          this.logDisabled = maxStrength <= 1
+
+          const areas = regionProfile.reduce((p, c, i) => ({...p, [matrixData.columns[i]]: c}), {})
+          this.pureConnections = areas
+          this.connectionsString = JSON.stringify(areas)
+          this.connectedAreas.next(this.formatConnections(areas))
+          this.setCustomLayer()
+
+          this.matrixString = JSON.stringify(matrixData.columns.map((mc, i) => ([mc, ...matrix.rawArray[i]])))
+          this.changeDetectionRef.detectChanges()
+
+        })
+    }
+
+    
+    changeLog(checked: boolean) {
+      this.logChecked = checked
+      this.connectedAreas.next(this.formatConnections(this.pureConnections))
+      this.connectivityComponentElement.nativeElement.toggleShowLog()
+      this.setCustomLayer()
+    }
+
+    //ToDo navigateRegion action does not work any more
+    navigateToRegion(region: SapiRegionModel) {
+      this.store$.dispatch(
+        atlasSelection.actions.navigateToRegion({
+          region
+        })
+      )
+    }
+
+    getRegionWithName(region: string) {
+      return this.allRegions.find(r => r.name === region)
+    }
+
+    exportConnectivityProfile() {
+      const a = document.querySelector('hbp-connectivity-matrix-row');
+      (a as any).downloadCSV()
+    }
+
+    public exportFullConnectivity() {
+      this.fullConnectivityGridElement?.nativeElement['downloadCSV']()
+    }
+
+    public ngOnDestroy(): void {
+      this.removeCustomLayer()
+      this.subscriptions.forEach(s => s.unsubscribe())
+    }
+
+    private formatConnections(areas: { [key: string]: number }) {
+      const cleanedObj = Object.keys(areas)
+        .map(key => ({name: key, numberOfConnections: areas[key]}))
+        .filter(f => f.numberOfConnections > 0)
+        .sort((a, b) => +b.numberOfConnections - +a.numberOfConnections)
+
+      const logMax = this.logChecked ? Math.log(cleanedObj[0].numberOfConnections) : cleanedObj[0].numberOfConnections
+      const colorAreas = []
+
+      cleanedObj.forEach((a) => {
+        colorAreas.push({
+          ...a,
+          color: {
+            r: this.colormap_red((this.logChecked ? Math.log(a.numberOfConnections) : a.numberOfConnections) / logMax ),
+            g: this.colormap_green((this.logChecked ? Math.log(a.numberOfConnections) : a.numberOfConnections) / logMax ),
+            b: this.colormap_blue((this.logChecked ? Math.log(a.numberOfConnections) : a.numberOfConnections) / logMax )
+          },
+        })
+      })
+      return colorAreas
+    }
+    private clamp = val => Math.round(Math.max(0, Math.min(1.0, val)) * 255)
+    private colormap_red = x => x < 0.7? this.clamp(4.0 * x - 1.5) : this.clamp(-4.0 * x + 4.5)
+    private colormap_green = x => x < 0.5? this.clamp(4.0 * x - 0.5) : this.clamp(-4.0 * x + 3.5)
+    private colormap_blue = x => x < 0.3? this.clamp(4.0 * x + 0.5) : this.clamp(-4.0 * x + 2.5)
+
+}
+
+export type ConnectedArea = {
+    color: {r: number, g: number, b: number}
+    name: string
+    numberOfConnections: number
+}
+
diff --git a/src/atlasComponents/sapiViews/features/connectivity/connectivityBrowser/connectivityBrowser.stories.ts b/src/atlasComponents/sapiViews/features/connectivity/connectivityBrowser/connectivityBrowser.stories.ts
new file mode 100644
index 0000000000000000000000000000000000000000..870492b96ef2ef98bc6657c01e3b78652a23c945
--- /dev/null
+++ b/src/atlasComponents/sapiViews/features/connectivity/connectivityBrowser/connectivityBrowser.stories.ts
@@ -0,0 +1,168 @@
+import { CommonModule } from "@angular/common"
+import { HttpClientModule } from "@angular/common/http"
+import {ChangeDetectorRef, Component, CUSTOM_ELEMENTS_SCHEMA} from "@angular/core"
+import { FormsModule } from "@angular/forms"
+import { BrowserAnimationsModule } from "@angular/platform-browser/animations"
+import { Meta, moduleMetadata, Story } from "@storybook/angular"
+import { SAPI, SapiAtlasModel, SapiParcellationModel } from "src/atlasComponents/sapi"
+import { getJba29Features, getHumanAtlas, getJba29 } from "src/atlasComponents/sapi/stories.base"
+import {SapiParcellationFeatureMatrixModel, SapiParcellationFeatureModel} from "src/atlasComponents/sapi/type"
+import { AngularMaterialModule } from "src/sharedModules"
+import {ConnectivityBrowserComponent} from "src/atlasComponents/sapiViews/features/connectivity";
+import {PARSE_TYPEDARRAY} from "src/atlasComponents/sapi/sapi.service";
+import {catchError, take} from "rxjs/operators"
+import {of} from "rxjs";
+
+@Component({
+  selector: 'autoradiograph-wrapper-cmp',
+  template: `
+    
+  <button mat-button (click)="datasetSliderChanged(1)" class="mb-3">Load Connectivity</button>
+
+  <div class="d-flex">Source: {{regionName}}</div>
+
+    <mat-label>
+        Dataset
+    </mat-label>
+    <mat-slider [min]="1"
+            [max]="numberOfDatasets"
+            (change)="datasetSliderChanged($event.value)"
+            [value]="pageNumber"
+            thumbLabel
+            step="1"
+            class="w-100">
+    </mat-slider>
+
+  <hbp-connectivity-matrix-row
+      #connectivityComponent
+      [region]="regionName"
+      [connections]="connectionsString"
+      showSource="true"
+      theme="light">
+  </hbp-connectivity-matrix-row>
+      
+  `,
+  styles: [
+    `
+    :host
+    {
+      display: block;
+      max-width: 60rem;
+      max-height: 60rem;
+    }
+    `
+  ]
+})
+class ExampleConnectivityBrowserWrapper {
+  atlas: SapiAtlasModel
+  parcellation: SapiParcellationModel
+  features: SapiParcellationFeatureModel[] = []
+  featureId: string
+
+  regionName: string = 'Area TE 3 (STG) right'
+  type: string = 'siibra/features/connectivity/streamlineCounts'
+  pageNumber = 1
+  numberOfDatasets = 1
+  private regionIndexInMatrix = -1
+  public connectionsString: string
+
+
+  constructor(private sapi: SAPI, private cdf: ChangeDetectorRef) {
+  }
+
+  datasetSliderChanged(pageNumber) {
+    this.pageNumber = pageNumber
+    this.loadDataset()
+  }
+
+  loadDataset() {
+    return this.sapi.getParcellation(this.atlas["@id"], this.parcellation["@id"])
+        .getFeatures({type: this.type, page: this.pageNumber, size: 1})
+        .pipe(
+            take(1),
+            catchError(() => {
+              return of(null)
+            })
+        ).subscribe((res: any) => {
+          if (res && res.items) {
+            this.numberOfDatasets = res.total
+            this.featureId = res.items[0]['@id']
+            this.fetchConnectivity()
+          }
+        })
+  }
+
+  fetchConnectivity() {
+    this.sapi.getParcellation(this.atlas["@id"], this.parcellation["@id"]).getFeatureInstance(this.featureId)
+      .pipe(take(1))
+      .subscribe(ds=> {
+        const matrixData = ds as SapiParcellationFeatureMatrixModel
+        this.regionIndexInMatrix =  (matrixData.columns as Array<string>).findIndex(md => md === this.regionName)
+        this.sapi.processNpArrayData<PARSE_TYPEDARRAY.RAW_ARRAY>(matrixData.matrix, PARSE_TYPEDARRAY.RAW_ARRAY)
+          .then(matrix => {
+            const areas = {}
+            matrix.rawArray[this.regionIndexInMatrix].forEach((value, i) => {
+              areas[matrixData.columns[i]] = value
+            })
+            this.connectionsString = JSON.stringify(areas)
+            this.cdf.detectChanges()
+          })
+      })
+  }
+
+
+}
+
+export default {
+  component: ExampleConnectivityBrowserWrapper,
+  decorators: [
+    moduleMetadata({
+      imports: [
+        CommonModule,
+        AngularMaterialModule,
+        HttpClientModule,
+        BrowserAnimationsModule,
+      ],
+      providers: [
+        SAPI
+      ],
+      schemas: [
+        CUSTOM_ELEMENTS_SCHEMA,
+      ],
+      declarations: [
+        ConnectivityBrowserComponent
+      ]
+    })
+  ],
+} as Meta
+
+const Template: Story<ExampleConnectivityBrowserWrapper> = (args: ExampleConnectivityBrowserWrapper, { loaded }) => {
+  const { atlas, parc, features } = loaded
+  return ({
+    props: {
+      ...args,
+      atlas: atlas,
+      parcellation: parc,
+      features
+    },
+  })
+}
+
+Template.loaders = [
+  async () => {
+    const atlas = await getHumanAtlas()
+    const parc = await getJba29()
+    const features = await getJba29Features()
+    return {
+      atlas, parc, features
+    }
+  }
+]
+
+export const Default = Template.bind({})
+Default.args = {
+
+}
+Default.loaders = [
+  ...Template.loaders
+]
diff --git a/src/atlasComponents/sapiViews/features/connectivity/connectivityBrowser/connectivityBrowser.template.html b/src/atlasComponents/sapiViews/features/connectivity/connectivityBrowser/connectivityBrowser.template.html
new file mode 100644
index 0000000000000000000000000000000000000000..724f131a5ebcb37af35bbee1a3c30f74b57d345a
--- /dev/null
+++ b/src/atlasComponents/sapiViews/features/connectivity/connectivityBrowser/connectivityBrowser.template.html
@@ -0,0 +1,76 @@
+<div class="w-100 h-100 d-block d-flex flex-column sxplr-pb-2">
+    <div>
+        <div *ngIf="types && types.length && selectedType" class="flex-grow-0 flex-shrink-0 d-flex flex-row flex-nowrap">
+            <mat-form-field class="flex-grow-1 flex-shrink-1 w-0">
+                <mat-label>
+                    Modality
+                </mat-label>
+
+                <mat-select
+                        [value]="selectedType"
+                        (selectionChange)="selectType($event.value)">
+                    <mat-option
+                            *ngFor="let type of types"
+                            [matTooltip]="type.name"
+                            [value]="type.name">
+                        {{ type.name }}
+                    </mat-option>
+                </mat-select>
+            </mat-form-field>
+        </div>
+
+        <div *ngIf="selectedDataset"  class="flex-grow-0 flex-shrink-0 d-flex flex-nowrap align-items-center">
+            <div class="flex-grow-1 flex-shrink-1 w-0">
+                <mat-label>
+                    Dataset
+                </mat-label>
+                <mat-slider [min]="1"
+                            [max]="numberOfDatasets"
+                            (change)="datasetSliderChanged($event.value)"
+                            [value]="pageNumber"
+                            thumbLabel
+                            step="1"
+                            class="w-100">
+                </mat-slider>
+            </div>
+        </div>
+
+    </div>
+
+    <div class="d-flex justify-content-center">
+        <mat-spinner *ngIf="fetching"></mat-spinner>
+    </div>
+
+    <div class="d-flex align-items-center" *ngIf="regionName && !fetching">
+        <mat-checkbox class="mr-2"
+                      [checked]="logChecked"
+                      (change)="changeLog($event.checked)"
+                      [disabled]="logDisabled || noConnectivityForRegion">Log 10</mat-checkbox>
+        <button mat-button [matMenuTriggerFor]="exportMenu"
+                [disabled]="!connectedAreas.value">
+            <i class="fas fa-download mb-2 mr-2"></i>
+            <label>Export</label>
+        </button>
+    </div>
+
+    <hbp-connectivity-matrix-row
+        #connectivityComponent
+        *ngIf="regionName && !fetching && !noConnectivityForRegion"
+        [region]="regionName + (regionHemisphere? ' - ' + regionHemisphere : '')"
+        [connections]="connectionsString"
+        show-export="true" hide-export-view="true"
+        theme="dark">
+    </hbp-connectivity-matrix-row>
+    <div *ngIf="noConnectivityForRegion">No connectivity for the region.</div>
+    <full-connectivity-grid #fullConnectivityGrid
+                            [matrix]="matrixString"
+                            [datasetName]="selectedDataset?.dataset?.name"
+                            [datasetDescription]="selectedDataset?.dataset?.description"
+                            only-export="true">
+    </full-connectivity-grid>
+
+    <mat-menu #exportMenu="matMenu">
+        <button mat-menu-item [disabled]="noConnectivityForRegion" (click)="exportConnectivityProfile()">Regional</button>
+        <button mat-menu-item (click)="exportFullConnectivity()">Dataset</button>
+    </mat-menu>
+</div>
diff --git a/src/atlasComponents/sapiViews/features/connectivity/hasConnectivity.directive.ts b/src/atlasComponents/sapiViews/features/connectivity/hasConnectivity.directive.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ea99a5b9c4cd5852c2ab7bd4708a7791d2bd5180
--- /dev/null
+++ b/src/atlasComponents/sapiViews/features/connectivity/hasConnectivity.directive.ts
@@ -0,0 +1,134 @@
+import {Directive, Input, OnDestroy} from "@angular/core";
+import {from, of, Subscription} from "rxjs";
+import {map, switchMap, take} from "rxjs/operators";
+import {HttpClient} from "@angular/common/http";
+import {PARSE_TYPEDARRAY, SAPI} from "src/atlasComponents/sapi/sapi.service";
+import {
+  SapiAtlasModel, SapiModalityModel,
+  SapiParcellationFeatureMatrixModel, SapiParcellationFeatureModel,
+  SapiParcellationModel,
+  SapiRegionModel
+} from "src/atlasComponents/sapi/type";
+
+@Directive({
+  selector: '[sxplr-sapiviews-features-connectivity-check]',
+  exportAs: 'hasConnectivityDirective'
+})
+
+export class HasConnectivity implements OnDestroy {
+
+    private subscriptions: Subscription[] = []
+
+    @Input('sxplr-sapiviews-features-connectivity-check-atlas')
+    atlas: SapiAtlasModel
+
+    @Input('sxplr-sapiviews-features-connectivity-check-parcellation')
+    parcellation: SapiParcellationModel
+
+    private _region: SapiRegionModel
+
+    @Input()
+    set region(val: SapiRegionModel) {
+      this._region = val
+      if (val) {
+        if (!this.connectivityModalities.length) {
+          this.waitForModalities = true
+        } else {
+          this.checkConnectivity()
+        }
+      } else {
+        this.connectivityNumber = 0
+      }
+    }
+
+    get region() {
+      return this._region
+    }
+
+    private regionIndex: number
+    public hasConnectivity = false
+    public connectivityNumber = 0
+
+    private connectivityModalities: SapiModalityModel[] = []
+    private waitForModalities = false
+    public defaultProfile: DefaultProfile
+    public availableModalities: SapiModalityModel[] = []
+
+    constructor(private httpClient: HttpClient,
+                private sapi: SAPI) {
+      this.sapi.getModalities()
+        .pipe(map((mod: SapiModalityModel[]) => mod.filter((m: SapiModalityModel) => m.types && m.types.find(t => t.includes('siibra/features/connectivity')))))
+        .subscribe(modalities => {
+          this.connectivityModalities = modalities
+          if (this.waitForModalities) {
+            this.waitForModalities = false
+            this.checkConnectivity()
+          }  
+        })
+    }
+
+    private checkConnectivity() {
+      if (this.region.name) {
+        this.connectivityModalities.forEach(m => {
+          const type = m.types[0]
+          
+          this.sapi.getParcellation(this.atlas["@id"], this.parcellation["@id"])
+            .getFeatures({type, page: 1, size: 1})
+            .pipe(
+              take(1),
+              // ToDo remove any type when `SapiParcellationFeatureModel` will be fixed
+              switchMap((res: SapiParcellationFeatureModel[] | any) => {
+                if (res && res.items) {
+
+                  this.availableModalities.push(m)
+                  const firstDataset = res.items[0]
+
+                  if (firstDataset) {
+                    this.hasConnectivity = true
+                    if (!(this.defaultProfile)) {
+                      return this.sapi.getParcellation(this.atlas["@id"], this.parcellation["@id"])
+                        .getFeatureInstance(firstDataset['@id'])
+                        .pipe(switchMap(inst => {
+                          if (inst) {
+                            this.defaultProfile = {
+                              type,
+                              selectedDataset: firstDataset,
+                              matrix: inst,
+                              numberOfDatasets: res.total
+                            }
+
+                            const matrixData = inst as SapiParcellationFeatureMatrixModel
+                            this.regionIndex = (matrixData.columns as Array<string>).findIndex(md => md === this.region.name)
+                            return from(this.sapi.processNpArrayData<PARSE_TYPEDARRAY.RAW_ARRAY>(matrixData.matrix, PARSE_TYPEDARRAY.RAW_ARRAY))
+                          }
+                          return of(null)
+                        }))
+                    }
+                  } else {
+                    this.hasConnectivity = false
+                    this.connectivityNumber = 0
+                  }
+                }
+                return of(null)
+              }), 
+            ).subscribe(res => {
+              if (res && res.rawArray && res.rawArray[this.regionIndex]) {
+                const connections = res.rawArray[this.regionIndex]
+                this.connectivityNumber = connections.filter(p => p > 0).length
+              }
+            })
+        })
+      }
+    }
+
+    ngOnDestroy(){
+      while (this.subscriptions.length > 0) this.subscriptions.pop().unsubscribe()
+    }
+}
+
+type DefaultProfile = {
+    type: string
+    selectedDataset: SapiParcellationFeatureModel
+    matrix: SapiParcellationFeatureModel
+    numberOfDatasets: number
+}
\ No newline at end of file
diff --git a/src/atlasComponents/connectivity/index.ts b/src/atlasComponents/sapiViews/features/connectivity/index.ts
similarity index 61%
rename from src/atlasComponents/connectivity/index.ts
rename to src/atlasComponents/sapiViews/features/connectivity/index.ts
index c9a2e808fc6290283e77ea4bccff8f199eea3ca0..b86944877b50cac06e6b05935fbcf41ac2a67720 100644
--- a/src/atlasComponents/connectivity/index.ts
+++ b/src/atlasComponents/sapiViews/features/connectivity/index.ts
@@ -1,2 +1,2 @@
 export { ConnectivityBrowserComponent } from "./connectivityBrowser/connectivityBrowser.component";
-export { AtlasCmptConnModule } from "./module";
\ No newline at end of file
+export { SapiViewsFeatureConnectivityModule } from "./module";
diff --git a/src/atlasComponents/sapiViews/features/connectivity/module.ts b/src/atlasComponents/sapiViews/features/connectivity/module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5402b4039439e6ff775507287712095ef0866104
--- /dev/null
+++ b/src/atlasComponents/sapiViews/features/connectivity/module.ts
@@ -0,0 +1,29 @@
+import { CommonModule } from "@angular/common";
+import {CUSTOM_ELEMENTS_SCHEMA, NgModule} from "@angular/core";
+import { SAPI } from "src/atlasComponents/sapi";
+import {ConnectivityBrowserComponent} from "src/atlasComponents/sapiViews/features/connectivity/connectivityBrowser/connectivityBrowser.component";
+import {HasConnectivity} from "src/atlasComponents/sapiViews/features/connectivity/hasConnectivity.directive";
+import {AngularMaterialModule} from "src/sharedModules";
+
+@NgModule({
+  imports: [
+    CommonModule,
+    AngularMaterialModule
+  ],
+  declarations: [
+    ConnectivityBrowserComponent,
+    HasConnectivity
+  ],
+  exports: [
+    ConnectivityBrowserComponent,
+    HasConnectivity
+  ],
+  providers: [
+    SAPI,
+  ],
+  schemas: [
+    CUSTOM_ELEMENTS_SCHEMA,
+  ],
+})
+
+export class SapiViewsFeatureConnectivityModule{}
diff --git a/src/atlasComponents/sapiViews/features/entry/entry.component.ts b/src/atlasComponents/sapiViews/features/entry/entry.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..691af83bd53ab4c5937c283fb3ea93056b917910
--- /dev/null
+++ b/src/atlasComponents/sapiViews/features/entry/entry.component.ts
@@ -0,0 +1,181 @@
+import { Component, Input, OnDestroy } from "@angular/core";
+import { Store } from "@ngrx/store";
+import { TNgAnnotationPoint } from "src/atlasComponents/annotations";
+import { SapiFeatureModel, SapiParcellationModel, SapiRegionModel, SapiSpaceModel, CLEANED_IEEG_DATASET_TYPE } from "src/atlasComponents/sapi";
+import { IeegOnFocusEvent, ContactPoint, Electrode, Session, IeegOnDefocusEvent } from "../ieeg";
+import { atlasSelection, annotation } from "src/state"
+
+@Component({
+  selector: 'sxplr-sapiviews-features-entry',
+  templateUrl: './entry.template.html',
+  styleUrls: [
+    './entry.style.css'
+  ]
+})
+
+export class FeatureEntryCmp implements OnDestroy{
+
+  /**
+   * in future, hopefully feature detail can be queried with just id,
+   * and atlas/space/parcellation/region are no longer necessary
+   */
+  @Input('sxplr-sapiviews-features-entry-atlas')
+  atlas: SapiFeatureModel
+
+  @Input('sxplr-sapiviews-features-entry-space')
+  space: SapiSpaceModel
+
+  @Input('sxplr-sapiviews-features-entry-parcellation')
+  parcellation: SapiParcellationModel
+
+  @Input('sxplr-sapiviews-features-entry-region')
+  region: SapiRegionModel
+
+  @Input('sxplr-sapiviews-features-entry-feature')
+  feature: SapiFeatureModel
+
+  featureType = {
+    receptor: "siibra/features/receptor",
+    ieeg: CLEANED_IEEG_DATASET_TYPE
+  }
+
+  private addedAnnotations: annotation.UnionAnnotation[] = []
+
+  ieegOnFocus(ev: IeegOnFocusEvent){
+    if (ev.contactPoint) {
+      /**
+       * navigate to the point
+       */
+      this.store.dispatch(
+        atlasSelection.actions.navigateTo({
+          navigation: {
+            position: ev.contactPoint.point.coordinates.map(v => v.value * 1e6)
+          },
+          animation: true
+        })
+      )
+      return
+    }
+    if (ev.session) {
+      /**
+       * 
+       */
+      const allInRoiPoints: TNgAnnotationPoint[] = this.getPointsFromSession(ev.session, true)
+      const allNonInRoiPoints: TNgAnnotationPoint[] = this.getPointsFromSession(ev.session, false)
+      const annotationsToBeAdded: annotation.UnionAnnotation[] = []
+      for (const pt of allInRoiPoints) {
+        annotationsToBeAdded.push({
+          "@id": pt.id,
+          color: annotation.AnnotationColor.RED,
+          openminds: {
+            "@id": pt.id,
+            "@type": "https://openminds.ebrains.eu/sands/CoordinatePoint",
+            coordinateSpace: {
+              "@id": this.space["@id"]
+            },
+            coordinates: pt.point.map(v => {
+              return {
+                value: v / 1e6
+              }
+            })
+          },
+          name: pt.description || "Untitled"
+        })
+      }
+      for (const pt of allNonInRoiPoints) {
+        annotationsToBeAdded.push({
+          "@id": pt.id,
+          color: annotation.AnnotationColor.WHITE,
+          openminds: {
+            "@id": pt.id,
+            "@type": "https://openminds.ebrains.eu/sands/CoordinatePoint",
+            coordinateSpace: {
+              "@id": this.space["@id"]
+            },
+            coordinates: pt.point.map(v => {
+              return {
+                value: v / 1e6
+              }
+            })
+          },
+          name: pt.description || "Untitled"
+        })
+      }
+      this.addedAnnotations = annotationsToBeAdded
+      this.store.dispatch(
+        annotation.actions.addAnnotations({
+          annotations: annotationsToBeAdded
+        })
+      )
+    }
+  }
+  ieegOnDefocus(ev: IeegOnDefocusEvent){
+    if (ev.session) {
+      const allInRoiPoints: TNgAnnotationPoint[] = this.getPointsFromSession(ev.session, true)
+      const allNonInRoiPoints: TNgAnnotationPoint[] = this.getPointsFromSession(ev.session, false)
+
+      this.store.dispatch(
+        annotation.actions.rmAnnotations({
+          annotations: [...allInRoiPoints, ...allNonInRoiPoints].map(p => {
+            return { "@id": p.id }
+          })
+        })
+      )
+    }
+  }
+
+  ngOnDestroy(): void {
+    this.store.dispatch(
+      annotation.actions.rmAnnotations({
+        annotations: this.addedAnnotations
+      })
+    )
+  }
+
+  constructor(
+    private store: Store
+  ){
+
+  }
+
+  private getPointsFromSession(session: Session<string>, inRoi: boolean):TNgAnnotationPoint[]{
+    const allPoints: TNgAnnotationPoint[] = []
+    for (const electrodeKey in session.electrodes) {
+      const electrode = session.electrodes[electrodeKey]
+      const points = this.getPointsFromElectrode(electrode, inRoi)
+      allPoints.push(...points)
+    }
+    return allPoints.map(pt => {
+      return {
+        ...pt,
+        id: `${session.sub_id}:${pt.id}`
+      }
+    })
+  }
+
+  private getPointsFromElectrode(electrode: Electrode<string>, inRoi: boolean): TNgAnnotationPoint[] {
+    const allPoints: TNgAnnotationPoint[] = []
+    for (const ctptKey in electrode.contact_points) {
+      const ctpt = electrode.contact_points[ctptKey]
+      if (!inRoi !== !ctpt.inRoi) {
+        continue
+      }
+      const point = this.getPointFromCtPt(ctpt)
+      allPoints.push(point)
+    }
+    return allPoints.map(pt => {
+      return {
+        ...pt,
+        id: `${electrode.electrode_id}:${pt.id}`
+      }
+    })
+  }
+
+  private getPointFromCtPt(ctpt: ContactPoint<string>): TNgAnnotationPoint {
+    return {
+      id: ctpt.id,
+      point: ctpt.point.coordinates.map(coord => coord.value * 1e6 ) as [number, number, number],
+      type: 'point'
+    }
+  }
+}
diff --git a/src/services/state/uiState.store.spec.ts b/src/atlasComponents/sapiViews/features/entry/entry.style.css
similarity index 100%
rename from src/services/state/uiState.store.spec.ts
rename to src/atlasComponents/sapiViews/features/entry/entry.style.css
diff --git a/src/atlasComponents/sapiViews/features/entry/entry.template.html b/src/atlasComponents/sapiViews/features/entry/entry.template.html
new file mode 100644
index 0000000000000000000000000000000000000000..38a00def6eba0d9546e5c729431e4d53a05292bb
--- /dev/null
+++ b/src/atlasComponents/sapiViews/features/entry/entry.template.html
@@ -0,0 +1,22 @@
+<div [ngSwitch]="feature?.['@type']">
+  <sxplr-sapiviews-features-receptor-entry
+    *ngSwitchCase="featureType.receptor"
+    [sxplr-sapiviews-features-receptor-atlas]="atlas"
+    [sxplr-sapiviews-features-receptor-parcellation]="parcellation"
+    [sxplr-sapiviews-features-receptor-template]="space"
+    [sxplr-sapiviews-features-receptor-region]="region"
+    [sxplr-sapiviews-features-receptor-featureid]="feature['@id']">
+  </sxplr-sapiviews-features-receptor-entry>
+  <sxplr-sapiviews-features-ieeg-ieegdataset
+    *ngSwitchCase="featureType.ieeg"  
+    [sxplr-sapiviews-features-ieeg-ieegdataset-atlas]="atlas"
+    [sxplr-sapiviews-features-ieeg-ieegdataset-space]="space"
+    [sxplr-sapiviews-features-ieeg-ieegdataset-parcellation]="parcellation"
+    [sxplr-sapiviews-features-ieeg-ieegdataset-region]="region"
+    [sxplr-sapiviews-features-ieeg-ieegdataset-feature]="feature"
+    (sxplr-sapiviews-features-ieeg-ieegdataset-on-focus)="ieegOnFocus($event)"
+    (sxplr-sapiviews-features-ieeg-ieegdataset-on-defocus)="ieegOnDefocus($event)"
+  >
+
+  </sxplr-sapiviews-features-ieeg-ieegdataset>
+</div>
diff --git a/src/atlasComponents/sapiViews/features/entryListItem/entryListItem.component.ts b/src/atlasComponents/sapiViews/features/entryListItem/entryListItem.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8008affe34a6b783fd1b93ed14ee527279e36d04
--- /dev/null
+++ b/src/atlasComponents/sapiViews/features/entryListItem/entryListItem.component.ts
@@ -0,0 +1,45 @@
+import { ChangeDetectionStrategy, Component, Input } from "@angular/core";
+import { SapiFeatureModel } from "src/atlasComponents/sapi";
+import { CleanedIeegDataset, CLEANED_IEEG_DATASET_TYPE, SapiDatasetModel, SapiParcellationFeatureMatrixModel, SapiRegionalFeatureReceptorModel, SapiSerializationErrorModel, SapiVOIDataResponse, SxplrCleanedFeatureModel } from "src/atlasComponents/sapi/type";
+
+@Component({
+  selector: `sxplr-sapiviews-features-entry-list-item`,
+  templateUrl: `./entryListItem.template.html`,
+  styleUrls: [
+    `./entryListItem.style.css`
+  ],
+  changeDetection: ChangeDetectionStrategy.OnPush,
+})
+
+export class SapiViewsFeaturesEntryListItem{
+  @Input('sxplr-sapiviews-features-entry-list-item-feature')
+  feature: SapiFeatureModel | SxplrCleanedFeatureModel
+
+  @Input('sxplr-sapiviews-features-entry-list-item-ripple')
+  ripple = true
+
+  get label(): string{
+    if (!this.feature) return null
+    const { '@type': type } = this.feature
+    if (
+      type === "https://openminds.ebrains.eu/core/DatasetVersion" ||
+      type === "siibra/features/cells" ||
+      type === "siibra/features/receptor" ||
+      type === "siibra/features/voi" ||
+      type === CLEANED_IEEG_DATASET_TYPE
+    ) {
+      return (this.feature as (SapiDatasetModel | SapiRegionalFeatureReceptorModel | SapiVOIDataResponse | CleanedIeegDataset) ).metadata.fullName
+    }
+
+    if (
+      type === "siibra/features/connectivity" ||
+      type === "siibra/features/connectivity/streamlineCounts"
+    ) {
+      return (this.feature as SapiParcellationFeatureMatrixModel).name
+    }
+    if (type === "spy/serialization-error") {
+      return (this.feature as SapiSerializationErrorModel).message
+    }
+    return "Unknown type"
+  }
+}
diff --git a/src/atlasComponents/sapiViews/features/entryListItem/entryListItem.style.css b/src/atlasComponents/sapiViews/features/entryListItem/entryListItem.style.css
new file mode 100644
index 0000000000000000000000000000000000000000..58f6c43f50f172f58c7933747209ca04ea5530a2
--- /dev/null
+++ b/src/atlasComponents/sapiViews/features/entryListItem/entryListItem.style.css
@@ -0,0 +1,5 @@
+:host span
+{
+  overflow: hidden;
+  white-space: nowrap;
+}
diff --git a/src/atlasComponents/sapiViews/features/entryListItem/entryListItem.template.html b/src/atlasComponents/sapiViews/features/entryListItem/entryListItem.template.html
new file mode 100644
index 0000000000000000000000000000000000000000..76f584328b3e46b5a240defb44e98e7cc2abe4a2
--- /dev/null
+++ b/src/atlasComponents/sapiViews/features/entryListItem/entryListItem.template.html
@@ -0,0 +1,17 @@
+<div matRipple [matRippleDisabled]="!ripple"
+  class="sxplr-p-2">
+
+  <mat-chip-list
+    *ngIf="feature | featureBadgeFlag"
+    class="sxplr-scale-80 transform-origin-left-center">
+    <mat-chip
+      [color]="feature | featureBadgeColour"
+      selected>
+      {{ feature | featureBadgeName }}
+    </mat-chip>
+  </mat-chip-list>
+
+  <span class="d-block mat-body">
+    {{ label }}
+  </span>
+</div>
diff --git a/src/atlasComponents/sapiViews/features/entryListItem/entryListitem.stories.ts b/src/atlasComponents/sapiViews/features/entryListItem/entryListitem.stories.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a93cd9816a00d9f29567b8a55ecfe481030e8c31
--- /dev/null
+++ b/src/atlasComponents/sapiViews/features/entryListItem/entryListitem.stories.ts
@@ -0,0 +1,88 @@
+import { CommonModule } from "@angular/common"
+import { Meta, moduleMetadata, Story } from "@storybook/angular"
+import { SapiFeatureModel } from "src/atlasComponents/sapi"
+import { getHoc1RightFeatures, getHoc1RightSpatialFeatures, getJba29Features, provideDarkTheme } from "src/atlasComponents/sapi/stories.base"
+import { AngularMaterialModule } from "src/sharedModules"
+import { SapiViewsFeaturesEntryListItem } from "./entryListItem.component"
+import { Component, EventEmitter, Input, Output } from "@angular/core"
+import { SapiViewsFeaturesModule } from ".."
+
+@Component({
+  selector: `feature-list-item-wrapper`,
+  template: `
+  <mat-card>
+    <sxplr-sapiviews-features-entry-list-item
+      *ngFor="let feat of features"
+      (click)="clicked.emit(feat)"
+      [sxplr-sapiviews-features-entry-list-item-feature]="feat"
+      [sxplr-sapiviews-features-entry-list-item-ripple]="ripple">
+    </sxplr-sapiviews-features-entry-list-item>
+  </mat-card>
+  `
+})
+
+class FeatureListItemWrapper {
+  features: SapiFeatureModel[] = []
+
+  @Input()
+  ripple: boolean = true
+
+  @Output()
+  clicked = new EventEmitter()
+}
+
+export default {
+  component: FeatureListItemWrapper,
+  decorators: [
+    moduleMetadata({
+      imports: [
+        CommonModule,
+        AngularMaterialModule,
+        SapiViewsFeaturesModule,
+      ],
+      declarations: [],
+      providers: [
+        ...provideDarkTheme,
+      ],
+    })
+  ],
+
+} as Meta
+
+const Template: Story<SapiViewsFeaturesEntryListItem> = (args: SapiViewsFeaturesEntryListItem, { loaded }) => {
+  const { features } = loaded
+  return ({
+    props: {
+      ...args,
+      features
+    },
+  })
+}
+
+
+export const RegionalFeatures = Template.bind({})
+RegionalFeatures.args = {
+
+}
+RegionalFeatures.loaders = [
+  async () => {
+    const features = [
+      ...(await getHoc1RightSpatialFeatures()),
+      ...(await getHoc1RightFeatures())
+    ]
+    return {
+      features
+    }
+  }
+]
+
+export const ParcellationFeatures = Template.bind({})
+ParcellationFeatures.loaders = [
+  async () => {
+
+    const features = await getJba29Features()
+    return {
+      features
+    }
+  }
+]
\ No newline at end of file
diff --git a/src/atlasComponents/sapiViews/features/featureBadgeColor.pipe.ts b/src/atlasComponents/sapiViews/features/featureBadgeColor.pipe.ts
new file mode 100644
index 0000000000000000000000000000000000000000..474d6a2adfe72257c35a406abde767ab7054f143
--- /dev/null
+++ b/src/atlasComponents/sapiViews/features/featureBadgeColor.pipe.ts
@@ -0,0 +1,19 @@
+import { Pipe, PipeTransform } from "@angular/core";
+import { SapiFeatureModel, SxplrCleanedFeatureModel, CLEANED_IEEG_DATASET_TYPE } from "src/atlasComponents/sapi";
+
+@Pipe({
+  name: 'featureBadgeColour',
+  pure: true
+})
+
+export class FeatureBadgeColourPipe implements PipeTransform{
+  public transform(regionalFeature: SapiFeatureModel|SxplrCleanedFeatureModel) {
+    if (regionalFeature['@type'] === "siibra/features/receptor") {
+      return "accent"
+    }
+    if (regionalFeature['@type'] === CLEANED_IEEG_DATASET_TYPE) {
+      return "primary"
+    }
+    return "default"
+  }
+}
diff --git a/src/atlasComponents/sapiViews/features/featureBadgeFlag.pipe.ts b/src/atlasComponents/sapiViews/features/featureBadgeFlag.pipe.ts
new file mode 100644
index 0000000000000000000000000000000000000000..986edffac941e522ee27d76dd3ab626aa40b993d
--- /dev/null
+++ b/src/atlasComponents/sapiViews/features/featureBadgeFlag.pipe.ts
@@ -0,0 +1,14 @@
+import { Pipe, PipeTransform } from "@angular/core";
+import { SapiFeatureModel, SxplrCleanedFeatureModel, CLEANED_IEEG_DATASET_TYPE } from "src/atlasComponents/sapi";
+
+@Pipe({
+  name: 'featureBadgeFlag',
+  pure: true
+})
+
+export class FeatureBadgeFlagPipe implements PipeTransform{
+  public transform(regionalFeature: SapiFeatureModel|SxplrCleanedFeatureModel) {
+    return regionalFeature['@type'] === "siibra/features/receptor"
+      || regionalFeature['@type'] === CLEANED_IEEG_DATASET_TYPE
+  }
+}
diff --git a/src/atlasComponents/sapiViews/features/featureBadgeName.pipe.ts b/src/atlasComponents/sapiViews/features/featureBadgeName.pipe.ts
new file mode 100644
index 0000000000000000000000000000000000000000..33570440c6aa4c0f601d7171cac61cce7c42e712
--- /dev/null
+++ b/src/atlasComponents/sapiViews/features/featureBadgeName.pipe.ts
@@ -0,0 +1,19 @@
+import { Pipe, PipeTransform } from "@angular/core";
+import { SapiFeatureModel, SxplrCleanedFeatureModel, CLEANED_IEEG_DATASET_TYPE } from "src/atlasComponents/sapi";
+
+@Pipe({
+  name: 'featureBadgeName',
+  pure: true
+})
+
+export class FeatureBadgeNamePipe implements PipeTransform{
+  public transform(regionalFeature: SapiFeatureModel|SxplrCleanedFeatureModel) {
+    if (regionalFeature['@type'] === "siibra/features/receptor") {
+      return "receptor density"
+    }
+    if (regionalFeature["@type"] === CLEANED_IEEG_DATASET_TYPE) {
+      return "IEEG dataset"
+    }
+    return null
+  }
+}
diff --git a/src/atlasComponents/sapiViews/features/ieeg/ieegDataset/ieegDataset.component.ts b/src/atlasComponents/sapiViews/features/ieeg/ieegDataset/ieegDataset.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..de23194ff366b475d57954b3050aa2cf94cfb256
--- /dev/null
+++ b/src/atlasComponents/sapiViews/features/ieeg/ieegDataset/ieegDataset.component.ts
@@ -0,0 +1,113 @@
+import { Component, EventEmitter, Input, OnChanges, OnDestroy, Output, SimpleChanges } from "@angular/core";
+import { BehaviorSubject, forkJoin } from "rxjs";
+import { SAPI } from "src/atlasComponents/sapi/sapi.service";
+import { CleanedIeegDataset, cleanIeegSessionDatasets, SapiAtlasModel, SapiIeegSessionModel, SapiParcellationModel, SapiRegionModel, SapiSpaceModel } from "src/atlasComponents/sapi/type";
+
+export type Session<SId extends string> = CleanedIeegDataset['sessions'][SId]
+export type Electrode<EId extends string> = Session<string>['electrodes'][EId]
+export type ContactPoint<CId extends string> = Electrode<string>['contact_points'][CId]
+
+export type IeegOnFocusEvent = {
+  contactPoint: ContactPoint<string>
+  electrode: Electrode<string>
+  session: Session<string>
+}
+
+export type IeegOnDefocusEvent = {
+  contactPoint: ContactPoint<string>
+  electrode: Electrode<string>
+  session: Session<string>
+}
+
+@Component({
+  selector: `sxplr-sapiviews-features-ieeg-ieegdataset`,
+  templateUrl: `./ieegDataset.template.html`,
+  styleUrls: [
+    `./ieegDataset.style.css`
+  ]
+})
+
+export class IEEGDatasetCmp implements OnChanges{
+
+  @Input('sxplr-sapiviews-features-ieeg-ieegdataset-atlas')
+  public atlas: SapiAtlasModel
+  
+  @Input('sxplr-sapiviews-features-ieeg-ieegdataset-space')
+  public space: SapiSpaceModel
+
+  @Input('sxplr-sapiviews-features-ieeg-ieegdataset-parcellation')
+  public parcellation: SapiParcellationModel
+
+  @Input('sxplr-sapiviews-features-ieeg-ieegdataset-region')
+  public region: SapiRegionModel
+  
+  /**
+   * we must assume that the passed feature does not have the detail flag on
+   * We need to fetch the 
+   */
+  @Input('sxplr-sapiviews-features-ieeg-ieegdataset-feature')
+  public feature: CleanedIeegDataset
+  public detailedFeature: CleanedIeegDataset
+
+  @Output('sxplr-sapiviews-features-ieeg-ieegdataset-on-focus')
+  public onFocus = new EventEmitter<IeegOnFocusEvent>()
+
+  @Output('sxplr-sapiviews-features-ieeg-ieegdataset-on-defocus')
+  public onDefocus = new EventEmitter<IeegOnDefocusEvent>()
+  
+  public busy$ = new BehaviorSubject<boolean>(false)
+
+  ngOnChanges(changes: SimpleChanges): void {
+    if (!this.feature) {
+      return
+    }
+    if (this.feature && this.feature["@type"] !== "sxplr/cleanedIeegDataset") {
+      throw new Error(`expected @type to be sxplr-cleaned-ieeg-dataset, but is ${this.feature['@type']}.`)
+    }
+    this.busy$.next(true)
+    
+    forkJoin(
+      Object.entries(this.feature.sessions).map(([ key, session ]) => {
+        return this.sapi.getSpace(this.atlas["@id"], this.space["@id"]).getFeatureInstance(session["@id"], { parcellationId: this.parcellation["@id"], region: this.region.name })
+      })
+    ).subscribe(feats => {
+
+      const ieegSessions: SapiIeegSessionModel[] = feats.filter(feat => feat["@type"] === "siibra/features/ieegSession")
+      const features = cleanIeegSessionDatasets(ieegSessions)
+      const foundFeat = features.find(f => f["@id"] === this.feature["@id"])
+      if (foundFeat) {
+        this.detailedFeature = foundFeat
+      }
+      this.busy$.next(false)
+    })
+  }
+
+  public onContactPointClicked(cpt: ContactPoint<string>, ele: Electrode<string>, sess: Session<string>){
+    this.onFocus.emit({
+      contactPoint: cpt,
+      electrode: ele,
+      session: sess
+    })
+  }
+
+  public onPanelOpen(session: Session<string>){
+    this.onFocus.emit({
+      contactPoint: null,
+      electrode: null,
+      session: session
+    })
+  }
+
+  public onPanelClose(session: Session<string>){
+    this.onDefocus.emit({
+      contactPoint: null,
+      electrode: null,
+      session: session
+    })
+  }
+
+  constructor(private sapi: SAPI){
+
+  }
+
+}
diff --git a/src/atlasComponents/sapiViews/features/ieeg/ieegDataset/ieegDataset.stories.ts b/src/atlasComponents/sapiViews/features/ieeg/ieegDataset/ieegDataset.stories.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c9f08880e4525ab8c2d9f550abee83bf60c876fc
--- /dev/null
+++ b/src/atlasComponents/sapiViews/features/ieeg/ieegDataset/ieegDataset.stories.ts
@@ -0,0 +1,113 @@
+import { Meta, moduleMetadata, Story } from "@storybook/angular"
+import { SAPI, SapiAtlasModel, SapiParcellationModel, SapiRegionModel, SapiSpaceModel } from "src/atlasComponents/sapi"
+import { getHoc1Right, getHumanAtlas, getJba29, getMni152, provideDarkTheme, getMni152SpatialFeatureHoc1Right } from "src/atlasComponents/sapi/stories.base"
+import { SxplrSapiViewsFeaturesIeegModule } from ".."
+import { Component } from "@angular/core"
+import { cleanIeegSessionDatasets, SapiSpatialFeatureModel } from "src/atlasComponents/sapi/type"
+import { action } from "@storybook/addon-actions"
+
+@Component({
+  selector: 'ieeg-entry-wrapper-cmp',
+  template: `
+  <sxplr-sapiviews-features-ieeg-ieegdataset
+    [sxplr-sapiviews-features-ieeg-ieegdataset-atlas]="atlas"
+    [sxplr-sapiviews-features-ieeg-ieegdataset-space]="template"
+    [sxplr-sapiviews-features-ieeg-ieegdataset-parcellation]="parcellation"
+    [sxplr-sapiviews-features-ieeg-ieegdataset-region]="region"
+    [sxplr-sapiviews-features-ieeg-ieegdataset-feature]="feature"
+    (sxplr-sapiviews-features-ieeg-ieegdataset-on-focus)="handleCtptClick($event)"
+    (sxplr-sapiviews-features-ieeg-ieegdataset-on-defocus)="handleOnDeFocus($event)"
+  >
+  </sxplr-sapiviews-features-ieeg-ieegdataset>
+  `,
+  styles: [
+    `
+    :host
+    {
+      display: block;
+      width: 20rem;
+    }
+    `
+  ]
+})
+class EntryWrappercls {
+  atlas: SapiAtlasModel
+  template: SapiSpaceModel
+  feature: SapiSpatialFeatureModel
+  parcellation: SapiParcellationModel
+  region: SapiRegionModel
+
+  handleOnFocus(cpt: unknown){}
+  handleOnDeFocus(cpt: unknown) {}
+}
+
+export default {
+  component: EntryWrappercls,
+  decorators: [
+    moduleMetadata({
+      imports: [
+        SxplrSapiViewsFeaturesIeegModule,
+      ],
+      providers: [
+        SAPI,
+        ...provideDarkTheme,
+      ],
+      declarations: [
+        EntryWrappercls
+      ]
+    })
+  ],
+} as Meta
+
+const Template: Story<EntryWrappercls> = (args: EntryWrappercls, { loaded }) => {
+  const { atlas, parc, space, region, feature } = loaded
+  return ({
+    props: {
+      ...args,
+      atlas: atlas,
+      parcellation: parc,
+      template: space,
+      region: region,
+      feature,
+      handleOnFocus: action('handleOnFocus'),
+      handleOnDeFocus: action('handleOnDeFocus')
+    },
+  })
+}
+
+const loadFeat = async () => {
+  const atlas = await getHumanAtlas()
+  const space = await getMni152()
+  const parc = await getJba29()
+  const region = await getHoc1Right()
+  
+  const features = await getMni152SpatialFeatureHoc1Right()
+  const spatialFeats = features.filter(f => f["@type"] === "siibra/features/ieegSession")
+  const feature = cleanIeegSessionDatasets(spatialFeats)[0]
+  return {
+    atlas,
+    space,
+    parc,
+    region,
+    feature
+  }
+}
+
+export const Default = Template.bind({})
+Default.args = {
+
+}
+Default.loaders = [
+  async () => {
+    const {
+      atlas,
+      space,
+      feature,
+      parc,
+      region,
+    } = await loadFeat()
+    return {
+      atlas, space, feature, parc, region  
+    }
+  }
+]
diff --git a/src/atlasComponents/sapiViews/features/ieeg/ieegDataset/ieegDataset.style.css b/src/atlasComponents/sapiViews/features/ieeg/ieegDataset/ieegDataset.style.css
new file mode 100644
index 0000000000000000000000000000000000000000..1c554eef70e8adae01cba4530c534a389ce6c93a
--- /dev/null
+++ b/src/atlasComponents/sapiViews/features/ieeg/ieegDataset/ieegDataset.style.css
@@ -0,0 +1,4 @@
+mat-form-field
+{
+  width: 100%;
+}
diff --git a/src/atlasComponents/sapiViews/features/ieeg/ieegDataset/ieegDataset.template.html b/src/atlasComponents/sapiViews/features/ieeg/ieegDataset/ieegDataset.template.html
new file mode 100644
index 0000000000000000000000000000000000000000..af7464f5e24b254d42985ef55766f755696c4909
--- /dev/null
+++ b/src/atlasComponents/sapiViews/features/ieeg/ieegDataset/ieegDataset.template.html
@@ -0,0 +1,112 @@
+<spinner-cmp *ngIf="busy$ | async; else resultTmpl">
+</spinner-cmp>
+
+<ng-template #resultTmpl>
+
+  <mat-accordion *ngIf="!!detailedFeature">
+    <ng-template
+      ngFor
+      [ngForOf]="detailedFeature.sessions | keyvalue"
+      let-sessionKeyVal>
+  
+      <ng-template
+        [ngIf]="sessionKeyVal.value.inRoi"
+        [ngTemplateOutlet]="sessionTmpl"
+        [ngTemplateOutletContext]="{
+          $implicit: sessionKeyVal.value
+        }">
+  
+      </ng-template>
+  
+    </ng-template>
+  </mat-accordion>
+  
+</ng-template>
+
+<!-- session template -->
+<ng-template #sessionTmpl let-session>
+  <mat-expansion-panel
+    (opened)="onPanelOpen(session)"
+    (closed)="onPanelClose(session)">
+    <mat-expansion-panel-header>
+      SessionID: {{ session.sub_id }}
+    </mat-expansion-panel-header>
+
+    <ng-template matExpansionPanelContent>
+      <ng-template
+        ngFor
+        [ngForOf]="session.electrodes | keyvalue | inRoi"
+        let-electrodeKeyVal>
+        <ng-template
+          [ngTemplateOutlet]="electrodeTmpl"
+          [ngTemplateOutletContext]="{
+            electrode: electrodeKeyVal.value,
+            session: session
+          }">
+
+        </ng-template>
+      </ng-template>
+
+      <mat-divider></mat-divider>
+      <ng-template
+        ngFor
+        [ngForOf]="session.electrodes | keyvalue | inRoi : false"
+        let-electrodeKeyVal>
+        <div class="sxplr-very-muted">
+          <ng-template
+            [ngTemplateOutlet]="electrodeTmpl"
+            [ngTemplateOutletContext]="{
+              electrode: electrodeKeyVal.value,
+              session: session
+            }">
+
+          </ng-template>
+        </div>
+      </ng-template>
+    </ng-template>
+  </mat-expansion-panel>
+</ng-template>
+
+<!-- electrode template -->
+<ng-template
+  #electrodeTmpl
+  let-electrode="electrode"
+  let-session="session">
+  <mat-form-field appearance="fill">
+    <mat-label>
+      ElectrodeID: {{ electrode.electrode_id }}
+    </mat-label>
+    <mat-chip-list>
+      <ng-template
+        ngFor
+        [ngForOf]="electrode.contact_points | keyvalue"
+        let-contactPointKeyVal>
+
+        <ng-template
+          [ngTemplateOutlet]="contactPointTmpl"
+          [ngTemplateOutletContext]="{
+            contactPointKey: contactPointKeyVal.key,
+            contactPoint: contactPointKeyVal.value,
+            electrode: electrode,
+            session: session
+          }">
+        </ng-template>
+      </ng-template>
+    </mat-chip-list>
+  </mat-form-field>
+</ng-template>
+
+<!-- contact point template -->
+<ng-template
+  #contactPointTmpl
+  let-contactPoint="contactPoint"
+  let-key="contactPointKey"
+  let-electrode="electrode"
+  let-session="session">
+  <mat-chip
+    (click)="onContactPointClicked(contactPoint, electrode, session)"
+    [color]="contactPoint.inRoi ? 'primary' : 'default'"
+    selected>
+    {{ key }}
+  </mat-chip>
+</ng-template>
\ No newline at end of file
diff --git a/src/atlasComponents/sapiViews/features/ieeg/inRoi.pipe.ts b/src/atlasComponents/sapiViews/features/ieeg/inRoi.pipe.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4f215c8ccd96ff7af503abc521e3c6558383c234
--- /dev/null
+++ b/src/atlasComponents/sapiViews/features/ieeg/inRoi.pipe.ts
@@ -0,0 +1,19 @@
+import { Pipe, PipeTransform } from "@angular/core";
+
+interface InRoi {
+  key: string
+  value: {
+    inRoi: boolean
+  }
+}
+
+@Pipe({
+  name: 'inRoi',
+  pure: true
+})
+
+export class InRoiPipe implements PipeTransform{
+  public transform(list: InRoi[], inroi=true): InRoi[] {
+    return list.filter(it => it.value.inRoi === inroi)
+  }
+}
diff --git a/src/atlasComponents/sapiViews/features/ieeg/index.ts b/src/atlasComponents/sapiViews/features/ieeg/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..757808e777ca4f96b83bba521ab6c71d3903cfaa
--- /dev/null
+++ b/src/atlasComponents/sapiViews/features/ieeg/index.ts
@@ -0,0 +1,2 @@
+export { IEEGDatasetCmp, IeegOnFocusEvent, IeegOnDefocusEvent, ContactPoint, Electrode, Session } from "./ieegDataset/ieegDataset.component"
+export { SxplrSapiViewsFeaturesIeegModule } from "./module"
\ No newline at end of file
diff --git a/src/atlasComponents/sapiViews/features/ieeg/module.ts b/src/atlasComponents/sapiViews/features/ieeg/module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c91161eac8e261121aaa58e583265961b324c007
--- /dev/null
+++ b/src/atlasComponents/sapiViews/features/ieeg/module.ts
@@ -0,0 +1,33 @@
+import { CommonModule } from "@angular/common";
+import { NgModule } from "@angular/core";
+import { MatChipsModule } from "@angular/material/chips";
+import { MatDividerModule } from "@angular/material/divider";
+import { MatExpansionModule } from "@angular/material/expansion";
+import { MatFormFieldModule } from "@angular/material/form-field";
+import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
+import { SAPIModule } from "src/atlasComponents/sapi/module";
+import { SpinnerModule } from "src/components/spinner";
+import { IEEGDatasetCmp } from "./ieegDataset/ieegDataset.component";
+import { InRoiPipe } from "./inRoi.pipe";
+
+@NgModule({
+  imports: [
+    CommonModule,
+    MatExpansionModule,
+    MatChipsModule,
+    MatFormFieldModule,
+    MatDividerModule,
+    BrowserAnimationsModule,
+    SpinnerModule,
+    SAPIModule,
+  ],
+  declarations: [
+    IEEGDatasetCmp,
+    InRoiPipe,
+  ],
+  exports: [
+    IEEGDatasetCmp,
+  ]
+})
+
+export class SxplrSapiViewsFeaturesIeegModule{}
\ No newline at end of file
diff --git a/src/atlasComponents/sapiViews/features/index.ts b/src/atlasComponents/sapiViews/features/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..89e1fafbad8108576c708d401326b2d5f4137f6d
--- /dev/null
+++ b/src/atlasComponents/sapiViews/features/index.ts
@@ -0,0 +1,7 @@
+export {
+  SapiViewsFeaturesModule
+} from "./module"
+
+export {
+  SapiViewsFeaturesVoiQuery
+} from "./voi"
diff --git a/src/atlasComponents/sapiViews/features/module.ts b/src/atlasComponents/sapiViews/features/module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3df348c72f79be4b53c3050e30627c896e8b93f2
--- /dev/null
+++ b/src/atlasComponents/sapiViews/features/module.ts
@@ -0,0 +1,56 @@
+import { CommonModule, DOCUMENT } from "@angular/common"
+import { NgModule } from "@angular/core"
+import { AngularMaterialModule } from "src/sharedModules"
+import { appendScriptFactory, APPEND_SCRIPT_TOKEN } from "src/util/constants"
+import { FeatureEntryCmp } from "./entry/entry.component"
+import { SapiViewsFeaturesEntryListItem } from "./entryListItem/entryListItem.component"
+import { FeatureBadgeColourPipe } from "./featureBadgeColor.pipe"
+import { FeatureBadgeFlagPipe } from "./featureBadgeFlag.pipe"
+import { FeatureBadgeNamePipe } from "./featureBadgeName.pipe"
+import * as ieeg from "./ieeg"
+import * as receptor from "./receptors"
+import {SapiViewsFeatureConnectivityModule} from "src/atlasComponents/sapiViews/features/connectivity";
+import * as voi from "./voi"
+import { OrderFilterFeaturesPipe } from "./orderFilterFeatureList.pipe"
+
+const {
+  SxplrSapiViewsFeaturesIeegModule
+} = ieeg
+const {
+  ReceptorViewModule
+} = receptor
+const { SapiViewsFeaturesVoiModule } = voi
+
+@NgModule({
+  imports: [
+    CommonModule,
+    ReceptorViewModule,
+    SxplrSapiViewsFeaturesIeegModule,
+    AngularMaterialModule,
+    SapiViewsFeaturesVoiModule,
+    SapiViewsFeatureConnectivityModule,
+  ],
+  declarations: [
+    FeatureEntryCmp,
+    FeatureBadgeNamePipe,
+    FeatureBadgeColourPipe,
+    FeatureBadgeFlagPipe,
+    SapiViewsFeaturesEntryListItem,
+    OrderFilterFeaturesPipe,
+  ],
+  providers: [
+    {
+      provide: APPEND_SCRIPT_TOKEN,
+      useFactory: appendScriptFactory,
+      deps: [ DOCUMENT ]
+    }
+  ],
+  exports: [
+    FeatureEntryCmp,
+    SapiViewsFeaturesEntryListItem,
+    SapiViewsFeaturesVoiModule,
+    SapiViewsFeatureConnectivityModule,
+    OrderFilterFeaturesPipe,
+  ]
+})
+export class SapiViewsFeaturesModule{}
diff --git a/src/atlasComponents/sapiViews/features/orderFilterFeatureList.pipe.ts b/src/atlasComponents/sapiViews/features/orderFilterFeatureList.pipe.ts
new file mode 100644
index 0000000000000000000000000000000000000000..382862b0300f4bdaffadce20aeb4e28274a8bef6
--- /dev/null
+++ b/src/atlasComponents/sapiViews/features/orderFilterFeatureList.pipe.ts
@@ -0,0 +1,37 @@
+import { Pipe, PipeTransform } from "@angular/core";
+import { CLEANED_IEEG_DATASET_TYPE, SapiFeatureModel, SxplrCleanedFeatureModel } from "src/atlasComponents/sapi/type";
+import { environment } from "src/environments/environment"
+
+type PipableFeatureType = SapiFeatureModel | SxplrCleanedFeatureModel
+
+type ArrayOperation<T extends boolean | number> = (input: PipableFeatureType) => T
+
+const FILTER_FN: ArrayOperation<boolean> = feature => {
+  return feature["@type"] !== "siibra/features/cells"
+}
+
+const ORDER_LIST: ArrayOperation<number> = feature => {
+  if (feature["@type"] === "siibra/features/receptor") return -4
+  if (feature["@type"] === CLEANED_IEEG_DATASET_TYPE) return -3
+  if (feature['@type'] === "https://openminds.ebrains.eu/core/DatasetVersion") return 2
+  return 0
+}
+
+@Pipe({
+  name: 'orderFilterFeatures',
+  pure: true
+})
+
+export class OrderFilterFeaturesPipe implements PipeTransform{
+  public transform(inputFeatures: PipableFeatureType[]): PipableFeatureType[] {
+    return inputFeatures
+      .filter(f => {
+        /**
+         * if experimental flag is set, do not filter out anything
+         */
+        if (environment.EXPERIMENTAL_FEATURE_FLAG) return true
+        return FILTER_FN(f)
+      })
+      .sort((a, b) => ORDER_LIST(a) - ORDER_LIST(b))
+  }
+}
diff --git a/src/atlasComponents/sapiViews/features/receptors/autoradiography/autoradiograph.stories.ts b/src/atlasComponents/sapiViews/features/receptors/autoradiography/autoradiograph.stories.ts
new file mode 100644
index 0000000000000000000000000000000000000000..093ec6a110eaca060b0bf3e48204026ee5ed1946
--- /dev/null
+++ b/src/atlasComponents/sapiViews/features/receptors/autoradiography/autoradiograph.stories.ts
@@ -0,0 +1,146 @@
+import { CommonModule } from "@angular/common"
+import { HttpClientModule } from "@angular/common/http"
+import { Component, ViewChild } from "@angular/core"
+import { FormsModule } from "@angular/forms"
+import { BrowserAnimationsModule } from "@angular/platform-browser/animations"
+import { Meta, moduleMetadata, Story } from "@storybook/angular"
+import { SAPI, SapiAtlasModel, SapiParcellationModel, SapiRegionModel, SapiSpaceModel } from "src/atlasComponents/sapi"
+import { getHoc1RightFeatureDetail, getHoc1RightFeatures, getHoc1Right, getHumanAtlas, getJba29, getMni152 } from "src/atlasComponents/sapi/stories.base"
+import { SapiRegionalFeatureReceptorModel } from "src/atlasComponents/sapi/type"
+import { AngularMaterialModule } from "src/sharedModules"
+import { Autoradiography } from "./autoradiography.component"
+
+@Component({
+  selector: 'autoradiograph-wrapper-cmp',
+  template: `
+  <mat-form-field appearance="fill">
+    <mat-select [(ngModel)]="selectedSymbol">
+      <mat-option value="" disabled>
+        --select--
+      </mat-option>
+
+      <mat-option [value]="option"
+        *ngFor="let option of options">
+        {{ option }}
+      </mat-option>
+    </mat-select>
+  </mat-form-field>
+  <sxplr-sapiviews-features-receptor-autoradiograph
+    class="d-inline-block w-100 h-100"
+    [sxplr-sapiviews-features-receptor-atlas]="atlas"
+    [sxplr-sapiviews-features-receptor-parcellation]="parcellation"
+    [sxplr-sapiviews-features-receptor-template]="template"
+    [sxplr-sapiviews-features-receptor-region]="region"
+    [sxplr-sapiviews-features-receptor-featureid]="featureId"
+    [sxplr-sapiviews-features-receptor-data]="feature"
+    [sxplr-sapiviews-features-receptor-autoradiograph-selected-symbol]="selectedSymbol"
+  >
+  </sxplr-sapiviews-features-receptor-autoradiograph>
+  `,
+  styles: [
+    `
+    :host
+    {
+      display: block;
+      max-width: 24rem;
+      max-height: 24rem;
+    }
+    `
+  ]
+})
+class AutoRadiographWrapperCls {
+  atlas: SapiAtlasModel
+  parcellation: SapiParcellationModel
+  template: SapiSpaceModel
+  region: SapiRegionModel
+
+  feature: SapiRegionalFeatureReceptorModel
+  featureId: string
+
+  @ViewChild(Autoradiography)
+  ar: Autoradiography
+
+  selectedSymbol: string
+
+  get options(){
+    return Object.keys(this.feature?.data?.autoradiographs || this.ar?.receptorData?.data?.autoradiographs || {})
+  }
+}
+
+export default {
+  component: AutoRadiographWrapperCls,
+  decorators: [
+    moduleMetadata({
+      imports: [
+        CommonModule,
+        AngularMaterialModule,
+        HttpClientModule,
+        BrowserAnimationsModule,
+        FormsModule,
+      ],
+      providers: [
+        SAPI
+      ],
+      declarations: [
+        Autoradiography
+      ]
+    })
+  ],
+} as Meta
+
+const Template: Story<AutoRadiographWrapperCls> = (args: AutoRadiographWrapperCls, { loaded }) => {
+  const { atlas, parc, space, region, featureId, feature } = loaded
+  return ({
+    props: {
+      ...args,
+      atlas: atlas,
+      parcellation: parc,
+      template: space,
+      region: region,
+      feature,
+      featureId,
+    },
+  })
+}
+
+const loadFeat = async () => {
+  const atlas = await getHumanAtlas()
+  const parc = await getJba29()
+  const region = await getHoc1Right()
+  const space = await getMni152()
+  const features = await getHoc1RightFeatures()
+  const receptorfeat = features.find(f => f['@type'] === "siibra/features/receptor")
+  const feature = await getHoc1RightFeatureDetail(receptorfeat["@id"])
+  return {
+    atlas,
+    parc,
+    space,
+    region,
+    featureId: receptorfeat["@id"],
+    feature
+  }
+}
+
+export const Default = Template.bind({})
+Default.args = {
+
+}
+Default.loaders = [
+  async () => {
+    const { atlas, parc, space, region, featureId } = await loadFeat()
+    return {
+      atlas, parc, space, region, featureId
+    }
+  }
+]
+
+export const LoadViaDirectlyInjectData = Template.bind({})
+LoadViaDirectlyInjectData.loaders = [
+  async () => {
+
+    const { feature } = await loadFeat()
+    return {
+      feature
+    }
+  }
+]
\ No newline at end of file
diff --git a/src/atlasComponents/sapiViews/features/receptors/autoradiography/autoradiography.component.ts b/src/atlasComponents/sapiViews/features/receptors/autoradiography/autoradiography.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ecd059f0a8fd1411e9a5c7b211c7aac78cff91ae
--- /dev/null
+++ b/src/atlasComponents/sapiViews/features/receptors/autoradiography/autoradiography.component.ts
@@ -0,0 +1,82 @@
+import { AfterViewInit, Component, ElementRef, Input, OnChanges, SimpleChanges } from "@angular/core";
+import { SAPI } from "src/atlasComponents/sapi";
+import { PARSE_TYPEDARRAY } from "src/atlasComponents/sapi/sapi.service";
+import { BaseReceptor } from "../base";
+
+@Component({
+  selector: `sxplr-sapiviews-features-receptor-autoradiograph`,
+  templateUrl: './autoradiography.template.html',
+  styleUrls: [
+    './autoradiography.style.css'
+  ],
+  exportAs: 'sxplrSapiViewsFeaturesReceptorAR'
+})
+
+export class Autoradiography extends BaseReceptor implements OnChanges, AfterViewInit{
+  
+  @Input('sxplr-sapiviews-features-receptor-autoradiograph-selected-symbol')
+  selectedSymbol: string
+
+  private pleaseRender = false
+
+  width: number
+  height: number
+  renderBuffer: Uint8ClampedArray
+
+  async ngOnChanges(simpleChanges: SimpleChanges) {
+    await super.ngOnChanges(simpleChanges)
+    if (!this.receptorData) {
+      return
+    }
+    if (this.selectedSymbol) {
+      const fp = this.receptorData.data.autoradiographs[this.selectedSymbol]
+      if (!fp) {
+        this.error = `selectedSymbol ${this.selectedSymbol} cannot be found. Valid symbols are ${Object.keys(this.receptorData.data.autoradiographs)}`
+        return
+      }
+      const { "x-width": width, "x-height": height } = fp
+      
+      this.width = width
+      this.height = height
+
+      const { result } = await this.sapi.processNpArrayData<PARSE_TYPEDARRAY.CANVAS_FORTRAN_RGBA>(fp, PARSE_TYPEDARRAY.CANVAS_FORTRAN_RGBA)
+      this.renderBuffer = result
+      this.rerender()
+    }
+  }
+  constructor(sapi: SAPI, private el: ElementRef){
+    super(sapi)
+  }
+
+  ngAfterViewInit(): void {
+    if (this.pleaseRender) this.rerender()
+  }
+
+  rerender(){
+    this.dataBlobAvailable = false
+    if (!this.el || !this.renderBuffer) {
+      this.pleaseRender = true
+      return
+    }
+
+    const arContainer = (this.el.nativeElement as HTMLElement)
+    while (arContainer.firstChild) {
+      arContainer.removeChild(arContainer.firstChild)
+    }
+
+    const canvas = document.createElement("canvas")
+    canvas.height = this.height
+    canvas.width = this.width
+    arContainer.appendChild(canvas)
+    const ctx = canvas.getContext("2d")
+    const imgData = ctx.createImageData(this.width, this.height)
+    imgData.data.set(this.renderBuffer)
+    ctx.putImageData(imgData, 0, 0)
+
+    canvas.toBlob(blob => {
+      this.dataBlobAvailable = true
+      this.dataBlob$.next(blob)
+    })
+    this.pleaseRender = false
+  }
+}
diff --git a/src/viewerModule/nehuba/maximisePanelButton/maximisePanelButton.style.css b/src/atlasComponents/sapiViews/features/receptors/autoradiography/autoradiography.style.css
similarity index 100%
rename from src/viewerModule/nehuba/maximisePanelButton/maximisePanelButton.style.css
rename to src/atlasComponents/sapiViews/features/receptors/autoradiography/autoradiography.style.css
diff --git a/src/viewerModule/viewerStateBreadCrumb/breadcrumb/breadcrumb.style.css b/src/atlasComponents/sapiViews/features/receptors/autoradiography/autoradiography.template.html
similarity index 100%
rename from src/viewerModule/viewerStateBreadCrumb/breadcrumb/breadcrumb.style.css
rename to src/atlasComponents/sapiViews/features/receptors/autoradiography/autoradiography.template.html
diff --git a/src/atlasComponents/sapiViews/features/receptors/base.ts b/src/atlasComponents/sapiViews/features/receptors/base.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2428c72f6e315c97f98a5914c867d7c6c7560af8
--- /dev/null
+++ b/src/atlasComponents/sapiViews/features/receptors/base.ts
@@ -0,0 +1,100 @@
+import { Directive, Input, SimpleChanges } from "@angular/core";
+import { BehaviorSubject } from "rxjs";
+import { SAPI, SapiAtlasModel, SapiParcellationModel, SapiRegionModel, SapiSpaceModel } from "src/atlasComponents/sapi";
+import { SapiRegionalFeatureReceptorModel } from "src/atlasComponents/sapi/type";
+
+@Directive()
+export abstract class BaseReceptor{
+  
+  @Input('sxplr-sapiviews-features-receptor-atlas')
+  atlas: SapiAtlasModel
+  
+  @Input('sxplr-sapiviews-features-receptor-parcellation')
+  parcellation: SapiParcellationModel
+
+  @Input('sxplr-sapiviews-features-receptor-template')
+  template: SapiSpaceModel
+  
+  @Input('sxplr-sapiviews-features-receptor-region')
+  region: SapiRegionModel
+
+  @Input('sxplr-sapiviews-features-receptor-featureid')
+  featureId: string
+
+  @Input('sxplr-sapiviews-features-receptor-data')
+  receptorData: SapiRegionalFeatureReceptorModel
+
+  error: string
+
+  async ngOnChanges(simpleChanges: SimpleChanges) {
+    if (simpleChanges.receptorData?.currentValue) {
+      this.rerender()
+      return
+    }
+    if (this.canFetch) {
+      this.receptorData = await this.fetchReceptorData()
+      this.rerender()
+    }
+  }
+
+  private get canFetch() {
+    if (!this.atlas) {
+      this.error = `atlas needs to be defined, but is not`
+      return false
+    }
+    if (!this.parcellation) {
+      this.error = `parcellation needs to be defined, but is not`
+      return false
+    }
+    if (!this.region) {
+      this.error = `region needs to be defined, but is not`
+      return false
+    }
+    if (!this.featureId) {
+      this.error = `featureId needs to be defined, but is not`
+      return false
+    }
+    return true
+  }
+
+  protected async fetchReceptorData() {
+    this.error = null
+    if (!this.atlas) {
+      this.error = `atlas needs to be defined, but is not`
+      return
+    }
+    if (!this.parcellation) {
+      this.error = `parcellation needs to be defined, but is not`
+      return
+    }
+    if (!this.region) {
+      this.error = `region needs to be defined, but is not`
+      return
+    }
+    if (!this.featureId) {
+      this.error = `featureId needs to be defined, but is not`
+      return
+    }
+    const result = await this.sapi.getRegion(this.atlas["@id"], this.parcellation["@id"], this.region.name).getFeatureInstance(this.featureId, this.template["@id"]).toPromise()
+    if (result["@type"] !== "siibra/features/receptor") {
+      throw new Error(`BaseReceptor Error. Expected .type to be "siibra/features/receptor", but was "${result['@type']}"`)
+    }
+    return result
+  }
+
+  abstract rerender(): void
+
+  /**
+   * flag to indicate that getDataBlob() can be called.
+   */
+  dataBlobAvailable = false
+  /**
+   * blob object observable, representing the data of the component. This allows the data to be downloaded.
+   */
+  dataBlob$ = new BehaviorSubject<Blob>(null)
+
+  constructor(
+    protected sapi: SAPI
+  ){
+  }
+}
diff --git a/src/atlasComponents/sapiViews/features/receptors/entry/entry.component.ts b/src/atlasComponents/sapiViews/features/receptors/entry/entry.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3f6a756c1b26a15d26a93c4900347072cd79be27
--- /dev/null
+++ b/src/atlasComponents/sapiViews/features/receptors/entry/entry.component.ts
@@ -0,0 +1,43 @@
+import { ChangeDetectorRef, Component, OnChanges, SimpleChanges } from "@angular/core";
+import { SAPI } from "src/atlasComponents/sapi";
+import { BaseReceptor } from "../base";
+
+@Component({
+  selector: 'sxplr-sapiviews-features-receptor-entry',
+  templateUrl: `./entry.template.html`,
+  styleUrls: [
+    `./entry.style.css`
+  ]
+})
+
+export class Entry extends BaseReceptor implements OnChanges {
+  selectedSymbol: string
+  symbolsOptions: string[] = []
+
+  async ngOnChanges(simpleChanges: SimpleChanges): Promise<void> {
+    await super.ngOnChanges(simpleChanges)
+  }
+
+  loading = true
+  rerender(): void {
+    if (this.receptorData.data.receptor_symbols) {
+      this.loading = false
+      this.symbolsOptions = Object.keys(this.receptorData.data.receptor_symbols)
+    }
+    this.cdr.detectChanges()
+  }
+  constructor(
+    sapi: SAPI,
+    private cdr: ChangeDetectorRef,
+  ){
+    super(sapi)
+  }
+
+  setSelectedSymbol(select: string){
+    this.selectedSymbol = select
+  }
+
+  getDataBlob(): Promise<Blob> {
+    throw new Error(`cannot get blob of entry component`)
+  }
+}
diff --git a/src/atlasComponents/sapiViews/features/receptors/entry/entry.stories.ts b/src/atlasComponents/sapiViews/features/receptors/entry/entry.stories.ts
new file mode 100644
index 0000000000000000000000000000000000000000..63f07ee1ef4f6dd57c402e07d08a4e9c5d742f01
--- /dev/null
+++ b/src/atlasComponents/sapiViews/features/receptors/entry/entry.stories.ts
@@ -0,0 +1,127 @@
+import { CommonModule, DOCUMENT } from "@angular/common"
+import { HttpClientModule } from "@angular/common/http"
+import { Meta, moduleMetadata, Story } from "@storybook/angular"
+import { SAPI, SapiAtlasModel, SapiParcellationModel, SapiRegionModel, SapiSpaceModel } from "src/atlasComponents/sapi"
+import { getHoc1RightFeatureDetail, getHoc1RightFeatures, getHoc1Right, getHumanAtlas, getJba29, getMni152, provideDarkTheme } from "src/atlasComponents/sapi/stories.base"
+import { AngularMaterialModule } from "src/sharedModules"
+import { Entry } from "./entry.component"
+import { ReceptorViewModule } from ".."
+import { appendScriptFactory, APPEND_SCRIPT_TOKEN } from "src/util/constants"
+import { Component, Output } from "@angular/core"
+import { SapiRegionalFeatureReceptorModel } from "src/atlasComponents/sapi/type"
+
+@Component({
+  selector: 'entry-wrapper-cmp',
+  template: `
+  <sxplr-sapiviews-features-receptor-entry
+    [sxplr-sapiviews-features-receptor-atlas]="atlas"
+    [sxplr-sapiviews-features-receptor-parcellation]="parcellation"
+    [sxplr-sapiviews-features-receptor-template]="template"
+    [sxplr-sapiviews-features-receptor-region]="region"
+    [sxplr-sapiviews-features-receptor-featureid]="featureId"
+    [sxplr-sapiviews-features-receptor-data]="feature"
+  >
+  </sxplr-sapiviews-features-receptor-entry>
+  `,
+  styles: [
+    `
+    :host
+    {
+      display: block;
+      width: 20rem;
+    }
+    `
+  ]
+})
+class EntryWrappercls {
+  atlas: SapiAtlasModel
+  parcellation: SapiParcellationModel
+  template: SapiSpaceModel
+  region: SapiRegionModel
+  feature: SapiRegionalFeatureReceptorModel
+  featureId: string
+
+}
+
+export default {
+  component: EntryWrappercls,
+  decorators: [
+    moduleMetadata({
+      imports: [
+        CommonModule,
+        HttpClientModule,
+        ReceptorViewModule,
+        AngularMaterialModule,
+      ],
+      providers: [
+        SAPI,
+        {
+          provide: APPEND_SCRIPT_TOKEN,
+          useFactory: appendScriptFactory,
+          deps: [ DOCUMENT ]
+        },
+        ...provideDarkTheme,
+      ],
+      declarations: [
+        EntryWrappercls
+      ]
+    })
+  ],
+} as Meta
+
+const Template: Story<Entry> = (args: Entry, { loaded }) => {
+  const { atlas, parc, space, region, featureId, feature } = loaded
+  return ({
+    props: {
+      ...args,
+      atlas: atlas,
+      parcellation: parc,
+      template: space,
+      region: region,
+      feature,
+      featureId,
+    },
+  })
+}
+
+const loadFeat = async () => {
+  const atlas = await getHumanAtlas()
+  const parc = await getJba29()
+  const region = await getHoc1Right()
+  const space = await getMni152()
+  const features = await getHoc1RightFeatures()
+  const receptorfeat = features.find(f => f['@type'] === "siibra/features/receptor")
+  const feature = await getHoc1RightFeatureDetail(receptorfeat["@id"])
+  return {
+    atlas,
+    parc,
+    space,
+    region,
+    featureId: receptorfeat["@id"],
+    feature
+  }
+}
+
+export const Default = Template.bind({})
+Default.args = {
+
+}
+Default.loaders = [
+  async () => {
+    const { atlas, parc, space, region, featureId } = await loadFeat()
+    return {
+      atlas, parc, space, region, featureId
+    }
+  }
+]
+
+export const LoadViaDirectlyInjectData = Template.bind({})
+LoadViaDirectlyInjectData.loaders = [
+  async () => {
+
+    const { feature } = await loadFeat()
+    return {
+      feature
+    }
+  }
+]
\ No newline at end of file
diff --git a/src/atlasComponents/sapiViews/features/receptors/entry/entry.style.css b/src/atlasComponents/sapiViews/features/receptors/entry/entry.style.css
new file mode 100644
index 0000000000000000000000000000000000000000..0645dc01a568740a2c3d2f51c486adcb7f111a02
--- /dev/null
+++ b/src/atlasComponents/sapiViews/features/receptors/entry/entry.style.css
@@ -0,0 +1,4 @@
+:host
+{
+  display: block;
+}
\ No newline at end of file
diff --git a/src/atlasComponents/sapiViews/features/receptors/entry/entry.template.html b/src/atlasComponents/sapiViews/features/receptors/entry/entry.template.html
new file mode 100644
index 0000000000000000000000000000000000000000..e88ce49a8da4549d21f77e950af2c9a1361a4fb1
--- /dev/null
+++ b/src/atlasComponents/sapiViews/features/receptors/entry/entry.template.html
@@ -0,0 +1,80 @@
+<mat-card>
+  <spinner-cmp *ngIf="loading"></spinner-cmp>
+
+  <ng-template [ngTemplateOutlet]="downloadBtn"
+    [ngTemplateOutletContext]="{
+      label: 'fingerprint.tsv',
+      filename: 'fingerprint.tsv',
+      receptorCmp: fp
+    }">
+  </ng-template>
+  <sxplr-sapiviews-features-receptor-fingerprint
+    [sxplr-sapiviews-features-receptor-data]="receptorData"
+    (sxplr-sapiviews-features-receptor-fingerprint-receptor-selected)="setSelectedSymbol($event)"
+    #fp="sxplrSapiViewsFeaturesReceptorFP"
+  >
+  </sxplr-sapiviews-features-receptor-fingerprint>
+
+  <mat-form-field appearance="fill" class="w-100">
+    <mat-select [(ngModel)]="selectedSymbol">
+      <mat-option value="null" disabled>
+        --select--
+      </mat-option>
+
+      <mat-option [value]="option"
+        *ngFor="let option of symbolsOptions">
+        {{ option }}
+      </mat-option>
+    </mat-select>
+  </mat-form-field>
+
+  <ng-template [ngIf]="selectedSymbol">
+    
+    <ng-template [ngTemplateOutlet]="downloadBtn"
+      [ngTemplateOutletContext]="{
+        label: 'profile.tsv',
+        filename: 'profile.tsv',
+        receptorCmp: profile
+      }">
+    </ng-template>
+    <sxplr-sapiviews-features-receptor-profile
+      [sxplr-sapiviews-features-receptor-data]="receptorData"
+      [sxplr-sapiviews-features-receptor-profile-selected-symbol]="selectedSymbol"
+      #profile="sxplrSapiViewsFeaturesReceptorProfile">
+    </sxplr-sapiviews-features-receptor-profile>
+
+
+    <ng-template [ngTemplateOutlet]="downloadBtn"
+      [ngTemplateOutletContext]="{
+        label: 'autoradiograph.png',
+        filename: 'autoradiograph.png',
+        receptorCmp: ar
+      }">
+    </ng-template>
+    <sxplr-sapiviews-features-receptor-autoradiograph
+      [sxplr-sapiviews-features-receptor-data]="receptorData"
+      [sxplr-sapiviews-features-receptor-autoradiograph-selected-symbol]="selectedSymbol"
+      #ar="sxplrSapiViewsFeaturesReceptorAR"
+    >
+    </sxplr-sapiviews-features-receptor-autoradiograph>
+  </ng-template>
+
+</mat-card>
+
+
+<!-- download data button template -->
+<ng-template #downloadBtn
+  let-label="label"
+  let-filename="filename"
+  let-receptorCmp="receptorCmp">
+  <button mat-button
+    *ngIf="receptorCmp.dataBlobAvailable"
+    single-file-output
+    [single-file-output-blob]="receptorCmp.dataBlob$ | async"
+    [single-file-output-filename]="filename">
+    <i class="fas fa-download"></i>
+    <span>
+      {{ label }}
+    </span>
+  </button>
+</ng-template>
diff --git a/src/atlasComponents/sapiViews/features/receptors/fingerprint/fingerprint.component.ts b/src/atlasComponents/sapiViews/features/receptors/fingerprint/fingerprint.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..98c5b0076da0350cd0a30fdc01633ee57b6e3bab
--- /dev/null
+++ b/src/atlasComponents/sapiViews/features/receptors/fingerprint/fingerprint.component.ts
@@ -0,0 +1,128 @@
+import { AfterViewInit, Component, ElementRef, EventEmitter, HostListener, Inject, OnChanges, OnDestroy, Output, SimpleChanges, ViewChild } from "@angular/core";
+import { fromEvent, Observable, Subscription } from "rxjs";
+import { distinctUntilChanged, map } from "rxjs/operators";
+import { SAPI } from "src/atlasComponents/sapi";
+import { SapiRegionalFeatureReceptorModel } from "src/atlasComponents/sapi/type";
+import { DARKTHEME } from "src/util/injectionTokens";
+import { BaseReceptor } from "../base";
+
+/**
+ * kg-dataset-dumb-radar requires input to be a different shape
+ * once the the return signature
+ */
+type RequiredType = {
+  receptor: {
+    label: string
+  }
+  density: {
+    mean: number
+    sd: number
+    unit: string
+  }
+}
+
+function transformRadar(input: SapiRegionalFeatureReceptorModel['data']['fingerprints']): RequiredType[]{
+  const listRequired: RequiredType[] = []
+  for (const key in input) {
+    const item = input[key]
+    listRequired.push({
+      receptor: {
+        label: key
+      },
+      density: {
+        mean: item.mean,
+        sd: item.std,
+        unit: item.unit
+      }
+    })
+    
+  }
+  return listRequired
+}
+
+@Component({
+  selector: 'sxplr-sapiviews-features-receptor-fingerprint',
+  templateUrl: './fingerprint.template.html',
+  styleUrls: [
+    './fingerprint.style.css'
+  ],
+  exportAs: "sxplrSapiViewsFeaturesReceptorFP"
+})
+
+export class Fingerprint extends BaseReceptor implements OnChanges, AfterViewInit, OnDestroy{
+
+  @Output('sxplr-sapiviews-features-receptor-fingerprint-receptor-selected')
+  selectReceptor = new EventEmitter<string>()
+
+  @HostListener('click')
+  onClick(){
+    if (this.mouseOverReceptor)  {
+      this.selectReceptor.emit(this.mouseOverReceptor)
+    }
+  }
+
+  async ngOnChanges(simpleChanges: SimpleChanges) {
+    await super.ngOnChanges(simpleChanges)
+  }
+
+  constructor(sapi: SAPI, private el: ElementRef, @Inject(DARKTHEME) public darktheme$: Observable<boolean>){
+    super(sapi)
+  }
+
+  get dumbRadarCmp(){
+    return this.el?.nativeElement?.querySelector('kg-dataset-dumb-radar')
+  }
+
+  private setDumbRadarPlease = false
+  private sub: Subscription[] = []
+  private mouseOverReceptor: string
+
+  ngOnDestroy(){
+    while (this.sub.length > 0) this.sub.pop().unsubscribe()
+  }
+
+  ngAfterViewInit(){
+    if (this.setDumbRadarPlease) {
+      this.rerender()
+    }
+
+    this.sub.push(
+      fromEvent<CustomEvent>(this.el.nativeElement, 'kg-ds-prv-regional-feature-mouseover').pipe(
+        map(ev => ev.detail?.data?.receptor?.label),
+        distinctUntilChanged(),
+      ).subscribe(label => {
+        this.mouseOverReceptor = label
+      })
+    )
+  }
+
+  rerender(): void {
+   
+    if (!this.dumbRadarCmp) {
+      this.setDumbRadarPlease = true
+      return
+    }
+    
+    this.dumbRadarCmp.metaBs = this.receptorData.data.receptor_symbols
+    this.dumbRadarCmp.radar= transformRadar(this.receptorData.data.fingerprints)
+
+    this.dataBlob$.next(this.getDataBlob())
+    this.dataBlobAvailable = true
+    this.setDumbRadarPlease = false
+  }
+
+  private getDataBlob(): Blob {
+    if (!this.receptorData?.data?.fingerprints) throw new Error(`raw data unavailable. Try again later.`)
+    const fingerprints = this.receptorData.data.fingerprints
+    const output: string[] = []
+    output.push(
+      ["name", "mean", "std"].join("\t")
+    )
+    for (const key in fingerprints) {
+      output.push(
+        [key, fingerprints[key].mean, fingerprints[key].std].join("\t")
+      )
+    }
+    return new Blob([output.join("\n")], { type: 'text/tab-separated-values' })
+  }
+}
diff --git a/src/atlasComponents/sapiViews/features/receptors/fingerprint/fingerprint.stories.ts b/src/atlasComponents/sapiViews/features/receptors/fingerprint/fingerprint.stories.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1c3327ac1470cc2a7d650aa3b5196f70958dbbe2
--- /dev/null
+++ b/src/atlasComponents/sapiViews/features/receptors/fingerprint/fingerprint.stories.ts
@@ -0,0 +1,128 @@
+import { CommonModule, DOCUMENT } from "@angular/common"
+import { HttpClientModule } from "@angular/common/http"
+import { Component, EventEmitter, Output } from "@angular/core"
+import { Meta, moduleMetadata, Story } from "@storybook/angular"
+import { SAPI, SapiAtlasModel, SapiParcellationModel, SapiRegionModel, SapiSpaceModel } from "src/atlasComponents/sapi"
+import { getHoc1RightFeatureDetail, getHoc1RightFeatures, getHoc1Right, getHumanAtlas, getJba29, getMni152, provideDarkTheme } from "src/atlasComponents/sapi/stories.base"
+import { SapiRegionalFeatureReceptorModel } from "src/atlasComponents/sapi/type"
+import { AngularMaterialModule } from "src/sharedModules"
+import { appendScriptFactory, APPEND_SCRIPT_TOKEN } from "src/util/constants"
+import { ReceptorViewModule } from ".."
+
+@Component({
+  selector: 'fingerprint-wrapper-cmp',
+  template: `
+  <sxplr-sapiviews-features-receptor-fingerprint
+    [sxplr-sapiviews-features-receptor-atlas]="atlas"
+    [sxplr-sapiviews-features-receptor-parcellation]="parcellation"
+    [sxplr-sapiviews-features-receptor-template]="template"
+    [sxplr-sapiviews-features-receptor-region]="region"
+    [sxplr-sapiviews-features-receptor-featureid]="featureId"
+    [sxplr-sapiviews-features-receptor-data]="feature"
+    (sxplr-sapiviews-features-receptor-fingerprint-receptor-selected)="selectReceptor.emit($event)"
+  >
+  </sxplr-sapiviews-features-receptor-fingerprint>
+  `,
+  styles: [
+    `
+    :host
+    {
+      display: block;
+      width: 20rem;
+      height: 20rem;
+    }
+    `
+  ]
+})
+class FingerprintWrapperCls {
+  atlas: SapiAtlasModel
+  parcellation: SapiParcellationModel
+  template: SapiSpaceModel
+  region: SapiRegionModel
+  feature: SapiRegionalFeatureReceptorModel
+  featureId: string
+
+  @Output()
+  selectReceptor = new EventEmitter()
+}
+
+export default {
+  component: FingerprintWrapperCls,
+  decorators: [
+    moduleMetadata({
+      imports: [
+        CommonModule,
+        AngularMaterialModule,
+        HttpClientModule,
+        ReceptorViewModule,
+      ],
+      providers: [
+        SAPI,
+        {
+          provide: APPEND_SCRIPT_TOKEN,
+          useFactory: appendScriptFactory,
+          deps: [ DOCUMENT ]
+        },
+        ...provideDarkTheme,
+      ],
+      declarations: []
+    })
+  ],
+} as Meta
+
+const Template: Story<FingerprintWrapperCls> = (args: FingerprintWrapperCls, { loaded }) => {
+  const { atlas, parc, space, region, featureId, feature } = loaded
+  return ({
+    props: {
+      ...args,
+      atlas: atlas,
+      parcellation: parc,
+      template: space,
+      region: region,
+      feature,
+      featureId,
+    },
+  })
+}
+
+const loadFeat = async () => {
+  const atlas = await getHumanAtlas()
+  const parc = await getJba29()
+  const region = await getHoc1Right()
+  const space = await getMni152()
+  const features = await getHoc1RightFeatures()
+  const receptorfeat = features.find(f => f['@type'] === "siibra/features/receptor")
+  const feature = await getHoc1RightFeatureDetail(receptorfeat["@id"])
+  return {
+    atlas,
+    parc,
+    space,
+    region,
+    featureId: receptorfeat["@id"],
+    feature
+  }
+}
+
+export const Default = Template.bind({})
+Default.args = {
+
+}
+Default.loaders = [
+  async () => {
+    const { atlas, parc, space, region, featureId } = await loadFeat()
+    return {
+      atlas, parc, space, region, featureId
+    }
+  }
+]
+
+export const LoadViaDirectlyInjectData = Template.bind({})
+LoadViaDirectlyInjectData.loaders = [
+  async () => {
+
+    const { feature } = await loadFeat()
+    return {
+      feature
+    }
+  }
+]
diff --git a/src/atlasComponents/sapiViews/features/receptors/fingerprint/fingerprint.style.css b/src/atlasComponents/sapiViews/features/receptors/fingerprint/fingerprint.style.css
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/atlasComponents/sapiViews/features/receptors/fingerprint/fingerprint.template.html b/src/atlasComponents/sapiViews/features/receptors/fingerprint/fingerprint.template.html
new file mode 100644
index 0000000000000000000000000000000000000000..c315de7303ead0ebb5e0c2032fd4a647d381dd39
--- /dev/null
+++ b/src/atlasComponents/sapiViews/features/receptors/fingerprint/fingerprint.template.html
@@ -0,0 +1,3 @@
+<kg-dataset-dumb-radar
+  [attr.kg-ds-prv-darkmode]="darktheme$ | async">
+</kg-dataset-dumb-radar>
\ No newline at end of file
diff --git a/src/atlasComponents/sapiViews/features/receptors/index.ts b/src/atlasComponents/sapiViews/features/receptors/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7ccaf9d94dbb4e164a98d600c701de5f419ed346
--- /dev/null
+++ b/src/atlasComponents/sapiViews/features/receptors/index.ts
@@ -0,0 +1,4 @@
+export { Autoradiography } from "./autoradiography/autoradiography.component";
+export { Fingerprint } from "./fingerprint/fingerprint.component"
+export { Profile } from "./profile/profile.component"
+export { ReceptorViewModule } from "./module"
diff --git a/src/atlasComponents/sapiViews/features/receptors/module.ts b/src/atlasComponents/sapiViews/features/receptors/module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d5bd04dc6b11f71c1646b4c8b2892c6ddf372f6b
--- /dev/null
+++ b/src/atlasComponents/sapiViews/features/receptors/module.ts
@@ -0,0 +1,55 @@
+import { CommonModule } from "@angular/common";
+import { APP_INITIALIZER, CUSTOM_ELEMENTS_SCHEMA, NgModule } from "@angular/core";
+import { FormsModule } from "@angular/forms";
+import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
+import { SpinnerModule } from "src/components/spinner";
+import { AngularMaterialModule } from "src/sharedModules";
+import { APPEND_SCRIPT_TOKEN } from "src/util/constants";
+import { ZipFilesOutputModule } from "src/zipFilesOutput/module";
+import { Autoradiography } from "./autoradiography/autoradiography.component";
+import { Entry } from "./entry/entry.component";
+import { Fingerprint } from "./fingerprint/fingerprint.component"
+import { Profile } from "./profile/profile.component"
+
+@NgModule({
+  imports: [
+    CommonModule,
+    AngularMaterialModule,
+    FormsModule,
+    BrowserAnimationsModule,
+    SpinnerModule,
+    ZipFilesOutputModule,
+  ],
+  declarations: [
+    Autoradiography,
+    Fingerprint,
+    Profile,
+    Entry,
+  ],
+  exports: [
+    Autoradiography,
+    Fingerprint,
+    Profile,
+    Entry,
+  ],
+  providers: [{
+    provide: APP_INITIALIZER,
+    multi: true,
+    useFactory: (appendScriptFn: (url: string) => Promise<any>) => {
+
+      const libraries = [
+        'https://cdnjs.cloudflare.com/ajax/libs/d3/6.2.0/d3.min.js',
+        'https://cdnjs.cloudflare.com/ajax/libs/mathjax/3.1.2/es5/tex-svg.js'
+      ]
+      return () => Promise.all(libraries.map(appendScriptFn))
+    },
+    deps: [
+      APPEND_SCRIPT_TOKEN
+    ]
+  }],
+  schemas: [
+    CUSTOM_ELEMENTS_SCHEMA,
+  ]
+})
+
+export class ReceptorViewModule{}
diff --git a/src/atlasComponents/sapiViews/features/receptors/profile/profile.component.ts b/src/atlasComponents/sapiViews/features/receptors/profile/profile.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..864584597568df263ed93be923f757bcd585f125
--- /dev/null
+++ b/src/atlasComponents/sapiViews/features/receptors/profile/profile.component.ts
@@ -0,0 +1,91 @@
+import { AfterViewInit, Component, ElementRef, Inject, Input, OnChanges, SimpleChanges } from "@angular/core";
+import { Observable } from "rxjs";
+import { SAPI } from "src/atlasComponents/sapi";
+import { PARSE_TYPEDARRAY } from "src/atlasComponents/sapi/sapi.service";
+import { DARKTHEME } from "src/util/injectionTokens";
+import { BaseReceptor } from "../base";
+
+@Component({
+  selector: `sxplr-sapiviews-features-receptor-profile`,
+  templateUrl: './profile.template.html',
+  styleUrls: [
+    './profile.style.css'
+  ],
+  exportAs: "sxplrSapiViewsFeaturesReceptorProfile"
+})
+
+export class Profile extends BaseReceptor implements AfterViewInit, OnChanges{
+
+  @Input('sxplr-sapiviews-features-receptor-profile-selected-symbol')
+  selectedSymbol: string
+
+  private pleaseRender = false
+  dumbLineData: Record<number, number>
+
+  constructor(sapi: SAPI, private el: ElementRef, @Inject(DARKTHEME) public darktheme$: Observable<boolean> ){
+    super(sapi)
+  }
+
+  async ngOnChanges(simpleChanges: SimpleChanges) {
+    await super.ngOnChanges(simpleChanges)
+    if (!this.receptorData) {
+      return
+    }
+    if (this.selectedSymbol) {
+      this.rerender()
+    }
+  }
+
+  ngAfterViewInit(): void {
+    if (this.pleaseRender) {
+      this.rerender()
+    }
+  }
+
+  get dumbLineCmp(){
+    return this.el?.nativeElement?.querySelector('kg-dataset-dumb-line')
+  }
+
+  async rerender() {
+    this.dataBlobAvailable = false
+    if (!this.dumbLineCmp) {
+      this.pleaseRender = true
+      return
+    }
+    this.pleaseRender = false
+    this.dumbLineData = null
+    
+    if (this.receptorData?.data?.profiles?.[this.selectedSymbol]) {
+      const { rawArray } = await this.sapi.processNpArrayData<PARSE_TYPEDARRAY.RAW_ARRAY>(
+        this.receptorData.data.profiles[this.selectedSymbol].density,
+        PARSE_TYPEDARRAY.RAW_ARRAY
+      )
+      if (rawArray.length !== 1) {
+        this.error = `expected rawArray.length to be 1, but is ${rawArray.length} instead`
+        return
+      }
+      const prof = rawArray[0]
+      this.dumbLineData = {}
+      for (const idx in prof) {
+        this.dumbLineData[idx] = prof[idx]
+      }
+      this.dataBlob$.next(this.getDataBlob())
+      this.dataBlobAvailable = true
+      this.dumbLineCmp.profileBs = this.dumbLineData
+    }
+  }
+
+  private getDataBlob(): Blob {
+    if (!this.dumbLineData) throw new Error(`data has not been populated. Perhaps wait until render finishes?`)
+    const output: string[] = []
+    output.push(
+      ["cortical depth (%)", "receptor density (fmol/mg)"].join("\t")
+    )
+    for (const key in this.dumbLineData) {
+      output.push(
+        [key, this.dumbLineData[key]].join("\t")
+      )
+    }
+    return new Blob([output.join("\n")], { type: 'text/tab-separated-values' })
+  }
+}
diff --git a/src/atlasComponents/sapiViews/features/receptors/profile/profile.stories.ts b/src/atlasComponents/sapiViews/features/receptors/profile/profile.stories.ts
new file mode 100644
index 0000000000000000000000000000000000000000..83d309f52d008d14183329f0539cf179c0354867
--- /dev/null
+++ b/src/atlasComponents/sapiViews/features/receptors/profile/profile.stories.ts
@@ -0,0 +1,155 @@
+import { CommonModule, DOCUMENT } from "@angular/common"
+import { HttpClientModule } from "@angular/common/http"
+import { Component, NgZone, ViewChild } from "@angular/core"
+import { FormsModule } from "@angular/forms"
+import { BrowserAnimationsModule } from "@angular/platform-browser/animations"
+import { Meta, moduleMetadata, Story } from "@storybook/angular"
+import { BehaviorSubject, Subject } from "rxjs"
+import { SAPI, SapiAtlasModel, SapiParcellationModel, SapiRegionModel, SapiSpaceModel } from "src/atlasComponents/sapi"
+import { addAddonEventListener, getHoc1RightFeatureDetail, getHoc1RightFeatures, getHoc1Right, getHumanAtlas, getJba29, getMni152, provideDarkTheme } from "src/atlasComponents/sapi/stories.base"
+import { SapiRegionalFeatureReceptorModel } from "src/atlasComponents/sapi/type"
+import { AngularMaterialModule } from "src/sharedModules"
+import { appendScriptFactory, APPEND_SCRIPT_TOKEN } from "src/util/constants"
+import { DARKTHEME } from "src/util/injectionTokens"
+import { ReceptorViewModule } from ".."
+import { Profile } from "./profile.component"
+
+@Component({
+  selector: 'autoradiograph-wrapper-cmp',
+  template: `
+  <mat-form-field appearance="fill">
+    <mat-select [(ngModel)]="selectedSymbol">
+      <mat-option value="" disabled>
+        --select--
+      </mat-option>
+
+      <mat-option [value]="option"
+        *ngFor="let option of options">
+        {{ option }}
+      </mat-option>
+    </mat-select>
+  </mat-form-field>
+  <sxplr-sapiviews-features-receptor-profile
+    class="d-inline-block w-100 h-100"
+    [sxplr-sapiviews-features-receptor-atlas]="atlas"
+    [sxplr-sapiviews-features-receptor-parcellation]="parcellation"
+    [sxplr-sapiviews-features-receptor-template]="template"
+    [sxplr-sapiviews-features-receptor-region]="region"
+    [sxplr-sapiviews-features-receptor-featureid]="featureId"
+    [sxplr-sapiviews-features-receptor-data]="feature"
+    [sxplr-sapiviews-features-receptor-profile-selected-symbol]="selectedSymbol"
+  >
+  </sxplr-sapiviews-features-receptor-profile>
+  `,
+  styles: [
+    `
+    :host
+    {
+      display: block;
+      max-width: 24rem;
+      max-height: 24rem;
+    }
+    `
+  ]
+})
+class ProfileWrapperCls {
+  atlas: SapiAtlasModel
+  parcellation: SapiParcellationModel
+  template: SapiSpaceModel
+  region: SapiRegionModel
+
+  feature: SapiRegionalFeatureReceptorModel
+  featureId: string
+
+  @ViewChild(Profile)
+  profile: Profile
+
+  selectedSymbol: string
+
+  get options(){
+    return Object.keys(this.feature?.data?.profiles || this.profile?.receptorData?.data?.profiles || {})
+  }
+}
+
+export default {
+  component: ProfileWrapperCls,
+  decorators: [
+    moduleMetadata({
+      imports: [
+        CommonModule,
+        AngularMaterialModule,
+        HttpClientModule,
+        BrowserAnimationsModule,
+        FormsModule,
+        ReceptorViewModule,
+      ],
+      providers: [
+        SAPI,
+        {
+          provide: APPEND_SCRIPT_TOKEN,
+          useFactory: appendScriptFactory,
+          deps: [ DOCUMENT ]
+        },
+        ...provideDarkTheme,
+      ],
+      declarations: []
+    })
+  ],
+} as Meta
+
+const Template: Story<ProfileWrapperCls> = (args: ProfileWrapperCls, { loaded }) => {
+  const { atlas, parc, space, region, featureId, feature } = loaded
+  return ({
+    props: {
+      ...args,
+      atlas: atlas,
+      parcellation: parc,
+      template: space,
+      region: region,
+      feature,
+      featureId,
+    },
+  })
+}
+
+const loadFeat = async () => {
+  const atlas = await getHumanAtlas()
+  const parc = await getJba29()
+  const region = await getHoc1Right()
+  const space = await getMni152()
+  const features = await getHoc1RightFeatures()
+  const receptorfeat = features.find(f => f['@type'] === "siibra/features/receptor")
+  const feature = await getHoc1RightFeatureDetail(receptorfeat["@id"])
+  return {
+    atlas,
+    parc,
+    space,
+    region,
+    featureId: receptorfeat["@id"],
+    feature
+  }
+}
+
+export const Default = Template.bind({})
+Default.args = {
+
+}
+Default.loaders = [
+  async () => {
+    const { atlas, parc, space, region, featureId } = await loadFeat()
+    return {
+      atlas, parc, space, region, featureId
+    }
+  }
+]
+
+export const LoadViaDirectlyInjectData = Template.bind({})
+LoadViaDirectlyInjectData.loaders = [
+  async () => {
+
+    const { feature } = await loadFeat()
+    return {
+      feature
+    }
+  }
+]
diff --git a/src/atlasComponents/sapiViews/features/receptors/profile/profile.style.css b/src/atlasComponents/sapiViews/features/receptors/profile/profile.style.css
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/atlasComponents/sapiViews/features/receptors/profile/profile.template.html b/src/atlasComponents/sapiViews/features/receptors/profile/profile.template.html
new file mode 100644
index 0000000000000000000000000000000000000000..182db6578a311b5661b8a571c927ab11d6c149ae
--- /dev/null
+++ b/src/atlasComponents/sapiViews/features/receptors/profile/profile.template.html
@@ -0,0 +1,3 @@
+<kg-dataset-dumb-line
+  [attr.kg-ds-prv-darkmode]="darktheme$ | async">
+</kg-dataset-dumb-line>
\ No newline at end of file
diff --git a/src/atlasComponents/sapiViews/features/voi/index.ts b/src/atlasComponents/sapiViews/features/voi/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..55b2fbc01ff6805ae01ef334e88d52db40ded951
--- /dev/null
+++ b/src/atlasComponents/sapiViews/features/voi/index.ts
@@ -0,0 +1,2 @@
+export { SapiViewsFeaturesVoiModule } from "./module"
+export { SapiViewsFeaturesVoiQuery } from "./voiQuery.directive"
diff --git a/src/atlasComponents/sapiViews/features/voi/module.ts b/src/atlasComponents/sapiViews/features/voi/module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e9817aacad60533082cbf4be70c62831d50727cd
--- /dev/null
+++ b/src/atlasComponents/sapiViews/features/voi/module.ts
@@ -0,0 +1,19 @@
+import { CommonModule } from "@angular/common";
+import { NgModule } from "@angular/core";
+import { SAPIModule } from "src/atlasComponents/sapi/module";
+import { SapiViewsFeaturesVoiQuery } from "./voiQuery.directive";
+
+@NgModule({
+  imports: [
+    CommonModule,
+    SAPIModule,
+  ],
+  declarations: [
+    SapiViewsFeaturesVoiQuery,
+  ],
+  exports: [
+    SapiViewsFeaturesVoiQuery
+  ]
+})
+
+export class SapiViewsFeaturesVoiModule{}
diff --git a/src/atlasComponents/sapiViews/features/voi/voiQuery.directive.ts b/src/atlasComponents/sapiViews/features/voi/voiQuery.directive.ts
new file mode 100644
index 0000000000000000000000000000000000000000..623011d89b1d2385fec8ca30c3456082056128e7
--- /dev/null
+++ b/src/atlasComponents/sapiViews/features/voi/voiQuery.directive.ts
@@ -0,0 +1,190 @@
+import { Directive, EventEmitter, Inject, Input, OnChanges, OnDestroy, Optional, Output, SimpleChanges } from "@angular/core";
+import { interval, merge, Observable, of, Subject, Subscription } from "rxjs";
+import { debounce, debounceTime, distinctUntilChanged, filter, pairwise, shareReplay, startWith, switchMap, take, tap } from "rxjs/operators";
+import { AnnotationLayer, TNgAnnotationPoint, TNgAnnotationAABBox } from "src/atlasComponents/annotations";
+import { SAPI } from "src/atlasComponents/sapi/sapi.service";
+import { BoundingBoxConcept, SapiAtlasModel, SapiSpaceModel, SapiVOIDataResponse, OpenMINDSCoordinatePoint } from "src/atlasComponents/sapi/type";
+import { ClickInterceptor, CLICK_INTERCEPTOR_INJECTOR } from "src/util";
+import { arrayEqual } from "src/util/array";
+
+@Directive({
+  selector: '[sxplr-sapiviews-features-voi-query]',
+  exportAs: 'sxplrSapiViewsFeaturesVoiQuery'
+})
+
+export class SapiViewsFeaturesVoiQuery implements OnChanges, OnDestroy{
+
+  static VOI_LAYER_NAME = 'voi-annotation-layer'
+  static VOI_ANNOTATION_COLOR = "#ffff00"
+  private voiQuerySpec = new Subject<{
+    atlas: SapiAtlasModel
+    space: SapiSpaceModel
+    bbox: BoundingBoxConcept
+  }>()
+
+  private canFetchVoi(){
+    return !!this.atlas && !!this.space && !!this.bbox
+  }
+  
+  @Input('sxplr-sapiviews-features-voi-query-atlas')
+  atlas: SapiAtlasModel
+
+  @Input('sxplr-sapiviews-features-voi-query-space')
+  space: SapiSpaceModel
+
+  @Input('sxplr-sapiviews-features-voi-query-bbox')
+  bbox: BoundingBoxConcept
+
+  @Output('sxplr-sapiviews-features-voi-query-onhover')
+  onhover = new EventEmitter<SapiVOIDataResponse>()
+
+  @Output('sxplr-sapiviews-features-voi-query-onclick')
+  onclick = new EventEmitter<SapiVOIDataResponse>()
+
+  public busy$ = new EventEmitter<boolean>()
+  public features$: Observable<SapiVOIDataResponse[]> = this.voiQuerySpec.pipe(
+    debounceTime(160),
+    tap(() => this.busy$.emit(true)),
+    switchMap(({ atlas, bbox, space }) => {
+      if (!this.canFetchVoi()) {
+        return of([])
+      }
+      return merge(
+        of([]),
+        this.sapi.getSpace(atlas["@id"], space["@id"]).getFeatures({ bbox: JSON.stringify(bbox) }).pipe(
+          tap(val => {
+            this.busy$.emit(false)
+          })
+        )
+      )
+    }),
+    startWith([]),
+    shareReplay(1)
+  )
+
+  private hoveredFeat: SapiVOIDataResponse
+  private onDestroyCb: (() => void)[] = []
+  private subscription: Subscription[] = []
+  ngOnChanges(simpleChanges: SimpleChanges): void {
+    if (simpleChanges.space) {
+      this.voiBBoxSvc = null
+    }
+    const {
+      atlas,
+      space,
+      bbox
+    } = this
+    this.voiQuerySpec.next({ atlas, space, bbox })
+  }
+  ngOnDestroy(): void {
+    if (this.voiBBoxSvc) this.voiBBoxSvc.dispose()
+    while (this.subscription.length > 0) this.subscription.pop().unsubscribe()
+    while (this.voiSubs.length > 0) this.voiSubs.pop().unsubscribe()
+    while(this.onDestroyCb.length > 0) this.onDestroyCb.pop()()
+  }
+
+  handleOnHoverFeature({ id }: { id?: string }){
+    const ann = this.annotationIdToFeature.get(id)
+    this.hoveredFeat = ann
+    this.onhover.emit(ann)
+  }
+
+  private voiSubs: Subscription[] = []
+  private _voiBBoxSvc: AnnotationLayer
+  get voiBBoxSvc(): AnnotationLayer {
+    if (this._voiBBoxSvc) return this._voiBBoxSvc
+    try {
+      const layer = AnnotationLayer.Get(
+        SapiViewsFeaturesVoiQuery.VOI_LAYER_NAME,
+        SapiViewsFeaturesVoiQuery.VOI_ANNOTATION_COLOR
+      )
+      this._voiBBoxSvc = layer
+      this.voiSubs.push(
+        layer.onHover.subscribe(val => this.handleOnHoverFeature(val || {}))
+      )
+      return layer
+    } catch (e) {
+      return null
+    }
+  }
+  set voiBBoxSvc(val) {
+    if (!!val) {
+      throw new Error(`cannot set voiBBoxSvc directly`)
+    }
+    while (this.voiSubs.length > 0) this.voiSubs.pop().unsubscribe()
+    this._voiBBoxSvc && this._voiBBoxSvc.dispose()
+    this._voiBBoxSvc = null
+  }
+
+  constructor(
+    private sapi: SAPI,
+    @Optional() @Inject(CLICK_INTERCEPTOR_INJECTOR) clickInterceptor: ClickInterceptor,
+  ){
+    const handle = () => {
+      if (!this.hoveredFeat) return true
+      this.onclick.emit(this.hoveredFeat)
+      return false
+    }
+    this.onDestroyCb.push(
+      () => clickInterceptor.deregister(handle)
+    )
+    clickInterceptor.register(handle)
+    this.subscription.push(
+      this.features$.pipe(
+        startWith([] as SapiVOIDataResponse[]),
+        distinctUntilChanged(arrayEqual((o, n) => o["@id"] === n["@id"])),
+        pairwise(),
+        debounce(() => 
+          interval(16).pipe(
+            filter(() => !!this.voiBBoxSvc),
+            take(1),
+          )
+        ),
+      ).subscribe(([ prev, curr ]) => {
+        for (const v of prev) {
+          const box = this.pointsToAABB(v.location.maxpoint, v.location.minpoint)
+          const point = this.pointToPoint(v.location.center)
+          this.annotationIdToFeature.delete(box.id)
+          this.annotationIdToFeature.delete(point.id)
+          if (!this.voiBBoxSvc) continue
+          for (const ann of [box, point]) {
+            this.voiBBoxSvc.removeAnnotation({
+              id: ann.id
+            })
+          }
+        }
+        for (const v of curr) {
+          const box = this.pointsToAABB(v.location.maxpoint, v.location.minpoint)
+          const point = this.pointToPoint(v.location.center)
+          this.annotationIdToFeature.set(box.id, v)
+          this.annotationIdToFeature.set(point.id, v)
+          if (!this.voiBBoxSvc) {
+            throw new Error(`annotation is expected to be added, but annotation layer cannot be instantiated.`)
+          }
+          for (const ann of [box, point]) {
+            this.voiBBoxSvc.updateAnnotation(ann)
+          }
+        }
+        if (this.voiBBoxSvc) this.voiBBoxSvc.setVisible(true)
+      })
+    )
+  }
+
+  private annotationIdToFeature = new Map<string, SapiVOIDataResponse>()
+
+  private pointsToAABB(pointA: OpenMINDSCoordinatePoint, pointB: OpenMINDSCoordinatePoint): TNgAnnotationAABBox{
+    return {
+      id: `${SapiViewsFeaturesVoiQuery.VOI_LAYER_NAME}:${pointA["@id"]}:${pointB["@id"]}`,
+      pointA: pointA.coordinates.map(v => v.value * 1e6) as [number, number, number],
+      pointB: pointB.coordinates.map(v => v.value * 1e6) as [number, number, number],
+      type: "aabbox"
+    }
+  }
+  private pointToPoint(point: OpenMINDSCoordinatePoint): TNgAnnotationPoint {
+    return {
+      id: `${SapiViewsFeaturesVoiQuery.VOI_LAYER_NAME}:${point["@id"]}`,
+      point: point.coordinates.map(v => v.value * 1e6) as [number, number, number],
+      type: "point"
+    }
+  }
+}
diff --git a/src/atlasComponents/sapiViews/index.ts b/src/atlasComponents/sapiViews/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9fdab95aa0bcf8ede9d104573c674ec8f243483e
--- /dev/null
+++ b/src/atlasComponents/sapiViews/index.ts
@@ -0,0 +1,4 @@
+export {
+  SapiViewsModule
+} from "./module"
+export { SapiViewsUtilModule } from "./util"
\ No newline at end of file
diff --git a/src/atlasComponents/sapiViews/module.ts b/src/atlasComponents/sapiViews/module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ff029f0362be7205bec3a45957debcc25f87197c
--- /dev/null
+++ b/src/atlasComponents/sapiViews/module.ts
@@ -0,0 +1,15 @@
+import { NgModule } from "@angular/core";
+import { SapiViewsCoreModule } from "./core";
+import { SapiViewsFeaturesModule } from "./features";
+
+@NgModule({
+  imports: [
+    SapiViewsFeaturesModule,
+    SapiViewsCoreModule,
+  ],
+  exports: [
+    SapiViewsFeaturesModule,
+    SapiViewsCoreModule,
+  ]
+})
+export class SapiViewsModule{}
\ No newline at end of file
diff --git a/src/atlasComponents/sapiViews/richDataset.stories.ts b/src/atlasComponents/sapiViews/richDataset.stories.ts
new file mode 100644
index 0000000000000000000000000000000000000000..03ac405b682240aedd06eac97db2b8d659e55d00
--- /dev/null
+++ b/src/atlasComponents/sapiViews/richDataset.stories.ts
@@ -0,0 +1,138 @@
+import { CommonModule } from "@angular/common"
+import { HttpClientModule } from "@angular/common/http"
+import { Component } from "@angular/core"
+import { provideMockStore } from "@ngrx/store/testing"
+import { Meta, moduleMetadata, Story } from "@storybook/angular"
+import { SAPI } from "src/atlasComponents/sapi"
+import { getHoc1RightFeatureDetail, getHoc1RightFeatures, getHoc1Right, getHumanAtlas, getJba29, getMni152, provideDarkTheme, getMni152SpatialFeatureHoc1Right } from "src/atlasComponents/sapi/stories.base"
+import { AngularMaterialModule } from "src/sharedModules"
+import { cleanIeegSessionDatasets, SapiAtlasModel, SapiFeatureModel, SapiParcellationModel, SapiRegionModel, SapiSpaceModel } from "../sapi/type"
+import { SapiViewsCoreDatasetModule } from "./core/datasets"
+import { SapiViewsFeaturesModule } from "./features"
+
+@Component({
+  selector: `rich-dataset-wrapper-cmp`,
+  template: `
+  <div *ngIf="!dataset">
+    Dataset must be provided
+  </div>
+  <mat-card *ngIf="dataset">
+    <sxplr-sapiviews-core-datasets-dataset
+      [sxplr-sapiviews-core-datasets-dataset-input]="dataset">
+    </sxplr-sapiviews-core-datasets-dataset>
+
+    <sxplr-sapiviews-features-entry
+      [sxplr-sapiviews-features-entry-atlas]="atlas"
+      [sxplr-sapiviews-features-entry-space]="template"
+      [sxplr-sapiviews-features-entry-parcellation]="parcellation"
+      [sxplr-sapiviews-features-entry-region]="region"
+      [sxplr-sapiviews-features-entry-feature]="dataset">
+    </sxplr-sapiviews-features-entry>
+  </mat-card>
+  
+  `,
+  styles: [
+    `mat-card { max-width: 40rem; }`
+  ]
+})
+
+class RichDatasetWrapperCmp {
+  atlas: SapiAtlasModel
+  parcellation: SapiParcellationModel
+  template: SapiSpaceModel
+  region: SapiRegionModel
+  dataset: SapiFeatureModel
+}
+
+export default {
+  component: RichDatasetWrapperCmp,
+  decorators: [
+    moduleMetadata({
+      imports: [
+        AngularMaterialModule,
+        CommonModule,
+        HttpClientModule,
+        SapiViewsCoreDatasetModule,
+        SapiViewsFeaturesModule,
+      ],
+      providers: [
+        SAPI,
+        provideMockStore(),
+        ...provideDarkTheme,
+      ],
+      declarations: []
+    })
+  ],
+} as Meta
+
+const Template: Story<RichDatasetWrapperCmp> = (args: RichDatasetWrapperCmp, { loaded }) => {
+  const {
+    
+    atlas, 
+    parcellation, 
+    template, 
+    region, 
+    feature
+  } = loaded
+  return ({
+    props: {
+      atlas, 
+      parcellation, 
+      template, 
+      region, 
+      dataset: feature,
+    },
+  })
+}
+
+const loadRegionMetadata = async () => {
+
+  const atlas = await getHumanAtlas()
+  const parcellation = await getJba29()
+  const template = await getMni152()
+  const region = await getHoc1Right()
+  return {
+    atlas, 
+    parcellation, 
+    template, 
+    region, 
+  }
+}
+
+const loadFeat = async () => {
+  const features = await getHoc1RightFeatures()
+  return { features }
+}
+
+export const ReceptorDataset = Template.bind({})
+ReceptorDataset.args = {
+
+}
+ReceptorDataset.loaders = [
+  async () => {
+    return await loadRegionMetadata()
+  },
+  async () => {
+    const { features } = await loadFeat()
+    const receptorfeat = features.find(f => f['@type'] === "siibra/features/receptor")
+    const feature = await getHoc1RightFeatureDetail(receptorfeat["@id"])
+    return { feature }
+  }
+]
+
+export const IeegDataset = Template.bind({})
+IeegDataset.args = {
+
+}
+IeegDataset.loaders = [
+  async () => {
+    return await loadRegionMetadata()
+  },
+  async () => {
+    const features = await getMni152SpatialFeatureHoc1Right()
+    const spatialFeats = features.filter(f => f["@type"] === "siibra/features/ieegSession")
+    const feature = cleanIeegSessionDatasets(spatialFeats)[0]
+    
+    return { feature }
+  }
+]
diff --git a/src/util/pipes/addUnitAndJoin.pipe.ts b/src/atlasComponents/sapiViews/util/addUnitAndJoin.pipe.ts
similarity index 100%
rename from src/util/pipes/addUnitAndJoin.pipe.ts
rename to src/atlasComponents/sapiViews/util/addUnitAndJoin.pipe.ts
diff --git a/src/atlasComponents/sapiViews/util/equality.pipe.ts b/src/atlasComponents/sapiViews/util/equality.pipe.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8251d641fed51ac99384c6470af5623e0c37451c
--- /dev/null
+++ b/src/atlasComponents/sapiViews/util/equality.pipe.ts
@@ -0,0 +1,16 @@
+import { Pipe } from "@angular/core";
+
+type TTrackBy<T, O> = (input: T) => O
+
+const defaultTrackBy: TTrackBy<unknown, unknown> = i => i
+
+@Pipe({
+  name: 'equality',
+  pure: true
+})
+
+export class EqualityPipe<T>{
+  public transform(c1: T, c2: T, trackBy: TTrackBy<T, unknown> = defaultTrackBy): boolean {
+    return trackBy(c1) === trackBy(c2)
+  }
+}
diff --git a/src/util/pipes/includes.pipe.ts b/src/atlasComponents/sapiViews/util/includes.pipe.ts
similarity index 100%
rename from src/util/pipes/includes.pipe.ts
rename to src/atlasComponents/sapiViews/util/includes.pipe.ts
diff --git a/src/atlasComponents/sapiViews/util/index.ts b/src/atlasComponents/sapiViews/util/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..36e2c654710830f0df9fdbbf0ea024f6a200545c
--- /dev/null
+++ b/src/atlasComponents/sapiViews/util/index.ts
@@ -0,0 +1 @@
+export { SapiViewsUtilModule } from "./module"
\ No newline at end of file
diff --git a/src/atlasComponents/sapiViews/util/module.ts b/src/atlasComponents/sapiViews/util/module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..53a3a88c549821dfe9a7ee654c93dac7747cd368
--- /dev/null
+++ b/src/atlasComponents/sapiViews/util/module.ts
@@ -0,0 +1,34 @@
+import { NgModule } from "@angular/core";
+import { AddUnitAndJoin } from "./addUnitAndJoin.pipe";
+import { EqualityPipe } from "./equality.pipe";
+import { IncludesPipe } from "./includes.pipe";
+import { NumbersPipe } from "./numbers.pipe";
+import { ParcellationSupportedInCurrentSpace } from "./parcellationSupportedInCurrentSpace.pipe";
+import { ParcellationSupportedInSpacePipe } from "./parcellationSupportedInSpace.pipe";
+import { ParseDoiPipe } from "./parseDoi.pipe";
+import { SpaceSupportedInCurrentParcellationPipe } from "./spaceSupportedInCurrentParcellation.pipe";
+
+@NgModule({
+  declarations: [
+    EqualityPipe,
+    ParseDoiPipe,
+    NumbersPipe,
+    AddUnitAndJoin,
+    IncludesPipe,
+    ParcellationSupportedInSpacePipe,
+    ParcellationSupportedInCurrentSpace,
+    SpaceSupportedInCurrentParcellationPipe,
+  ],
+  exports: [
+    EqualityPipe,
+    ParseDoiPipe,
+    NumbersPipe,
+    AddUnitAndJoin,
+    IncludesPipe,
+    ParcellationSupportedInSpacePipe,
+    ParcellationSupportedInCurrentSpace,
+    SpaceSupportedInCurrentParcellationPipe,
+  ]
+})
+
+export class SapiViewsUtilModule{}
\ No newline at end of file
diff --git a/src/util/pipes/numbers.pipe.ts b/src/atlasComponents/sapiViews/util/numbers.pipe.ts
similarity index 52%
rename from src/util/pipes/numbers.pipe.ts
rename to src/atlasComponents/sapiViews/util/numbers.pipe.ts
index 0390ea90d1a526faa421629fbe01911a5e6832a1..e96164a92178de9fa8f7d28fcf28331739fb496b 100644
--- a/src/util/pipes/numbers.pipe.ts
+++ b/src/atlasComponents/sapiViews/util/numbers.pipe.ts
@@ -1,12 +1,12 @@
 import { Pipe, PipeTransform } from "@angular/core";
 
 @Pipe({
-  name: 'nmToMm',
+  name: 'numbers',
   pure: true
 })
 
-export class NmToMm implements PipeTransform{
+export class NumbersPipe implements PipeTransform{
   public transform(nums: number[], decimal: number = 2): number[] {
-    return nums.map(num => (num / 1e6).toFixed(decimal)).map(Number)
+    return nums.map(num => num.toFixed(decimal)).map(Number)
   }
-}
\ No newline at end of file
+}
diff --git a/src/atlasComponents/sapiViews/util/parcellationSupportedInCurrentSpace.pipe.ts b/src/atlasComponents/sapiViews/util/parcellationSupportedInCurrentSpace.pipe.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3fd1069f0b13c69822d115aba804ff93e81c6c27
--- /dev/null
+++ b/src/atlasComponents/sapiViews/util/parcellationSupportedInCurrentSpace.pipe.ts
@@ -0,0 +1,37 @@
+import { Pipe, PipeTransform } from "@angular/core";
+import { select, Store } from "@ngrx/store";
+import { Observable } from "rxjs";
+import { switchMap } from "rxjs/operators";
+import { SAPI } from "src/atlasComponents/sapi/sapi.service";
+import { SapiParcellationModel } from "src/atlasComponents/sapi/type";
+import { atlasSelection } from "src/state";
+import { ParcellationSupportedInSpacePipe } from "./parcellationSupportedInSpace.pipe"
+
+@Pipe({
+  name: 'parcellationSupportedInCurrentSpace',
+  /**
+   * the pipe is not exactly pure, since it makes http call
+   * but for the sake of angular change detection, this is suitable
+   * since the result should only change on input change
+   */
+  pure: true
+})
+
+export class ParcellationSupportedInCurrentSpace implements PipeTransform{
+
+  private transformPipe = new ParcellationSupportedInSpacePipe(this.sapi)
+
+  private selectedTemplate$ = this.store.pipe(
+    select(atlasSelection.selectors.selectedTemplate)
+  )
+  constructor(
+    private store: Store,
+    private sapi: SAPI,
+  ){}
+
+  public transform(parcellation: SapiParcellationModel): Observable<boolean> {
+    return this.selectedTemplate$.pipe(
+      switchMap(tmpl => this.transformPipe.transform(parcellation, tmpl))
+    )
+  }
+}
diff --git a/src/atlasComponents/sapiViews/util/parcellationSupportedInSpace.pipe.ts b/src/atlasComponents/sapiViews/util/parcellationSupportedInSpace.pipe.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f16e6a0a82d991ca8955e84f9ed4a15fe14e2ab4
--- /dev/null
+++ b/src/atlasComponents/sapiViews/util/parcellationSupportedInSpace.pipe.ts
@@ -0,0 +1,47 @@
+import { Pipe, PipeTransform } from "@angular/core";
+import { Observable, of } from "rxjs";
+import { map } from "rxjs/operators";
+import { SAPIParcellation } from "src/atlasComponents/sapi/core";
+import { SAPI } from "src/atlasComponents/sapi/sapi.service";
+import { SapiParcellationModel, SapiSpaceModel } from "src/atlasComponents/sapi/type";
+
+export const knownExceptions = {
+  supported: {
+    /**
+     * jba29
+     */
+    'minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579-290': [
+      /**
+       * big brain
+       */
+      'minds/core/referencespace/v1.0.0/a1655b99-82f1-420f-a3c2-fe80fd4c8588'
+    ]
+  }
+}
+
+@Pipe({
+  name: 'parcellationSuppportedInSpace',
+  pure: false,
+})
+
+export class ParcellationSupportedInSpacePipe implements PipeTransform{
+
+  constructor(private sapi: SAPI){}
+
+  public transform(parc: SapiParcellationModel|string, tmpl: SapiSpaceModel|string): Observable<boolean> {
+    const parcId = typeof parc === "string"
+      ? parc
+      : parc["@id"]
+    const tmplId = typeof tmpl === "string"
+      ? tmpl
+      : tmpl["@id"]
+    for (const key in knownExceptions.supported) {
+      if (key === parcId && knownExceptions.supported[key].indexOf(tmplId) >= 0) {
+        return of(true)
+      }
+    }
+    return this.sapi.registry.get<SAPIParcellation>(parcId).getVolumes().pipe(
+      map(volumes => volumes.some(v => v.data.space["@id"] === tmplId))
+    )
+  }
+}
diff --git a/src/atlasComponents/sapiViews/util/parseDoi.pipe.spec.ts b/src/atlasComponents/sapiViews/util/parseDoi.pipe.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..866f3c40cad50af75fa45abfe849f8af9a2e5712
--- /dev/null
+++ b/src/atlasComponents/sapiViews/util/parseDoi.pipe.spec.ts
@@ -0,0 +1,17 @@
+import {} from 'jasmine'
+import { ParseDoiPipe } from './parseDoi.pipe'
+
+describe('doiPipe.pipe.ts', () => {
+  const pipe = new ParseDoiPipe()
+  describe('DoiParsePIpe' , () => {
+    it('should parse string without prefix by appending doi prefix', () => {
+      const result = pipe.transform('123.456')
+      expect(result).toBe(`https://doi.org/123.456`)
+    })
+
+    it('should not append doi prefix if the first argument leads by http or https', () => {
+      expect(pipe.transform('http://google.com')).toBe('http://google.com')
+      expect(pipe.transform('https://google.com')).toBe('https://google.com')
+    })
+  })
+})
diff --git a/src/atlasComponents/sapiViews/util/parseDoi.pipe.ts b/src/atlasComponents/sapiViews/util/parseDoi.pipe.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1a4d87cbc9bb37216e379e5e80a4360eacfb055a
--- /dev/null
+++ b/src/atlasComponents/sapiViews/util/parseDoi.pipe.ts
@@ -0,0 +1,12 @@
+import { Pipe, PipeTransform } from "@angular/core";
+
+@Pipe({
+  name: 'parseDoi',
+})
+
+export class ParseDoiPipe implements PipeTransform {
+  public transform(s: string, prefix: string = 'https://doi.org/') {
+    const hasProtocol = /^https?:\/\//.test(s)
+    return `${hasProtocol ? '' : prefix}${s}`
+  }
+}
diff --git a/src/atlasComponents/sapiViews/util/spaceSupportedInCurrentParcellation.pipe.ts b/src/atlasComponents/sapiViews/util/spaceSupportedInCurrentParcellation.pipe.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e462fcc6bf41fc412852a9ef8d3404023eff96d6
--- /dev/null
+++ b/src/atlasComponents/sapiViews/util/spaceSupportedInCurrentParcellation.pipe.ts
@@ -0,0 +1,38 @@
+import { Pipe, PipeTransform } from "@angular/core";
+import { select, Store } from "@ngrx/store";
+import { Observable } from "rxjs";
+import { switchMap } from "rxjs/operators";
+import { SAPI } from "src/atlasComponents/sapi/sapi.service";
+import { SapiSpaceModel } from "src/atlasComponents/sapi/type";
+import { atlasSelection } from "src/state";
+import { ParcellationSupportedInSpacePipe } from "./parcellationSupportedInSpace.pipe"
+
+@Pipe({
+  name: "spaceSupportedInCurrentParcellation",
+  /**
+   * the pipe is not exactly pure, since it makes http call
+   * but for the sake of angular change detection, this is suitable
+   * since the result should only change on input change
+   */
+  pure: true
+})
+
+export class SpaceSupportedInCurrentParcellationPipe implements PipeTransform{
+  private supportedPipe = new ParcellationSupportedInSpacePipe(this.sapi)
+  private selectedParcellation$ = this.store.pipe(
+    select(atlasSelection.selectors.selectedParcellation)
+  )
+  constructor(
+    private store: Store,
+    private sapi: SAPI
+  ){
+
+  }
+  public transform(space: SapiSpaceModel): Observable<boolean> {
+    return this.selectedParcellation$.pipe(
+      switchMap(parc => 
+        this.supportedPipe.transform(parc, space)
+      )
+    )
+  }
+}
diff --git a/src/atlasComponents/splashScreen/index.ts b/src/atlasComponents/splashScreen/index.ts
deleted file mode 100644
index 5927a7b64a7f19529a4998770dd5f27a3d3a2546..0000000000000000000000000000000000000000
--- a/src/atlasComponents/splashScreen/index.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-export { GetTemplateImageSrcPipe, SplashScreen } from "./splashScreen/splashScreen.component";
-export { SplashUiModule } from './module'
\ No newline at end of file
diff --git a/src/atlasComponents/splashScreen/module.ts b/src/atlasComponents/splashScreen/module.ts
deleted file mode 100644
index cafa61559313528ffe6a1e74c2b4a88c240f8329..0000000000000000000000000000000000000000
--- a/src/atlasComponents/splashScreen/module.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-import { CommonModule } from "@angular/common";
-import { NgModule } from "@angular/core";
-import { ComponentsModule } from "src/components";
-import { KgTosModule } from "src/ui/kgtos/module";
-import { AngularMaterialModule } from "src/sharedModules";
-import { UtilModule } from "src/util";
-import { GetTemplateImageSrcPipe, SplashScreen } from "./splashScreen/splashScreen.component";
-
-@NgModule({
-  imports: [
-    AngularMaterialModule,
-    CommonModule,
-    UtilModule,
-    KgTosModule,
-    ComponentsModule,
-  ],
-  declarations: [
-    SplashScreen,
-    GetTemplateImageSrcPipe,
-  ],
-  exports: [
-    SplashScreen,
-  ]
-})
-
-export class SplashUiModule{}
\ No newline at end of file
diff --git a/src/atlasComponents/splashScreen/splashScreen/splashScreen.component.ts b/src/atlasComponents/splashScreen/splashScreen/splashScreen.component.ts
deleted file mode 100644
index 3df8c71c099444e4be81158e3093c8c9e6d3dcde..0000000000000000000000000000000000000000
--- a/src/atlasComponents/splashScreen/splashScreen/splashScreen.component.ts
+++ /dev/null
@@ -1,75 +0,0 @@
-import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Pipe, PipeTransform, ViewChild } from "@angular/core";
-import { MatSnackBar } from "@angular/material/snack-bar";
-import { select, Store } from "@ngrx/store";
-import { Observable, Subject, Subscription } from "rxjs";
-import { filter } from 'rxjs/operators'
-import { viewerStateHelperStoreName, viewerStateSelectAtlas } from "src/services/state/viewerState.store.helper";
-import { PureContantService } from "src/util";
-import { CONST } from 'common/constants'
-
-@Component({
-  selector : 'ui-splashscreen',
-  templateUrl : './splashScreen.template.html',
-  styleUrls : [
-    `./splashScreen.style.css`,
-  ],
-  changeDetection: ChangeDetectionStrategy.OnPush,
-})
-
-export class SplashScreen {
-
-  public finishedLoading: boolean = false
-
-  public loadedAtlases$: Observable<any[]>
-
-  public filterNullFn(atlas: any){
-    return !!atlas
-  }
-
-  @ViewChild('parentContainer', {read: ElementRef})
-  public activatedTemplate$: Subject<any> = new Subject()
-
-  private subscriptions: Subscription[] = []
-
-  constructor(
-    private store: Store<any>,
-    private snack: MatSnackBar,
-    private pureConstantService: PureContantService,
-    private cdr: ChangeDetectorRef,
-  ) {
-    this.subscriptions.push(
-      this.pureConstantService.allFetchingReady$.subscribe(flag => {
-        this.finishedLoading = flag
-        this.cdr.markForCheck()
-      })
-    )
-
-    this.loadedAtlases$ = this.store.pipe(
-      select(state => state[viewerStateHelperStoreName]),
-      select(state => state.fetchedAtlases),
-      filter(v => !!v)
-    )
-  }
-
-  public selectAtlas(atlas: any){
-    if (!this.finishedLoading) {
-      this.snack.open(CONST.DATA_NOT_READY, null, {
-        duration: 3000
-      })
-      return
-    }
-    this.store.dispatch(
-      viewerStateSelectAtlas({ atlas })
-    )
-  }
-}
-
-@Pipe({
-  name: 'getTemplateImageSrcPipe',
-})
-
-export class GetTemplateImageSrcPipe implements PipeTransform {
-  public transform(name: string): string {
-    return `./res/image/${name.replace(/[|&;$%@()+,\s./]/g, '')}.png`
-  }
-}
diff --git a/src/atlasComponents/template/index.ts b/src/atlasComponents/template/index.ts
deleted file mode 100644
index 38cd6578cc518656abb1303d11557b86529cc996..0000000000000000000000000000000000000000
--- a/src/atlasComponents/template/index.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-export { GetTemplatePreviewUrlPipe } from "./getTemplatePreviewUrl.pipe";
-export { SiibraExplorerTemplateModule } from './module'
diff --git a/src/atlasComponents/template/module.ts b/src/atlasComponents/template/module.ts
deleted file mode 100644
index 9f1a49646b274dc7936b24800f99a4bf12e152c7..0000000000000000000000000000000000000000
--- a/src/atlasComponents/template/module.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import { NgModule } from "@angular/core";
-import { GetTemplatePreviewUrlPipe } from "./getTemplatePreviewUrl.pipe";
-import { TemplateIsDarkThemePipe } from "./templateIsDarkTheme.pipe";
-
-@NgModule({
-  imports: [],
-  declarations: [
-    GetTemplatePreviewUrlPipe,
-    TemplateIsDarkThemePipe,
-  ],
-  exports: [
-    GetTemplatePreviewUrlPipe,
-    TemplateIsDarkThemePipe,
-  ]
-})
-
-export class SiibraExplorerTemplateModule{}
\ No newline at end of file
diff --git a/src/atlasComponents/template/templateIsDarkTheme.pipe.ts b/src/atlasComponents/template/templateIsDarkTheme.pipe.ts
deleted file mode 100644
index 8454fd96e81e9815f8ab0bb627eb1e01d18b95b0..0000000000000000000000000000000000000000
--- a/src/atlasComponents/template/templateIsDarkTheme.pipe.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-import { OnDestroy, Pipe, PipeTransform } from "@angular/core";
-import { select, Store } from "@ngrx/store";
-import { Subscription } from "rxjs";
-import { viewerStateSelectedTemplateFullInfoSelector } from "src/services/state/viewerState/selectors";
-import { IHasId } from "src/util/interfaces";
-
-@Pipe({
-  name: 'templateIsDarkTheme',
-  pure: true,
-})
-
-export class TemplateIsDarkThemePipe implements OnDestroy, PipeTransform{
-
-  private templateFullInfo: any[] = []
-  constructor(store: Store<any>){
-    this.sub.push(
-      store.pipe(
-        select(viewerStateSelectedTemplateFullInfoSelector)
-      ).subscribe(val => this.templateFullInfo = val)
-    )
-  }
-
-  private sub: Subscription[] = []
-
-  ngOnDestroy(){
-    while(this.sub.length) this.sub.pop().unsubscribe()
-  }
-
-  public transform(template: IHasId): boolean{
-    const found = this.templateFullInfo.find(t => t['@id'] === template["@id"])
-    return found && found.darktheme
-  }
-}
\ No newline at end of file
diff --git a/src/atlasComponents/uiSelectors/atlasDropdown/atlasDropdown.component.spec.ts b/src/atlasComponents/uiSelectors/atlasDropdown/atlasDropdown.component.spec.ts
deleted file mode 100644
index 70b786d12ed055a08b57f5cf47f717bf6a266301..0000000000000000000000000000000000000000
--- a/src/atlasComponents/uiSelectors/atlasDropdown/atlasDropdown.component.spec.ts
+++ /dev/null
@@ -1 +0,0 @@
-// TODO
diff --git a/src/atlasComponents/uiSelectors/atlasDropdown/atlasDropdown.component.ts b/src/atlasComponents/uiSelectors/atlasDropdown/atlasDropdown.component.ts
deleted file mode 100644
index c44816155bcbb23701aa04e7a9de767b70dd056f..0000000000000000000000000000000000000000
--- a/src/atlasComponents/uiSelectors/atlasDropdown/atlasDropdown.component.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-import { Component } from "@angular/core";
-import { Store, select } from "@ngrx/store";
-import { Observable } from "rxjs";
-import { distinctUntilChanged } from "rxjs/operators";
-import { viewerStateHelperStoreName, viewerStateSelectAtlas } from "src/services/state/viewerState.store.helper";
-import { ARIA_LABELS } from 'common/constants'
-import { viewerStateGetSelectedAtlas } from "src/services/state/viewerState/selectors";
-
-@Component({
-  selector: 'atlas-dropdown-selector',
-  templateUrl: './atlasDropdown.template.html',
-  styleUrls: [
-    './atlasDropdown.style.css'
-  ]
-})
-
-export class AtlasDropdownSelector{
-
-  public fetchedAtlases$: Observable<any[]>
-  public selectedAtlas$: Observable<any>
-
-  public SELECT_ATLAS_ARIA_LABEL = ARIA_LABELS.SELECT_ATLAS
-
-  constructor(private store$: Store<any>){
-    this.fetchedAtlases$ = this.store$.pipe(
-      select(viewerStateHelperStoreName),
-      select('fetchedAtlases'),
-      distinctUntilChanged()
-    )
-    this.selectedAtlas$ = this.store$.pipe(
-      select(viewerStateGetSelectedAtlas)
-    )
-  }
-
-  handleChangeAtlas({ value }) {
-    this.store$.dispatch(
-      viewerStateSelectAtlas({
-        atlas: {
-          ['@id']: value
-        }
-      })
-    )
-  }
-}
-
diff --git a/src/atlasComponents/uiSelectors/atlasLayerSelector/atlasLayerSelector.component.spec.ts b/src/atlasComponents/uiSelectors/atlasLayerSelector/atlasLayerSelector.component.spec.ts
deleted file mode 100644
index 730f7b258819bf92a3a678976bead30a75817452..0000000000000000000000000000000000000000
--- a/src/atlasComponents/uiSelectors/atlasLayerSelector/atlasLayerSelector.component.spec.ts
+++ /dev/null
@@ -1,100 +0,0 @@
-import { CommonModule } from "@angular/common"
-import { ComponentFixture, TestBed } from "@angular/core/testing"
-import { NoopAnimationsModule } from "@angular/platform-browser/animations"
-import { MockStore, provideMockStore } from "@ngrx/store/testing"
-import { ComponentsModule } from "src/components"
-import { viewerStateSelectTemplateWithId, viewerStateToggleLayer } from "src/services/state/viewerState.store.helper"
-import { AngularMaterialModule } from "src/sharedModules"
-import { QuickTourModule } from "src/ui/quickTour"
-import { GetGroupedParcPipe } from "../pipes/getGroupedParc.pipe"
-import { GetIndividualParcPipe } from "../pipes/getIndividualParc.pipe"
-import { GetNonbaseParcPipe } from "../pipes/getNonBaseParc.pipe"
-import { AtlasLayerSelector } from "./atlasLayerSelector.component"
-
-describe("> atlasLayerSelector.component.ts", () => {
-  describe("> AtlasLayerSelector", () => {
-    let fixture: ComponentFixture<AtlasLayerSelector>
-    beforeEach(async () => {
-      await TestBed.configureTestingModule({
-        imports: [
-          CommonModule,
-          AngularMaterialModule,
-          NoopAnimationsModule,
-          QuickTourModule,
-          ComponentsModule,
-        ],
-        declarations: [
-          AtlasLayerSelector,
-          GetIndividualParcPipe,
-          GetNonbaseParcPipe,
-          GetGroupedParcPipe,
-        ],
-        providers: [
-          provideMockStore()
-        ]
-      }).compileComponents()
-    })
-
-    it("> can be init", () => {
-      fixture = TestBed.createComponent(AtlasLayerSelector)
-      expect(fixture).toBeTruthy()
-    })
-
-    describe("> methods", () => {
-      beforeEach(() => {
-        fixture = TestBed.createComponent(AtlasLayerSelector)
-      })
-      describe("> selectParcellationWithName", () => {
-        let tmplSupportParcPipeTransform: jasmine.Spy
-        let storeDispatchSpy: jasmine.Spy
-        const dummySelectedTmpl = {
-          "this": "obj"
-        }
-        const dummyParc = {
-          "foo": "bar",
-          "availableIn": [{
-            "baz": "yoo"
-          }]
-        }
-        beforeEach(() => {
-          tmplSupportParcPipeTransform = spyOn(fixture.componentInstance['currTmplSupportParcPipe'], 'transform')
-          const store = TestBed.inject(MockStore)
-          storeDispatchSpy = spyOn(store, 'dispatch')
-          fixture.componentInstance['selectedTemplate'] = dummySelectedTmpl
-        })
-        afterEach(() => {
-          if (tmplSupportParcPipeTransform) {
-            tmplSupportParcPipeTransform.calls.reset()
-          }
-        })
-        it("> calls pipe.transform", () => {
-          tmplSupportParcPipeTransform.and.returnValue(true)
-          fixture.componentInstance.selectParcellationWithName(dummyParc)
-          expect(tmplSupportParcPipeTransform).toHaveBeenCalledWith(dummySelectedTmpl, dummyParc)
-          expect(storeDispatchSpy).toHaveBeenCalledTimes(1)
-        })
-        
-        const tmplChgReqParam = [true, false]
-        
-        for (const tmplChgReq of tmplChgReqParam) {
-          describe(`> tmplChgReq: ${tmplChgReq}`, () => {
-            it("> expect the correct type of method to be called", () => {
-              tmplSupportParcPipeTransform.and.returnValue(!tmplChgReq)
-              fixture.componentInstance.selectParcellationWithName(dummyParc)
-              const allArgs = storeDispatchSpy.calls.allArgs()
-              expect(allArgs.length).toEqual(1)
-              expect(allArgs[0].length).toEqual(1)
-              const action = allArgs[0][0]
-
-              const expectedAction = tmplChgReq
-                ? viewerStateSelectTemplateWithId
-                : viewerStateToggleLayer
-
-              expect(action.type).toEqual(expectedAction.type)
-            })
-          })
-        }
-      })
-    })
-  })
-})
diff --git a/src/atlasComponents/uiSelectors/atlasLayerSelector/atlasLayerSelector.component.ts b/src/atlasComponents/uiSelectors/atlasLayerSelector/atlasLayerSelector.component.ts
deleted file mode 100644
index 9fbca8cce700ba9ef5bef01581a618e451308df3..0000000000000000000000000000000000000000
--- a/src/atlasComponents/uiSelectors/atlasLayerSelector/atlasLayerSelector.component.ts
+++ /dev/null
@@ -1,167 +0,0 @@
-import { Component, OnInit, ViewChildren, QueryList, HostBinding, ViewChild, ElementRef, OnDestroy } from "@angular/core";
-import { select, Store } from "@ngrx/store";
-import { distinctUntilChanged, map, withLatestFrom, shareReplay, mapTo } from "rxjs/operators";
-import { merge, Observable, Subject, Subscription } from "rxjs";
-import { viewerStateSelectTemplateWithId, viewerStateToggleLayer } from "src/services/state/viewerState.store.helper";
-import { MatMenuTrigger } from "@angular/material/menu";
-import { viewerStateGetSelectedAtlas, viewerStateAtlasLatestParcellationSelector, viewerStateSelectedTemplateFullInfoSelector, viewerStateSelectedTemplatePureSelector, viewerStateSelectedParcellationSelector } from "src/services/state/viewerState/selectors";
-import { ARIA_LABELS, CONST, QUICKTOUR_DESC } from 'common/constants'
-import { IQuickTourData } from "src/ui/quickTour/constrants";
-import { animate, state, style, transition, trigger } from "@angular/animations";
-import { IHasId, OVERWRITE_SHOW_DATASET_DIALOG_TOKEN } from "src/util/interfaces";
-import { CurrentTmplSupportsParcPipe } from "../pipes/currTmplSupportsParc.pipe";
-
-@Component({
-  selector: 'atlas-layer-selector',
-  templateUrl: './atlasLayerSelector.template.html',
-  styleUrls: ['./atlasLayerSelector.style.css'],
-  exportAs: 'atlasLayerSelector',
-  animations: [
-    trigger('toggleAtlasLayerSelector', [
-      state('false', style({
-        transform: 'scale(0)',
-        opacity: 0,
-        transformOrigin: '0% 100%'
-      })),
-      state('true', style({
-        transform: 'scale(1)',
-        opacity: 1,
-        transformOrigin: '0% 100%'
-      })),
-      transition('false => true', [
-        animate('200ms cubic-bezier(0.35, 0, 0.25, 1)')
-      ]),
-      transition('true => false', [
-        animate('200ms cubic-bezier(0.35, 0, 0.25, 1)')
-      ])
-    ])
-  ],
-  providers:[
-    {
-      provide: OVERWRITE_SHOW_DATASET_DIALOG_TOKEN,
-      useValue: null
-    }
-  ]
-})
-export class AtlasLayerSelector implements OnInit, OnDestroy {
-
-  public ARIA_LABELS = ARIA_LABELS
-  public CONST = CONST
-
-  @ViewChildren(MatMenuTrigger)
-  matMenuTriggers: QueryList<MatMenuTrigger>
-
-  @ViewChild('selectorPanelTmpl', { read: ElementRef })
-  selectorPanelTemplateRef: ElementRef
-
-  public selectedAtlas$: Observable<any> = this.store$.pipe(
-    select(viewerStateGetSelectedAtlas),
-    distinctUntilChanged(),
-    shareReplay(1)
-  )
-
-  public atlasLayersLatest$ = this.store$.pipe(
-    select(viewerStateAtlasLatestParcellationSelector),
-    shareReplay(1),
-  )
-
-  public availableTemplates$ = this.store$.pipe<any[]>(
-    select(viewerStateSelectedTemplateFullInfoSelector),
-  )
-
-  private selectedTemplate: any
-  public selectedTemplate$ = this.store$.pipe(
-    select(viewerStateSelectedTemplatePureSelector),
-    withLatestFrom(this.availableTemplates$),
-    map(([selectedTmpl, fullInfoTemplates]) => {
-      return fullInfoTemplates.find(t => t['@id'] === selectedTmpl['@id'])
-    })
-  )
-  private showOverlayIntent$ = new Subject()
-  public showLoadingOverlay$ = merge(
-    this.showOverlayIntent$.pipe(
-      mapTo(true)
-    ),
-    this.selectedTemplate$.pipe(
-      mapTo(false)
-    )
-  ).pipe(
-    distinctUntilChanged(),
-  )
-
-  public selectedParcellation$ = this.store$.pipe(
-    select(viewerStateSelectedParcellationSelector),
-  )
-
-  private subscriptions: Subscription[] = []
-
-  @HostBinding('attr.data-opened')
-  public selectorExpanded: boolean = false
-      
-  public quickTourData: IQuickTourData = {
-    order: 4,
-    description: QUICKTOUR_DESC.LAYER_SELECTOR,
-  }
-
-  constructor(private store$: Store<any>) {}
-
-  ngOnInit(): void {
-    this.subscriptions.push(
-      this.selectedTemplate$.subscribe(st => {
-        this.selectedTemplate = st
-      }),
-    )
-  }
-
-  ngOnDestroy() {
-    while(this.subscriptions.length) this.subscriptions.pop().unsubscribe()
-  }
-
-
-  toggleSelector() {
-    this.selectorExpanded = !this.selectorExpanded
-  }
-
-  selectTemplatewithId(templateId: string) {
-    this.showOverlayIntent$.next(true)
-    this.store$.dispatch(viewerStateSelectTemplateWithId({
-      payload: {
-        '@id': templateId
-      }
-    }))
-  }
-
-  private currTmplSupportParcPipe = new CurrentTmplSupportsParcPipe()
-
-  selectParcellationWithName(layer: any) {
-    const tmplChangeReq = !this.currTmplSupportParcPipe.transform(this.selectedTemplate, layer)
-    if (!tmplChangeReq) {
-      this.store$.dispatch(
-        viewerStateToggleLayer({ payload: layer })
-      )
-    } else {
-      this.showOverlayIntent$.next(true)
-      this.store$.dispatch(
-        viewerStateSelectTemplateWithId({
-          payload: layer.availableIn[0],
-          config: {
-            selectParcellation: layer
-          }
-        })
-      )
-    }
-  }
-
-  collapseExpandedGroup(){
-    this.matMenuTriggers.forEach(t => t.menuOpen && t.closeMenu())
-  }
-
-
-  trackbyAtId(t: IHasId){
-    return t['@id']
-  }
-
-  trackKeyVal(obj: {key: string, value: any}) {
-    return obj.key
-  }
-}
diff --git a/src/atlasComponents/uiSelectors/atlasLayerSelector/atlasLayerSelector.template.html b/src/atlasComponents/uiSelectors/atlasLayerSelector/atlasLayerSelector.template.html
deleted file mode 100644
index ed78029cd9c644c84e4c809e9498ff21a5eece81..0000000000000000000000000000000000000000
--- a/src/atlasComponents/uiSelectors/atlasLayerSelector/atlasLayerSelector.template.html
+++ /dev/null
@@ -1,171 +0,0 @@
-<!-- selector panel when expanded -->
-
-<mat-card class="selector-container m-2 position-absolute"
-    [ngClass]="{'pe-all': selectorExpanded}"
-    [@toggleAtlasLayerSelector]="selectorExpanded"
-    (@toggleAtlasLayerSelector.done)="atlasSelectorTour?.attachTo(selectorExpanded ? selectorPanelTemplateRef : null)"
-    #selectorPanelTmpl>
-    <mat-card-content>
-
-        <!-- templates -->
-        <mat-card-subtitle>
-            {{ CONST.ATLAS_SELECTOR_LABEL_SPACES }}
-        </mat-card-subtitle>
-
-        <!-- template grid and tiles -->
-        <mat-grid-list cols="3"
-            rowHeight="2:3"
-            gutterSize="16">
-
-            <mat-grid-tile *ngFor="let template of availableTemplates$ | async; trackBy: trackbyAtId"
-                [attr.aria-checked]="(selectedTemplate$ | async | getProperty : '@id')  === template['@id']">
-                
-                <div [hidden]
-                    iav-dataset-show-dataset-dialog
-                    [iav-dataset-show-dataset-dialog-name]="template.originDatainfos[0]?.name"
-                    [iav-dataset-show-dataset-dialog-description]="template.originDatainfos[0]?.description"
-                    [iav-dataset-show-dataset-dialog-urls]="template.originDatainfos[0]?.urls"
-                    #kgInfo="iavDatasetShowDatasetDialog">
-                </div>
-                <tile-cmp [tile-image-src]="template | getPreviewUrlPipe"
-                    class="cursor-pointer pe-all"
-                    tile-image-alt="Preview of this tile"
-                    [tile-text]="template.displayName || template.name"
-                    [tile-show-info]="template.originDatainfos?.length > 0"
-                    [tile-disabled]="!(selectedParcellation$ | async | currParcSupportsTmpl : template)"
-                    [tile-image-darktheme]="template.darktheme"
-                    [tile-selected]="(selectedTemplate$ | async | getProperty : '@id')  === template['@id']"
-                    (tile-on-click)="selectTemplatewithId(template['@id'])"
-                    (tile-on-info-click)="kgInfo && kgInfo.onClick()">
-                </tile-cmp>
-            </mat-grid-tile>
-        </mat-grid-list>
-
-        <mat-divider></mat-divider>
-
-        <!-- parcellations -->
-        <mat-card-subtitle class="mt-2">
-            {{ CONST.ATLAS_SELECTOR_LABEL_PARC_MAPS }}
-        </mat-card-subtitle>
-
-        <mat-grid-list cols="3"
-            rowHeight="2:3"
-            gutterSize="16">
-
-            <!-- non grouped layers -->
-            <mat-grid-tile *ngFor="let layer of (atlasLayersLatest$ | async | getNonbaseParc | getIndividualParc); trackBy: trackbyAtId"
-                [attr.aria-checked]="selectedParcellation$ | async | groupParcSelected : layer">
-
-                <div [hidden]
-                    iav-dataset-show-dataset-dialog
-                    [iav-dataset-show-dataset-dialog-name]="layer.originDatainfos[0]?.name"
-                    [iav-dataset-show-dataset-dialog-description]="layer.originDatainfos[0]?.description"
-                    [iav-dataset-show-dataset-dialog-urls]="layer.originDatainfos[0]?.urls"
-                    #kgInfo="iavDatasetShowDatasetDialog">
-                </div>
-                <tile-cmp [tile-image-src]="layer | getPreviewUrlPipe"
-                    class="cursor-pointer pe-all"
-                    tile-image-alt="Preview of this tile"
-                    [tile-text]="layer.displayName || layer.name"
-                    [tile-show-info]="layer.originDatainfos?.length > 0"
-                    [tile-disabled]="!(selectedTemplate$ | async | currentTemplateSupportsParcellation : layer)"
-                    [tile-image-darktheme]="layer.darktheme"
-
-                    [tile-selected]="selectedParcellation$ | async | groupParcSelected : layer"
-
-                    (tile-on-click)="selectParcellationWithName(layer)"
-                    (tile-on-info-click)="kgInfo && kgInfo.onClick()">
-                </tile-cmp>
-
-            </mat-grid-tile>
-
-            <!-- grouped layers -->
-            <mat-grid-tile *ngFor="let groupKeyVal of (atlasLayersLatest$ | async | getNonbaseParc | getGroupedParc | keyvalue); trackBy: trackKeyVal"
-                [attr.aria-checked]="false">
-
-                <!-- prevent click bubbling to document is necessary -->
-                <!-- or else, the outsideclick directive will fire immediately -->
-                <!-- resulting in immediate opening and closing of mat menu -->
-                <tile-cmp [tile-image-src]="groupKeyVal['value'][0] | getPreviewUrlPipe"
-                    class="cursor-pointer pe-all"
-                    tile-image-alt="Preview of this tile"
-                    [tile-text]="groupKeyVal['key']"
-                    [tile-show-info]="false"
-                    [tile-disabled]="!(selectedTemplate$ | async | currentTemplateSupportsParcellation : groupKeyVal['value'])"
-                    [tile-image-darktheme]="groupKeyVal['value'][0].darktheme"
-                    [tile-is-dir]="true"
-                    [matMenuTriggerFor]=layerGroupMenu
-                    [matMenuTriggerData]="{
-                        layerGroupItems: groupKeyVal['value']
-                    }"
-                    [tile-selected]="selectedParcellation$ | async | groupParcSelected : groupKeyVal['value']"
-                    iav-stop="click">
-                </tile-cmp>
-            </mat-grid-tile>
-        </mat-grid-list>
-    </mat-card-content>
-
-    <div [hidden]="!(showLoadingOverlay$ | async)"
-        class="loading-overlay">
-        <spinner-cmp class="spinner"></spinner-cmp>
-    </div>
-</mat-card>
-
-<!-- place holder when not expanded -->
-<div class="position-relative m-2 cursor-pointer scale-up-bl pe-all"
-    quick-tour
-    [quick-tour-description]="quickTourData.description"
-    [quick-tour-order]="quickTourData.order"
-    #atlasSelectorTour="quickTour">
-    <!-- TODO check when do we disable atlas selector -->
-    <button color="primary"
-        *ngIf="true"
-        matTooltip="Select layer"
-        mat-mini-fab
-        [attr.aria-label]="ARIA_LABELS.TOGGLE_ATLAS_LAYER_SELECTOR"
-        (click)="toggleSelector()">
-        <i class="fas fa-layer-group"></i>
-    </button>
-</div>
-
-<!-- mat menu for grouped layer -->
-<mat-menu
-    #layerGroupMenu="matMenu"
-    hasBackdrop="false">
-
-    <ng-template matMenuContent let-layerGroupItems="layerGroupItems">
-        <mat-grid-list cols="1"
-            rowHeight="6:7"
-            gutterSize="8"
-            iav-stop="click"
-            (iav-outsideClick)="collapseExpandedGroup()">
-            <mat-grid-tile *ngFor="let layer of layerGroupItems"
-                [attr.aria-checked]="selectedParcellation$ | async | groupParcSelected : layer">
-
-                <div [hidden]
-                    iav-dataset-show-dataset-dialog
-                    [iav-dataset-show-dataset-dialog-name]="layer.originDatainfos[0]?.name"
-                    [iav-dataset-show-dataset-dialog-description]="layer.originDatainfos[0]?.description"
-                    [iav-dataset-show-dataset-dialog-urls]="layer.originDatainfos[0]?.urls"
-                    #kgInfo="iavDatasetShowDatasetDialog">
-                </div>
-
-                <tile-cmp [tile-image-src]="layer | getPreviewUrlPipe"
-                    class="iv-custom-comp text m-3 cursor-pointer pe-all"
-                    tile-image-alt="Preview of this tile"
-                    [tile-text]="layer.displayName || layer.name"
-                    [tile-show-info]="layer.originDatainfos?.length > 0"
-                    [tile-disabled]="!(selectedTemplate$ | async | currentTemplateSupportsParcellation : layer)"
-                    [tile-image-darktheme]="layer.darktheme"
-
-                    [tile-selected]="selectedParcellation$ | async | groupParcSelected : layer"
-
-                    (tile-on-click)="selectParcellationWithName(layer)"
-                    (tile-on-info-click)="kgInfo && kgInfo.onClick()">
-                </tile-cmp>
-
-            </mat-grid-tile>
-        </mat-grid-list>
-
-    </ng-template>
-</mat-menu>
diff --git a/src/atlasComponents/uiSelectors/index.ts b/src/atlasComponents/uiSelectors/index.ts
deleted file mode 100644
index 28925e7dac73fcc4f6b8896a1026ec6c556a24e4..0000000000000000000000000000000000000000
--- a/src/atlasComponents/uiSelectors/index.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-export { AtlasCmpUiSelectorsModule } from "./module"
-export { AtlasDropdownSelector } from "./atlasDropdown/atlasDropdown.component"
-export { AtlasLayerSelector } from "./atlasLayerSelector/atlasLayerSelector.component"
\ No newline at end of file
diff --git a/src/atlasComponents/uiSelectors/module.ts b/src/atlasComponents/uiSelectors/module.ts
deleted file mode 100644
index b6994ea3003f3dbe9815b2986507cd7fdeea20a5..0000000000000000000000000000000000000000
--- a/src/atlasComponents/uiSelectors/module.ts
+++ /dev/null
@@ -1,50 +0,0 @@
-import { CommonModule } from "@angular/common";
-import { NgModule } from "@angular/core";
-import { AngularMaterialModule } from "src/sharedModules";
-import { UtilModule } from "src/util";
-import { AtlasDropdownSelector } from "./atlasDropdown/atlasDropdown.component";
-import { AtlasLayerSelector } from "./atlasLayerSelector/atlasLayerSelector.component";
-import {QuickTourModule} from "src/ui/quickTour/module";
-import { KgDatasetModule } from "../regionalFeatures/bsFeatures/kgDataset";
-import { AtlaslayerTooltipPipe } from "./pipes/atlasLayerTooltip.pipe";
-import { ComponentsModule } from "src/components";
-import { GetNonbaseParcPipe } from "./pipes/getNonBaseParc.pipe";
-import { GetIndividualParcPipe } from "./pipes/getIndividualParc.pipe";
-import { GetGroupedParcPipe } from "./pipes/getGroupedParc.pipe";
-import { CurrentTmplSupportsParcPipe } from "./pipes/currTmplSupportsParc.pipe";
-import { GroupParcSelectedPipe } from "./pipes/groupParcSelected.pipe";
-import { GetPreviewUrlPipe } from "./pipes/getPreviewUrl.pipe";
-import { CurrParcSupportsTmplPipe } from "./pipes/currParcSupportsTmpl.pipe";
-import { AtlasCmpParcellationModule } from "../parcellation";
-import { SiibraExplorerTemplateModule } from "../template";
-
-@NgModule({
-  imports: [
-    CommonModule,
-    AngularMaterialModule,
-    UtilModule,
-    QuickTourModule,
-    KgDatasetModule,
-    ComponentsModule,
-    AtlasCmpParcellationModule,
-    SiibraExplorerTemplateModule,
-  ],
-  declarations: [
-    AtlasDropdownSelector,
-    AtlasLayerSelector,
-    GetPreviewUrlPipe,
-    AtlaslayerTooltipPipe,
-    GetNonbaseParcPipe,
-    GetIndividualParcPipe,
-    GetGroupedParcPipe,
-    CurrentTmplSupportsParcPipe,
-    GroupParcSelectedPipe,
-    CurrParcSupportsTmplPipe,
-  ],
-  exports: [
-    AtlasDropdownSelector,
-    AtlasLayerSelector,
-  ]
-})
-
-export class AtlasCmpUiSelectorsModule{}
diff --git a/src/atlasComponents/uiSelectors/pipes/atlasLayerTooltip.pipe.ts b/src/atlasComponents/uiSelectors/pipes/atlasLayerTooltip.pipe.ts
deleted file mode 100644
index 285f134543afa5151de4627fe2f507515e5f81de..0000000000000000000000000000000000000000
--- a/src/atlasComponents/uiSelectors/pipes/atlasLayerTooltip.pipe.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import { Pipe, PipeTransform } from "@angular/core";
-
-@Pipe({
-  name: 'atlasLayerTooltip',
-  pure: true
-})
-
-export class AtlaslayerTooltipPipe implements PipeTransform{
-  public transform(layer: any){
-    return layer.name
-  }
-}
diff --git a/src/atlasComponents/uiSelectors/pipes/currParcSupportsTmpl.pipe.ts b/src/atlasComponents/uiSelectors/pipes/currParcSupportsTmpl.pipe.ts
deleted file mode 100644
index dcdf13d404ba747a5ce65f9f1bbe6fb08189b196..0000000000000000000000000000000000000000
--- a/src/atlasComponents/uiSelectors/pipes/currParcSupportsTmpl.pipe.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import { Pipe, PipeTransform } from "@angular/core";
-
-@Pipe({
-  name: 'currParcSupportsTmpl',
-  pure: true
-})
-
-export class CurrParcSupportsTmplPipe implements PipeTransform{
-  public transform(parc: any, tmpl: any){
-    /**
-     * TODO
-     * buggy. says julich brain v290 is not supported in fsaverage
-     * related to https://github.com/FZJ-INM1-BDA/siibra-python/issues/98 
-     */
-    const parcSupportTmpl = (p: any) => !!(tmpl.availableIn || []).find(tmplP => tmplP['@id'] === p && p['@id'])
-    return Array.isArray(parc)
-      ? parc.some(parcSupportTmpl)
-      : parcSupportTmpl(parc)
-  }
-}
diff --git a/src/atlasComponents/uiSelectors/pipes/currTmplSupportsParc.pipe.ts b/src/atlasComponents/uiSelectors/pipes/currTmplSupportsParc.pipe.ts
deleted file mode 100644
index 6d1ed06bd92acea21148e64bc6815c5d4345aa73..0000000000000000000000000000000000000000
--- a/src/atlasComponents/uiSelectors/pipes/currTmplSupportsParc.pipe.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { Pipe, PipeTransform } from "@angular/core";
-
-@Pipe({
-  name: 'currentTemplateSupportsParcellation',
-  pure: true
-})
-
-export class CurrentTmplSupportsParcPipe implements PipeTransform{
-  public transform(tmpl: any, parc: any): boolean {
-    const testParc = (p: any) => !!(p?.availableIn || []).find((availTmpl: any) => availTmpl['@id'] === tmpl['@id'])
-    return Array.isArray(parc)
-      ? parc.some(testParc)
-      : testParc(parc)
-  }
-}
diff --git a/src/atlasComponents/uiSelectors/pipes/getGroupedParc.pipe.ts b/src/atlasComponents/uiSelectors/pipes/getGroupedParc.pipe.ts
deleted file mode 100644
index cd98f91a417520f57af7f9ae9952ffd134716a8f..0000000000000000000000000000000000000000
--- a/src/atlasComponents/uiSelectors/pipes/getGroupedParc.pipe.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-import { Pipe, PipeTransform } from "@angular/core";
-
-type TReturn = {
-  [key: string]: any[]
-}
-
-@Pipe({
-  name: 'getGroupedParc',
-  pure: true
-})
-export class GetGroupedParcPipe implements PipeTransform{
-
-  public transform(arr: any[]):TReturn{
-    const returnObj: TReturn = {}
-    const filteredArr = arr.filter(p => p['groupName'])
-    for (const obj of filteredArr) {
-      const groupName: string = obj['groupName']
-      returnObj[groupName] = (returnObj[groupName] || []).concat(obj)
-    }
-    return returnObj
-  }
-}
diff --git a/src/atlasComponents/uiSelectors/pipes/getIndividualParc.pipe.ts b/src/atlasComponents/uiSelectors/pipes/getIndividualParc.pipe.ts
deleted file mode 100644
index 1e7b155b258819a4490ed41083aa0f55f6051ee4..0000000000000000000000000000000000000000
--- a/src/atlasComponents/uiSelectors/pipes/getIndividualParc.pipe.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import { Pipe, PipeTransform } from "@angular/core";
-
-@Pipe({
-  name: 'getIndividualParc',
-  pure: true
-})
-export class GetIndividualParcPipe implements PipeTransform{
-
-  public transform(arr: any[]){
-    return arr.filter(p => !p['groupName'])
-  }
-}
diff --git a/src/atlasComponents/uiSelectors/pipes/getNonBaseParc.pipe.ts b/src/atlasComponents/uiSelectors/pipes/getNonBaseParc.pipe.ts
deleted file mode 100644
index e0b7997bfa191ee6e790fc935a541e7c8d9a5bb1..0000000000000000000000000000000000000000
--- a/src/atlasComponents/uiSelectors/pipes/getNonBaseParc.pipe.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import { Pipe, PipeTransform } from "@angular/core";
-
-@Pipe({
-  name: 'getNonbaseParc',
-  pure: true
-})
-export class GetNonbaseParcPipe implements PipeTransform{
-
-  public transform(arr: any[]){
-    return arr.filter(p => !p['baseLayer'])
-  }
-}
diff --git a/src/atlasComponents/uiSelectors/pipes/getPreviewUrl.pipe.ts b/src/atlasComponents/uiSelectors/pipes/getPreviewUrl.pipe.ts
deleted file mode 100644
index 7826f44e267119a0cab9c848f0dd368280629b95..0000000000000000000000000000000000000000
--- a/src/atlasComponents/uiSelectors/pipes/getPreviewUrl.pipe.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import { Pipe, PipeTransform } from "@angular/core";
-import { GetParcPreviewUrlPipe } from "src/atlasComponents/parcellation";
-import { GetTemplatePreviewUrlPipe } from "src/atlasComponents/template";
-
-const templateUrlPipe = new GetTemplatePreviewUrlPipe()
-const parcUrlPipe = new GetParcPreviewUrlPipe()
-
-@Pipe({
-  name: 'getPreviewUrlPipe',
-  pure: true
-})
-
-export class GetPreviewUrlPipe implements PipeTransform{
-  public transform(tile: any){
-    const filename = templateUrlPipe.transform(tile) || parcUrlPipe.transform(tile)
-    return filename
-  }
-}
diff --git a/src/atlasComponents/uiSelectors/pipes/groupParcSelected.pipe.ts b/src/atlasComponents/uiSelectors/pipes/groupParcSelected.pipe.ts
deleted file mode 100644
index 6beb302be626272f72e62f23b722ce1ce7411015..0000000000000000000000000000000000000000
--- a/src/atlasComponents/uiSelectors/pipes/groupParcSelected.pipe.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { Pipe, PipeTransform } from "@angular/core";
-
-@Pipe({
-  name: 'groupParcSelected',
-  pure: true
-})
-
-export class GroupParcSelectedPipe implements PipeTransform{
-  public transform(selectedParc: any, parc: any){
-    const isSelected = (p: any) => p['@id'] === selectedParc['@id']
-    return Array.isArray(parc)
-      ? parc.some(isSelected)
-      : isSelected(parc)
-  }
-}
diff --git a/src/atlasComponents/userAnnotations/annotationList/annotationList.template.html b/src/atlasComponents/userAnnotations/annotationList/annotationList.template.html
index f0832efe03d55a7295ad5699d058d35d029304de..d90110fa3f66f2a6483787462e5432bc4704986f 100644
--- a/src/atlasComponents/userAnnotations/annotationList/annotationList.template.html
+++ b/src/atlasComponents/userAnnotations/annotationList/annotationList.template.html
@@ -55,7 +55,7 @@
     </div>
 
     <!-- content -->
-    <mat-card-content class="mt-4 ml-15px-n mr-15px-n pb-4">
+    <mat-card-content class="mt-4 ml-15px-n mr-15px-n sxplr-pb-4">
         <!-- list of annotations -->
         <ng-template [ngIf]="managedAnnotations$ | async" [ngIfElse]="placeholderTmpl" let-managedAnnotations>
 
@@ -98,14 +98,14 @@
         </ng-template>
 
         <ng-template [ngIf]="annotationInOtherSpaces$ | async" let-annsInOtherSpace>
-            <div *ngIf="annsInOtherSpace.length > 0" class="p-4 text-muted">
+            <div *ngIf="annsInOtherSpace.length > 0" class="sxplr-p-4 text-muted">
                 {{ annsInOtherSpace.length }} annotations found in other reference spaces, and not shown here.
             </div>
         </ng-template>
 
         <!-- place holder when no annotations exist -->
         <ng-template #placeholderTmpl>
-            <div class="p-4 text-muted">
+            <div class="sxplr-p-4 text-muted">
                 <p>Start by adding an annotation, or import existing annotations.</p>
             </div>
         </ng-template>
diff --git a/src/atlasComponents/userAnnotations/singleAnnotationUnit/singleAnnotationUnit.component.ts b/src/atlasComponents/userAnnotations/singleAnnotationUnit/singleAnnotationUnit.component.ts
index 8c9a11566b9f7f27350a684cc9ffc3976525c32e..327439092987a0dae77a60d0d409f5200f6b60c6 100644
--- a/src/atlasComponents/userAnnotations/singleAnnotationUnit/singleAnnotationUnit.component.ts
+++ b/src/atlasComponents/userAnnotations/singleAnnotationUnit/singleAnnotationUnit.component.ts
@@ -5,10 +5,11 @@ import { Polygon } from '../tools/poly'
 import { FormControl, FormGroup } from "@angular/forms";
 import { Subscription } from "rxjs";
 import { select, Store } from "@ngrx/store";
-import { viewerStateFetchedAtlasesSelector } from "src/services/state/viewerState/selectors";
 import { ModularUserAnnotationToolService } from "../tools/service";
 import { MatSnackBar } from "@angular/material/snack-bar";
 import { Line } from "../tools/line";
+import { atlasSelection } from "src/state";
+import { map } from "rxjs/operators";
 
 @Component({
   selector: 'single-annotation-unit',
@@ -31,7 +32,6 @@ export class SingleAnnotationUnit implements OnDestroy, AfterViewInit{
   private subs: Subscription[] = []
   public templateSpaces: {
     ['@id']: string
-    name: string
   }[] = []
   ngOnChanges(){
     while(this.chSubs.length > 0) this.chSubs.pop().unsubscribe()
@@ -52,30 +52,22 @@ export class SingleAnnotationUnit implements OnDestroy, AfterViewInit{
         this.managedAnnotation.desc = desc
       })
     )
-
   }
 
+  public tmpls$ = this.store.pipe(
+    select(atlasSelection.selectors.selectedTemplate),
+    map(val => {
+      return [val]
+    })
+  )
+
   constructor(
-    store: Store<any>,
+    private store: Store<any>,
     private snackbar: MatSnackBar,
     private svc: ModularUserAnnotationToolService,
     private cfr: ComponentFactoryResolver,
     private injector: Injector,
   ){
-    this.subs.push(
-      store.pipe(
-        select(viewerStateFetchedAtlasesSelector),
-      ).subscribe(atlases => {
-        for (const atlas of atlases) {
-          for (const tmpl of atlas.templateSpaces) {
-            this.templateSpaces.push({
-              '@id': tmpl['@id'],
-              name: tmpl.name
-            })
-          }
-        }
-      })
-    )
   }
 
   ngAfterViewInit(){
diff --git a/src/atlasComponents/userAnnotations/singleAnnotationUnit/singleAnnotationUnit.template.html b/src/atlasComponents/userAnnotations/singleAnnotationUnit/singleAnnotationUnit.template.html
index e03b3068ebc2360cc9e6a08c537c73c750c4ece9..51f1c004700f834b92c3afd81d6cd93ee3d01026 100644
--- a/src/atlasComponents/userAnnotations/singleAnnotationUnit/singleAnnotationUnit.template.html
+++ b/src/atlasComponents/userAnnotations/singleAnnotationUnit/singleAnnotationUnit.template.html
@@ -4,8 +4,8 @@
       Space
     </mat-label>
     <mat-select formControlName="spaceId">
-      <mat-option *ngFor="let tmpl of templateSpaces" [value]="tmpl['@id']">
-        {{ tmpl.name }}
+      <mat-option *ngFor="let tmpl of tmpls$ | async" [value]="tmpl['@id']">
+        {{ tmpl.fullName }}
       </mat-option>
     </mat-select>
   </mat-form-field>
diff --git a/src/atlasComponents/userAnnotations/tools/line/line.component.ts b/src/atlasComponents/userAnnotations/tools/line/line.component.ts
index bf3d32401fc9cfc2378b2a4d17000e53cef2390d..a98e126c64223483c2bbaf9131f9db091d3cb2f2 100644
--- a/src/atlasComponents/userAnnotations/tools/line/line.component.ts
+++ b/src/atlasComponents/userAnnotations/tools/line/line.component.ts
@@ -4,10 +4,10 @@ import { Store } from "@ngrx/store";
 import { Line, LINE_ICON_CLASS } from "../line";
 import { ToolCmpBase } from "../toolCmp.base";
 import { IAnnotationGeometry, TExportFormats, UDPATE_ANNOTATION_TOKEN } from "../type";
-import { viewerStateChangeNavigation } from "src/services/state/viewerState/actions";
 import { Point } from "../point";
 import { ARIA_LABELS } from 'common/constants'
 import { ComponentStore } from "src/viewerModule/componentStore";
+import { actions } from "src/state/atlasSelection";
 
 @Component({
   selector: 'line-update-cmp',
@@ -59,12 +59,12 @@ export class LineUpdateCmp extends ToolCmpBase implements OnDestroy{
       const { x, y, z } = roi
 
       this.store.dispatch(
-        viewerStateChangeNavigation({
+        actions.navigateTo({
           navigation: {
-            position: [x, y, z],
-            positionReal: true,
-            animation: {}
-          }
+            position: [x, y, z]
+          },
+          physical: true,
+          animation: true
         })
       )
       return
@@ -79,12 +79,12 @@ export class LineUpdateCmp extends ToolCmpBase implements OnDestroy{
     const { x, y, z } = this.updateAnnotation.points[0]
     
     this.store.dispatch(
-      viewerStateChangeNavigation({
+      actions.navigateTo({
         navigation: {
-          position: [x, y, z],
-          positionReal: true,
-          animation: {}
-        }
+          position: [x, y, z]
+        },
+        physical: true,
+        animation: true
       })
     )
   }
diff --git a/src/atlasComponents/userAnnotations/tools/line/line.template.html b/src/atlasComponents/userAnnotations/tools/line/line.template.html
index e0b3b06a78c4ebb58e26e4d9254f6c59e74a2ca3..3f72bd1d7a590f34b3f20636a44a7ff46f9ce2e2 100644
--- a/src/atlasComponents/userAnnotations/tools/line/line.template.html
+++ b/src/atlasComponents/userAnnotations/tools/line/line.template.html
@@ -36,10 +36,10 @@
 </div>
 
 <mat-menu #exportMenu="matMenu" xPosition="before">
-  <div class="iv-custom-comp card text"
+  <div class="sxplr-custom-cmp card text"
     iav-stop="click">
 
-    <div class="iv-custom-comp text">
+    <div class="sxplr-custom-cmp text">
       <textarea-copy-export
         [textarea-copy-export-label]="useFormat"
         [textarea-copy-export-text]="updateAnnotation.updateSignal$ | async | toFormattedStringPipe : updateAnnotation : useFormat"
diff --git a/src/atlasComponents/userAnnotations/tools/point/point.component.ts b/src/atlasComponents/userAnnotations/tools/point/point.component.ts
index c28152125a1a006ac1f69d8543792364dcb8b4ab..b4ef51006975e34cdcc753ad85cf1dc10056a9dd 100644
--- a/src/atlasComponents/userAnnotations/tools/point/point.component.ts
+++ b/src/atlasComponents/userAnnotations/tools/point/point.component.ts
@@ -3,9 +3,9 @@ import { Point, POINT_ICON_CLASS } from "../point";
 import { IAnnotationGeometry, TExportFormats, UDPATE_ANNOTATION_TOKEN } from "../type";
 import { ToolCmpBase } from "../toolCmp.base";
 import { Store } from "@ngrx/store";
-import { viewerStateChangeNavigation } from "src/services/state/viewerState/actions";
 import { ComponentStore } from "src/viewerModule/componentStore";
 import { ARIA_LABELS } from 'common/constants'
+import { actions } from "src/state/atlasSelection";
 
 @Component({
   selector: 'point-update-cmp',
@@ -55,12 +55,12 @@ export class PointUpdateCmp extends ToolCmpBase implements OnDestroy{
     }
     const { x, y, z } = this.updateAnnotation
     this.store.dispatch(
-      viewerStateChangeNavigation({
+      actions.navigateTo({
         navigation: {
-          position: [x, y, z],
-          positionReal: true,
-          animation: {}
-        }
+          position: [x, y, z]
+        },
+        physical: true,
+        animation: true
       })
     )
   }
diff --git a/src/atlasComponents/userAnnotations/tools/point/point.template.html b/src/atlasComponents/userAnnotations/tools/point/point.template.html
index b1b5ab285e11bc2926f3a00d4b8b0b39b4ab0579..805ab08767a59c9f202ae053b814150678bef881 100644
--- a/src/atlasComponents/userAnnotations/tools/point/point.template.html
+++ b/src/atlasComponents/userAnnotations/tools/point/point.template.html
@@ -35,10 +35,10 @@
 </div>
 
 <mat-menu #exportMenu="matMenu" xPosition="before">
-  <div class="iv-custom-comp card text"
+  <div class="sxplr-custom-cmp card text"
     iav-stop="click">
 
-    <div class="iv-custom-comp text">
+    <div class="sxplr-custom-cmp text">
 
       <textarea-copy-export
         [textarea-copy-export-label]="useFormat"
diff --git a/src/atlasComponents/userAnnotations/tools/poly/poly.component.ts b/src/atlasComponents/userAnnotations/tools/poly/poly.component.ts
index 9d8371b404a5eec8d4bc4ca60bbcf07cdfac2c5e..6e8852d88b665d2b988ed59f1340c311d0086515 100644
--- a/src/atlasComponents/userAnnotations/tools/poly/poly.component.ts
+++ b/src/atlasComponents/userAnnotations/tools/poly/poly.component.ts
@@ -3,11 +3,11 @@ import { MatSnackBar } from "@angular/material/snack-bar";
 import { Polygon, POLY_ICON_CLASS } from "../poly";
 import { ToolCmpBase } from "../toolCmp.base";
 import { IAnnotationGeometry, TExportFormats, UDPATE_ANNOTATION_TOKEN } from "../type";
-import { viewerStateChangeNavigation } from "src/services/state/viewerState/actions";
 import { Store } from "@ngrx/store";
 import { Point } from "../point";
 import { ARIA_LABELS } from 'common/constants'
 import { ComponentStore } from "src/viewerModule/componentStore";
+import { actions } from "src/state/atlasSelection";
 
 @Component({
   selector: 'poly-update-cmp',
@@ -68,12 +68,12 @@ export class PolyUpdateCmp extends ToolCmpBase implements OnDestroy{
       const { x, y, z } = roi
 
       this.store.dispatch(
-        viewerStateChangeNavigation({
+        actions.navigateTo({
           navigation: {
-            position: [x, y, z],
-            positionReal: true,
-            animation: {}
-          }
+            position: [x, y, z]
+          },
+          physical: true,
+          animation: true
         })
       )
       return
@@ -88,12 +88,12 @@ export class PolyUpdateCmp extends ToolCmpBase implements OnDestroy{
     const { x, y, z } = this.updateAnnotation.points[0]
     
     this.store.dispatch(
-      viewerStateChangeNavigation({
+      actions.navigateTo({
         navigation: {
-          position: [x, y, z],
-          positionReal: true,
-          animation: {}
-        }
+          position: [x, y, z]
+        },
+        physical: true,
+        animation: true
       })
     )
   }
diff --git a/src/atlasComponents/userAnnotations/tools/poly/poly.template.html b/src/atlasComponents/userAnnotations/tools/poly/poly.template.html
index ea8665437974b0c1d29b31de294b72ed426f7095..6827915b80ecfb04504184c6519b9670cfa62cf0 100644
--- a/src/atlasComponents/userAnnotations/tools/poly/poly.template.html
+++ b/src/atlasComponents/userAnnotations/tools/poly/poly.template.html
@@ -36,10 +36,10 @@
 </div>
 
 <mat-menu #exportMenu="matMenu" xPosition="before">
-  <div class="iv-custom-comp card text"
+  <div class="sxplr-custom-cmp card text"
     iav-stop="click">
 
-    <div class="iv-custom-comp text">
+    <div class="sxplr-custom-cmp text">
       <textarea-copy-export
         [textarea-copy-export-label]="useFormat"
         [textarea-copy-export-text]="updateAnnotation.updateSignal$ | async | toFormattedStringPipe : updateAnnotation : useFormat"
diff --git a/src/atlasComponents/userAnnotations/tools/service.ts b/src/atlasComponents/userAnnotations/tools/service.ts
index 26fc809549b7f7fd59740539aaaec0e09ae66a4a..e998c9c53a65be4965b8cad62e84d1b45df3c3f3 100644
--- a/src/atlasComponents/userAnnotations/tools/service.ts
+++ b/src/atlasComponents/userAnnotations/tools/service.ts
@@ -4,18 +4,20 @@ import { Inject, Optional } from "@angular/core";
 import { select, Store } from "@ngrx/store";
 import { BehaviorSubject, combineLatest, fromEvent, merge, Observable, of, Subject, Subscription } from "rxjs";
 import {map, switchMap, filter, shareReplay, pairwise } from "rxjs/operators";
-import { viewerStateSelectedTemplatePureSelector, viewerStateViewerModeSelector } from "src/services/state/viewerState/selectors";
 import { NehubaViewerUnit } from "src/viewerModule/nehuba";
 import { NEHUBA_INSTANCE_INJTKN } from "src/viewerModule/nehuba/util";
 import { AbsToolClass, ANNOTATION_EVENT_INJ_TOKEN, IAnnotationEvents, IAnnotationGeometry, INgAnnotationTypes, INJ_ANNOT_TARGET, TAnnotationEvent, ClassInterface, TCallbackFunction, TSands, TGeometryJson, TNgAnnotationLine, TCallback } from "./type";
-import { switchMapWaitFor } from "src/util/fn";
+import { getExportNehuba, switchMapWaitFor } from "src/util/fn";
 import { Polygon } from "./poly";
 import { Line } from "./line";
 import { Point } from "./point";
 import { FilterAnnotationsBySpace } from "../filterAnnotationBySpace.pipe";
 import { retry } from 'common/util'
 import { MatSnackBar } from "@angular/material/snack-bar";
-import { viewerStateSetViewerMode } from "src/services/state/viewerState.store.helper";
+import { actions } from "src/state/atlasSelection";
+import { atlasSelection } from "src/state";
+import { SapiSpaceModel } from "src/atlasComponents/sapi";
+import { AnnotationLayer } from "src/atlasComponents/annotations";
 
 const LOCAL_STORAGE_KEY = 'userAnnotationKey'
 
@@ -85,13 +87,13 @@ export class ModularUserAnnotationToolService implements OnDestroy{
 
   private previewNgAnnIds: string[] = []
 
-  private ngAnnotationLayer: any
+  private annotationLayer: AnnotationLayer
   private activeToolName: string
   private forcedAnnotationRefresh$ = new BehaviorSubject(null)
 
-  private selectedTmpl: any
+  private selectedTmpl: SapiSpaceModel
   private selectedTmpl$ = this.store.pipe(
-    select(viewerStateSelectedTemplatePureSelector),
+    select(atlasSelection.selectors.selectedTemplate),
   )
   public moduleAnnotationTypes: {instance: {name: string, iconClass: string, toolSelected$: Observable<boolean>}, onClick: () => void}[] = []
   private managedAnnotationsStream$ = new Subject<{
@@ -321,36 +323,7 @@ export class ModularUserAnnotationToolService implements OnDestroy{
      */
     this.subscription.push(
       nehubaViewer$.subscribe(() => {
-        this.ngAnnotationLayer = null
-      })
-    )
-
-    /**
-     * on new nehubaViewer, listen to mouseState
-     */
-    let cb: () => void
-    this.subscription.push(
-      nehubaViewer$.pipe(
-        switchMap(switchMapWaitFor({
-          condition: nv => !!(nv?.nehubaViewer),
-        }))
-      ).subscribe(nehubaViewer => {
-        if (cb) cb()
-        if (nehubaViewer) {
-          const mouseState = nehubaViewer.nehubaViewer.ngviewer.mouseState
-          cb = mouseState.changed.add(() => {
-            const payload: IAnnotationEvents['hoverAnnotation'] = mouseState.active && !!mouseState.pickedAnnotationId
-              ? {
-                pickedAnnotationId: mouseState.pickedAnnotationId,
-                pickedOffset: mouseState.pickedOffset
-              }
-              : null
-            this.annotnEvSubj.next({
-              type: 'hoverAnnotation',
-              detail: payload
-            })
-          })
-        }
+        this.annotationLayer = null
       })
     )
 
@@ -411,23 +384,12 @@ export class ModularUserAnnotationToolService implements OnDestroy{
           this.clearAllPreviewAnnotations()
         }
         for (let idx = 0; idx < previewNgAnnotation.length; idx ++) {
-          const localAnnotations = this.ngAnnotationLayer.layer.localAnnotations
-          const annSpec = {
-            ...parseNgAnnotation(previewNgAnnotation[idx]),
+          const spec = {
+            ...previewNgAnnotation[idx],
             id: `${ModularUserAnnotationToolService.TMP_PREVIEW_ANN_ID}_${idx}`
           }
-          const annRef = localAnnotations.references.get(annSpec.id)
-          if (annRef) {
-            localAnnotations.update(
-              annRef,
-              annSpec
-            )
-          } else {
-            localAnnotations.add(
-              annSpec
-            )
-          }
-          this.previewNgAnnIds[idx] = annSpec.id
+          this.annotationLayer.updateAnnotation(spec)
+          this.previewNgAnnIds[idx] = spec.id
         }
       })
     )
@@ -439,7 +401,7 @@ export class ModularUserAnnotationToolService implements OnDestroy{
       this.forcedAnnotationRefresh$,
       this.spaceFilteredManagedAnnotations$.pipe(
         switchMap(switchMapWaitFor({
-          condition: () => !!this.ngAnnotationLayer,
+          condition: () => !!this.annotationLayer,
           leading: true
         })),
       )
@@ -479,20 +441,8 @@ export class ModularUserAnnotationToolService implements OnDestroy{
             this.deleteNgAnnotationById(annotation.id)
             continue
           }
-          if (!this.ngAnnotationLayer) continue
-          const localAnnotations = this.ngAnnotationLayer.layer.localAnnotations
-          const annRef = localAnnotations.references.get(annotation.id)
-          const annSpec = parseNgAnnotation(annotation)
-          if (annRef) {
-            localAnnotations.update(
-              annRef,
-              annSpec
-            )
-          } else {
-            localAnnotations.add(
-              annSpec
-            )
-          }
+          if (!this.annotationLayer) continue
+          this.annotationLayer.updateAnnotation(annotation)
         }
       })
     )
@@ -502,30 +452,33 @@ export class ModularUserAnnotationToolService implements OnDestroy{
      */
     this.subscription.push(
       store.pipe(
-        select(viewerStateViewerModeSelector)
+        select(atlasSelection.selectors.viewerMode)
       ).subscribe(viewerMode => {
         this.currMode = viewerMode
         if (viewerMode === ModularUserAnnotationToolService.VIEWER_MODE) {
-          if (this.ngAnnotationLayer) this.ngAnnotationLayer.setVisible(true)
+          if (this.annotationLayer) this.annotationLayer.setVisible(true)
           else {
             const viewer = (window as any).viewer
-            const voxelSize = IAV_VOXEL_SIZES_NM[this.selectedTmpl.fullId]
-            if (!voxelSize) throw new Error(`voxelSize of ${this.selectedTmpl.fullId} cannot be found!`)
-            const layer = viewer.layerSpecification.getLayer(
+            const voxelSize = IAV_VOXEL_SIZES_NM[this.selectedTmpl["@id"]]
+            if (!voxelSize) throw new Error(`voxelSize of ${this.selectedTmpl["@id"]} cannot be found!`)
+            if (this.annotationLayer) {
+              this.annotationLayer.dispose()
+            }
+            this.annotationLayer = new AnnotationLayer(
               ModularUserAnnotationToolService.ANNOTATION_LAYER_NAME,
-              {
-                ...ModularUserAnnotationToolService.USER_ANNOTATION_LAYER_SPEC,
-                // since voxel coordinates are no longer defined, so voxel size will always be 1/1/1
-                transform: [
-                  [1, 0, 0, 0],
-                  [0, 1, 0, 0],
-                  [0, 0, 1, 0],
-                  [0, 0, 0, 1],
-                ]
-              }
+              ModularUserAnnotationToolService.USER_ANNOTATION_LAYER_SPEC.annotationColor
             )
-            this.ngAnnotationLayer = viewer.layerManager.addManagedLayer(layer)
-
+            this.annotationLayer.onHover.subscribe(val => {
+              this.annotnEvSubj.next({
+                type: 'hoverAnnotation',
+                detail: val
+                  ? {
+                    pickedAnnotationId: val.id,
+                    pickedOffset: val.offset
+                  }
+                  : null
+              })
+            })
             /**
              * on template changes, the layer gets lost
              * force redraw annotations if layer needs to be recreated
@@ -533,7 +486,7 @@ export class ModularUserAnnotationToolService implements OnDestroy{
             this.forcedAnnotationRefresh$.next(null)
           }
         } else {
-          if (this.ngAnnotationLayer) this.ngAnnotationLayer.setVisible(false)
+          if (this.annotationLayer) this.annotationLayer.setVisible(false)
         }
       })
     )
@@ -583,14 +536,14 @@ export class ModularUserAnnotationToolService implements OnDestroy{
     const bin = atob(encoded)
     
     await retry(() => {
-      if (!!(window as any).export_nehuba) return true
+      if (!!getExportNehuba()) return true
       else throw new Error(`export nehuba not yet ready`)
     }, {
       timeout: 1000,
       retries: 10
     })
     
-    const { pako } = (window as any).export_nehuba
+    const { pako } = getExportNehuba()
     const decoded = pako.inflate(bin, { to: 'string' })
     const arr = JSON.parse(decoded)
     const anns: IAnnotationGeometry[] = []
@@ -626,8 +579,9 @@ export class ModularUserAnnotationToolService implements OnDestroy{
       arr.push(json)
     }
     const stringifiedJSON = JSON.stringify(arr)
-    if (!(window as any).export_nehuba) return
-    const { pako } = (window as any).export_nehuba
+    const exportNehuba = getExportNehuba()
+    if (!exportNehuba) return
+    const { pako } = exportNehuba
     const compressed = pako.deflate(stringifiedJSON)
     let out = ''
     for (const num of compressed) {
@@ -666,12 +620,7 @@ export class ModularUserAnnotationToolService implements OnDestroy{
   }
 
   private deleteNgAnnotationById(annId: string) {
-    const localAnnotations = this.ngAnnotationLayer.layer.localAnnotations
-    const annRef = localAnnotations.references.get(annId)
-    if (annRef) {
-      localAnnotations.delete(annRef)
-      localAnnotations.references.delete(annId)
-    }
+    this.annotationLayer.removeAnnotation({ id: annId })
   }
 
   public defaultTool: AbsToolClass<any>
@@ -749,7 +698,7 @@ export class ModularUserAnnotationToolService implements OnDestroy{
   private currMode: string
   switchAnnotationMode(mode: 'on' | 'off' | 'toggle' = 'toggle') {
 
-    let payload = null
+    let payload: 'annotating' = null
     if (mode === 'on') payload = ARIA_LABELS.VIEWER_MODE_ANNOTATING
     if (mode === 'off') {
       if (this.currMode === ARIA_LABELS.VIEWER_MODE_ANNOTATING) payload = null
@@ -761,7 +710,9 @@ export class ModularUserAnnotationToolService implements OnDestroy{
         : ARIA_LABELS.VIEWER_MODE_ANNOTATING
     }
     this.store.dispatch(
-      viewerStateSetViewerMode({ payload })
+      actions.setViewerMode({
+        viewerMode: payload
+      })
     )
   }
 }
diff --git a/src/atlasViewer/atlasViewer.apiService.service.spec.ts b/src/atlasViewer/atlasViewer.apiService.service.spec.ts
deleted file mode 100644
index 2f510f5d98763e61d25f46a9ac157f9d19308cea..0000000000000000000000000000000000000000
--- a/src/atlasViewer/atlasViewer.apiService.service.spec.ts
+++ /dev/null
@@ -1,279 +0,0 @@
-import { AtlasViewerAPIServices } from "src/atlasViewer/atlasViewer.apiService.service";
-import { async, TestBed, fakeAsync, tick } from "@angular/core/testing";
-import { provideMockStore } from "@ngrx/store/testing";
-import { defaultRootState } from "src/services/stateStore.service";
-import { AngularMaterialModule } from "src/sharedModules";
-import { WidgetModule } from 'src/widget';
-import { HttpClientTestingModule, HttpTestingController } from "@angular/common/http/testing";
-import { PluginServices } from "src/plugin";
-import { CANCELLABLE_DIALOG } from "src/util/interfaces";
-
-describe('atlasViewer.apiService.service.ts', () => {
-
-  describe('AtlasViewerAPIServices', () => {
-
-    const cancelTokenSpy = jasmine.createSpy('cancelToken')
-    const cancellableDialogSpy = jasmine.createSpy('openCallableDialog').and.returnValue(cancelTokenSpy)
-
-    afterEach(() => {
-      cancelTokenSpy.calls.reset()
-      cancellableDialogSpy.calls.reset()
-
-      const ctrl = TestBed.inject(HttpTestingController)
-      ctrl.verify()
-    })
-
-    beforeEach(async(() => {
-      TestBed.configureTestingModule({
-        imports: [
-          AngularMaterialModule,
-          HttpClientTestingModule,
-          WidgetModule,
-        ],
-        providers: [
-          AtlasViewerAPIServices,
-          provideMockStore({ initialState: defaultRootState }),
-          {
-            provide: CANCELLABLE_DIALOG,
-            useValue: cancellableDialogSpy
-          },
-          {
-            provide: PluginServices,
-            useValue: {}
-          }
-        ]
-      }).compileComponents()
-    }))  
-
-    it('service exists', () => {
-      const service = TestBed.inject(AtlasViewerAPIServices)
-      expect(service).not.toBeNull()
-    })
-
-    describe('uiHandle', () => {
-
-      describe('getUserToSelectARegion', () => {
-
-        it('on init, expect getUserToSelectRegion to be length 0', () => {
-          const service = TestBed.inject(AtlasViewerAPIServices)
-          expect(service.getUserToSelectRegion.length).toEqual(0)
-        })
-        it('calling getUserToSelectARegion() populates getUserToSelectRegion', () => {
-          const service = TestBed.inject(AtlasViewerAPIServices)
-
-          const pr = service.interactiveViewer.uiHandle.getUserToSelectARegion('hello world')
-          
-          expect(service.getUserToSelectRegion.length).toEqual(1)
-          const { promise, message, rs, rj } = service.getUserToSelectRegion[0]
-          expect(promise).toEqual(pr)
-          expect(message).toEqual('hello world')
-          
-          expect(rs).not.toBeUndefined()
-          expect(rs).not.toBeNull()
-
-          expect(rj).not.toBeUndefined()
-          expect(rj).not.toBeNull()
-        })
-      })
-
-      describe('> getUserToSelectRoi', () => {
-        it('> calling getUserToSelectRoi without spec throws error', () => {
-          const service = TestBed.inject(AtlasViewerAPIServices)
-          expect(() => {
-            service.interactiveViewer.uiHandle.getUserToSelectRoi('hello world')
-          }).toThrow()
-        })
-
-        it('> calling getUserToSelectRoi without spec.type throws', () => {
-          const service = TestBed.inject(AtlasViewerAPIServices)
-          expect(() => {
-            service.interactiveViewer.uiHandle.getUserToSelectRoi('hello world', { foo: 'bar' } as any)
-          }).toThrow()
-        })
-
-        it('> calling getUserToSelectRoi populates getUserToSelectRegion with malformed spec.type is fine', () => {
-          const service = TestBed.inject(AtlasViewerAPIServices)
-          expect(() => {
-            service.interactiveViewer.uiHandle.getUserToSelectRoi('hello world', { type: 'foobar' })
-          }).not.toThrow()
-        })
-        it('> calling getUserToSelectRoi populates getUserToSelectRegion', () => {
-
-          const service = TestBed.inject(AtlasViewerAPIServices)
-
-          const pr = service.interactiveViewer.uiHandle.getUserToSelectRoi('hello world', { type: 'POINT' })
-          
-          expect(service.getUserToSelectRegion.length).toEqual(1)
-          const { promise, message, spec, rs, rj } = service.getUserToSelectRegion[0]
-          expect(promise).toEqual(pr)
-          expect(message).toEqual('hello world')
-          expect(spec).toEqual({ type: 'POINT' })
-          
-          expect(rs).not.toBeFalsy()
-          expect(rj).not.toBeFalsy()
-        })
-      })
-
-      describe('cancelPromise', () => {
-        it('calling cancelPromise removes pr from getUsertoSelectRegion', done => {
-
-          const service = TestBed.inject(AtlasViewerAPIServices)
-          const pr = service.interactiveViewer.uiHandle.getUserToSelectARegion('test')
-          pr.catch(e => {
-            expect(e.userInitiated).toEqual(false)
-            expect(service.getUserToSelectRegion.length).toEqual(0)
-            done()
-          })
-          service.interactiveViewer.uiHandle.cancelPromise(pr)
-        })
-
-        it('alling cancelPromise on non existing promise, throws ', () => {
-
-          const service = TestBed.inject(AtlasViewerAPIServices)
-          const pr = service.interactiveViewer.uiHandle.getUserToSelectARegion('test')
-          service.interactiveViewer.uiHandle.cancelPromise(pr)
-          expect(() => {
-            service.interactiveViewer.uiHandle.cancelPromise(pr)
-          }).toThrow()
-        })
-      })
-
-      describe('getUserToSelectARegion, cancelPromise and userCancel', () => {
-        it('if token is provided, on getUserToSelectRegionUI$ next should follow by call to injected function', () => {
-          const service = TestBed.inject(AtlasViewerAPIServices)
-          
-          const rsSpy = jasmine.createSpy('rs') 
-          const rjSpy = jasmine.createSpy('rj')
-          const mockObj = {
-            message: 'test',
-            promise: new Promise((rs, rj) => {}),
-            rs: rsSpy,
-            rj: rjSpy,
-          }
-          service.getUserToSelectRegionUI$.next([ mockObj ])
-          
-
-          expect(cancellableDialogSpy).toHaveBeenCalled()
-          
-          const arg = cancellableDialogSpy.calls.mostRecent().args
-          expect(arg[0]).toEqual('test')
-          expect(arg[1].userCancelCallback).toBeTruthy()
-        })
-
-        it('if multiple regionUIs are provided, only the last one is used', () => {
-          const service = TestBed.inject(AtlasViewerAPIServices)
-          
-          const rsSpy = jasmine.createSpy('rs') 
-          const rjSpy = jasmine.createSpy('rj')
-          const mockObj1 = {
-            message: 'test1',
-            promise: new Promise((rs, rj) => {}),
-            rs: rsSpy,
-            rj: rjSpy,
-          }
-          const mockObj2 = {
-            message: 'test2',
-            promise: new Promise((rs, rj) => {}),
-            rs: rsSpy,
-            rj: rjSpy,
-          }
-          service.getUserToSelectRegionUI$.next([ mockObj1, mockObj2 ])
-          
-          expect(cancellableDialogSpy).toHaveBeenCalled()
-          
-          const arg = cancellableDialogSpy.calls.mostRecent().args
-          expect(arg[0]).toEqual('test2')
-          expect(arg[1].userCancelCallback).toBeTruthy()
-        })
-
-        describe('calling userCacellationCb', () => {
-
-          it('correct usage => in removeBasedOnPr called, rj with userini as true', fakeAsync(() => {
-            const service = TestBed.inject(AtlasViewerAPIServices)
-            
-            const rsSpy = jasmine.createSpy('rs') 
-            const rjSpy = jasmine.createSpy('rj')
-            const promise = new Promise((rs, rj) => {})
-            const mockObj = {
-              message: 'test',
-              promise,
-              rs: rsSpy,
-              rj: rjSpy,
-            }
-
-            const removeBaseOnPr = spyOn(service, 'removeBasedOnPr').and.returnValue(null)
-
-            service.getUserToSelectRegionUI$.next([ mockObj ])
-            const arg = cancellableDialogSpy.calls.mostRecent().args
-            const cb = arg[1].userCancelCallback
-            cb()
-            tick(100)
-            expect(rjSpy).toHaveBeenCalledWith({ userInitiated: true })
-            expect(removeBaseOnPr).toHaveBeenCalledWith(promise, { userInitiated: true })
-            
-          }))
-
-          it('incorrect usage (resolve) => removebasedonpr, rj not called', fakeAsync(() => {
-
-            const service = TestBed.inject(AtlasViewerAPIServices)
-            
-            const dummyObj = {
-              hello:'world'
-            }
-
-            const rsSpy = jasmine.createSpy('rs') 
-            const rjSpy = jasmine.createSpy('rj')
-            const promise = Promise.resolve(dummyObj)
-            const mockObj = {
-              message: 'test',
-              promise,
-              rs: rsSpy,
-              rj: rjSpy,
-            }
-
-            const removeBaseOnPr = spyOn(service, 'removeBasedOnPr').and.returnValue(null)
-
-            service.getUserToSelectRegionUI$.next([ mockObj ])
-            const arg = cancellableDialogSpy.calls.mostRecent().args
-            const cb = arg[1].userCancelCallback
-            cb()
-            tick(100)
-            expect(rjSpy).not.toHaveBeenCalled()
-            expect(removeBaseOnPr).not.toHaveBeenCalled()
-            
-          }))
-
-          it('incorrect usage (reject) => removebasedonpr, rj not called', fakeAsync(() => {
-
-            const service = TestBed.inject(AtlasViewerAPIServices)
-            
-            const dummyObj = {
-              hello:'world'
-            }
-
-            const rsSpy = jasmine.createSpy('rs') 
-            const rjSpy = jasmine.createSpy('rj')
-            const promise = Promise.reject(dummyObj)
-            const mockObj = {
-              message: 'test',
-              promise,
-              rs: rsSpy,
-              rj: rjSpy,
-            }
-
-            const removeBaseOnPr = spyOn(service, 'removeBasedOnPr').and.returnValue(null)
-
-            service.getUserToSelectRegionUI$.next([ mockObj ])
-            const arg = cancellableDialogSpy.calls.mostRecent().args
-            const cb = arg[1].userCancelCallback
-            cb()
-            tick(100)
-            expect(rjSpy).not.toHaveBeenCalled()
-            expect(removeBaseOnPr).not.toHaveBeenCalled()
-            
-          }))
-        })
-      })
-    })
-  })
-})
diff --git a/src/atlasViewer/atlasViewer.apiService.service.ts b/src/atlasViewer/atlasViewer.apiService.service.ts
deleted file mode 100644
index ad6d2fb7e7057784edadb8b227f015f4e83f74ac..0000000000000000000000000000000000000000
--- a/src/atlasViewer/atlasViewer.apiService.service.ts
+++ /dev/null
@@ -1,468 +0,0 @@
-/* eslint-disable @typescript-eslint/no-empty-function */
-import {Injectable, NgZone, Optional, Inject, OnDestroy, InjectionToken} from "@angular/core";
-import { MatSnackBar } from "@angular/material/snack-bar";
-import { select, Store } from "@ngrx/store";
-import { Observable, Subject, Subscription, from, race, of, } from "rxjs";
-import { distinctUntilChanged, map, filter, startWith, switchMap, catchError, mapTo, take } from "rxjs/operators";
-import { DialogService } from "src/services/dialogService.service";
-import { uiStateMouseOverSegmentsSelector } from "src/services/state/uiState/selectors";
-import {
-  viewerStateFetchedTemplatesSelector,
-} from "src/services/state/viewerState/selectors";
-import {
-  getLabelIndexMap,
-  getMultiNgIdsRegionsLabelIndexMap,
-  IavRootStoreInterface,
-  safeFilter
-} from "src/services/stateStore.service";
-import { ClickInterceptor, CLICK_INTERCEPTOR_INJECTOR } from "src/util";
-import { FRAGMENT_EMIT_RED } from "src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component";
-import { IPluginManifest, PluginServices } from "src/plugin";
-import { ILoadMesh } from 'src/messaging/types'
-import { CANCELLABLE_DIALOG } from "src/util/interfaces";
-
-declare let window
-
-interface IRejectUserInput{
-  userInitiated: boolean
-  reason?: string
-}
-
-interface IGetUserSelectRegionPr{
-  message: string
-  promise: Promise<any>
-  spec?: ICustomRegionSpec
-  rs: (region: any) => void
-  rj: (reject: IRejectUserInput) => void
-}
-
-@Injectable({
-  providedIn : 'root'
-})
-
-export class AtlasViewerAPIServices implements OnDestroy{
-
-  public loadMesh$ = new Subject<ILoadMesh>()
-
-  private onDestoryCb: (() => void)[] = []
-  private loadedTemplates$: Observable<any>
-  private selectParcellation$: Observable<any>
-  public interactiveViewer: IInteractiveViewerInterface
-
-  public loadedLibraries: Map<string, {counter: number, src: HTMLElement|null}> = new Map()
-
-  public removeBasedOnPr = (pr: Promise<any>, {userInitiated = false} = {}) => {
-
-    const idx = this.getUserToSelectRegion.findIndex(({ promise }) => promise === pr)
-    if (idx >=0) {
-      const { rj } = this.getUserToSelectRegion.splice(idx, 1)[0]
-      this.getUserToSelectRegionUI$.next([...this.getUserToSelectRegion])
-      this.zone.run(() => {  })
-      rj({ userInitiated })
-    }
-    else throw new Error(`This promise has already been fulfilled.`)
-
-  }
-
-  private dismissDialog: () => void
-  public getUserToSelectRegion: IGetUserSelectRegionPr[] = []
-  public getUserToSelectRegionUI$: Subject<IGetUserSelectRegionPr[]> = new Subject()
-
-  public getNextUserRegionSelectHandler: () => IGetUserSelectRegionPr = () => {
-    if (this.getUserToSelectRegion.length > 0) {
-      return this.getUserToSelectRegion[this.getUserToSelectRegion.length - 1]
-    }
-    else return null
-  }
-
-  public popUserRegionSelectHandler = () => {
-    if (this.getUserToSelectRegion.length > 0) {
-      this.getUserToSelectRegion.pop()
-      this.getUserToSelectRegionUI$.next([...this.getUserToSelectRegion])
-    }
-  }
-
-  private s: Subscription[] = []
-
-  private onMouseClick(ev: any): boolean {
-    const { rs, spec } = this.getNextUserRegionSelectHandler() || {}
-    if (!!rs) {
-
-      let moSegments
-      this.store.pipe(
-        select(uiStateMouseOverSegmentsSelector),
-        take(1)
-      ).subscribe(val => moSegments = val)
-
-      /**
-       * getROI api
-       */
-      if (spec) {
-        /**
-         * if spec of overwrite click is for a point
-         */
-        if (spec.type === EnumCustomRegion.POINT) {
-          this.popUserRegionSelectHandler()
-          let mousePositionReal
-          // rather than commiting mousePositionReal in state via action, do a single subscription instead.
-          // otherwise, the state gets updated way too often
-          if (window && (window as any).nehubaViewer) {
-            (window as any).nehubaViewer.mousePosition.inRealSpace
-              .take(1)
-              .subscribe(floatArr => {
-                mousePositionReal = floatArr && Array.from(floatArr).map((val: number) => val / 1e6)
-              })
-          }
-          rs({
-            type: spec.type,
-            payload: mousePositionReal
-          })
-          return false
-        }
-
-        /**
-         * if spec of overwrite click is for a point
-         */
-        if (spec.type === EnumCustomRegion.PARCELLATION_REGION) {
-
-          if (!!moSegments && Array.isArray(moSegments) && moSegments.length > 0) {
-            this.popUserRegionSelectHandler()
-            rs({
-              type: spec.type,
-              payload: moSegments
-            })
-            return false
-          }
-        }
-      } else {
-        /**
-         * selectARegion API
-         * TODO deprecate
-         */
-        if (!!moSegments && Array.isArray(moSegments) && moSegments.length > 0) {
-          this.popUserRegionSelectHandler()
-          rs(moSegments[0])
-          return false
-        }
-      }
-    }
-    return true
-  }
-
-  constructor(
-    private store: Store<IavRootStoreInterface>,
-    private dialogService: DialogService,
-    private snackbar: MatSnackBar,
-    private zone: NgZone,
-    private pluginService: PluginServices,
-    @Optional() @Inject(CANCELLABLE_DIALOG) openCancellableDialog: (message: string, options: any) => () => void,
-    @Optional() @Inject(CLICK_INTERCEPTOR_INJECTOR) clickInterceptor: ClickInterceptor
-  ) {
-    if (clickInterceptor) {
-      const { register, deregister } = clickInterceptor
-      const onMouseClick = this.onMouseClick.bind(this)
-      register(onMouseClick)
-      this.onDestoryCb.push(() => deregister(onMouseClick))
-    }
-    if (openCancellableDialog) {
-      this.s.push(
-        this.getUserToSelectRegionUI$.pipe(
-          distinctUntilChanged(),
-          switchMap(arr => {
-            if (this.dismissDialog) {
-              this.dismissDialog()
-              this.dismissDialog = null
-            }
-
-            if (arr.length === 0) return of(null)
-
-            const last = arr[arr.length - 1]
-            const { message, promise } = last
-            return race(
-              from(new Promise(resolve => {
-                this.dismissDialog = openCancellableDialog(message, {
-                  userCancelCallback: () => {
-                    resolve(last)
-                  },
-                  ariaLabel: message
-                })
-              })),
-              from(promise).pipe(
-                catchError(() => of(null)),
-                mapTo(null),
-              )
-            )
-          })
-        ).subscribe(obj => {
-          if (obj) {
-            const { promise, rj } = obj
-            rj({ userInitiated: true })
-            this.removeBasedOnPr(promise, { userInitiated: true })
-          }
-        })
-      )
-    }
-
-    this.loadedTemplates$ = this.store.pipe(
-      select(viewerStateFetchedTemplatesSelector)
-    )
-
-    this.selectParcellation$ = this.store.pipe(
-      select('viewerState'),
-      safeFilter('parcellationSelected'),
-      map(state => state.parcellationSelected),
-    )
-
-    this.interactiveViewer = {
-      metadata : {
-        selectedTemplateBSubject : this.store.pipe(
-          select('viewerState'),
-          safeFilter('templateSelected'),
-          map(state => state.templateSelected)),
-
-        selectedParcellationBSubject : this.store.pipe(
-          select('viewerState'),
-          safeFilter('parcellationSelected'),
-          map(state => state.parcellationSelected)),
-
-        selectedRegionsBSubject : this.store.pipe(
-          select('viewerState'),
-          safeFilter('regionsSelected'),
-          map(state => state.regionsSelected),
-          distinctUntilChanged((arr1, arr2) =>
-            arr1.length === arr2.length &&
-            (arr1 as any[]).every((item, index) => item.name === arr2[index].name)),
-        ),
-
-        loadedTemplates : [],
-
-        // TODO deprecate
-        regionsLabelIndexMap : new Map(),
-
-        layersRegionLabelIndexMap: new Map(),
-
-        datasetsBSubject : this.store.pipe(
-          select('dataStore'),
-          select('fetchedDataEntries'),
-          startWith([])
-        ),
-      },
-      uiHandle : {
-        getModalHandler : () => {
-          throw new Error(`uihandle.getModalHandler has been deprecated`)
-        },
-
-        /* to be overwritten by atlasViewer.component.ts */
-        getToastHandler : () => {
-          throw new Error('uiHandle.getToastHandler has been deprecated')
-        },
-
-        /**
-         * to be overwritten by atlas
-         */
-        launchNewWidget: (manifest) => this.pluginService.launchNewWidget(manifest)
-          .then(() => {
-            // trigger change detection in Angular
-            // otherwise, model won't be updated until user input
-
-            /* eslint-disable-next-line @typescript-eslint/no-empty-function */
-            this.zone.run(() => {  })
-          }),
-
-        getUserInput: config => this.dialogService.getUserInput(config) ,
-        getUserConfirmation: config => this.dialogService.getUserConfirm(config),
-
-        getUserToSelectARegion: message => {
-          console.warn(`interactiveViewer.uiHandle.getUserToSelectARegion is becoming deprecated. Use getUserToSelectRoi instead`)
-          const obj = {
-            message,
-            promise: null,
-            rs: null,
-            rj: null
-          }
-          const pr = new Promise((rs, rj) => {
-            obj.rs = rs
-            obj.rj = rj
-          })
-
-          obj.promise = pr
-
-          this.getUserToSelectRegion.push(obj)
-          this.getUserToSelectRegionUI$.next([...this.getUserToSelectRegion])
-          this.zone.run(() => {
-
-          })
-          return pr
-        },
-        getUserToSelectRoi: (message: string, spec: ICustomRegionSpec) => {
-          if (!spec || !spec.type) throw new Error(`spec.type must be defined for getUserToSelectRoi`)
-          const obj = {
-            message,
-            spec,
-            promise: null,
-            rs: null,
-            rj: null
-          }
-          const pr = new Promise((rs, rj) => {
-            obj.rs = rs
-            obj.rj = rj
-          })
-
-          obj.promise = pr
-
-          this.getUserToSelectRegion.push(obj)
-          this.getUserToSelectRegionUI$.next([...this.getUserToSelectRegion])
-          this.zone.run(() => {
-
-          })
-          return pr
-        },
-
-        cancelPromise: pr => {
-          this.removeBasedOnPr(pr)
-
-          this.zone.run(() => {  })
-        }
-      },
-      pluginControl: new Proxy({}, {
-        get: (_, prop) => {
-          if (prop === 'loadExternalLibraries') return this.pluginService.loadExternalLibraries
-          if (prop === 'unloadExternalLibraries') return this.pluginService.unloadExternalLibraries
-          if (typeof prop === 'string') return this.pluginService.pluginHandlersMap.get(prop)
-          return undefined
-        }
-      }) as any,
-    }
-    window.interactiveViewer = this.interactiveViewer
-    this.init()
-  }
-
-  private init() {
-    this.loadedTemplates$.subscribe(templates => this.interactiveViewer.metadata.loadedTemplates = templates)
-    this.selectParcellation$.pipe(
-      filter(p => !!p && p.regions),
-      distinctUntilChanged()
-    ).subscribe(parcellation => {
-      this.interactiveViewer.metadata.regionsLabelIndexMap = getLabelIndexMap(parcellation.regions)
-      this.interactiveViewer.metadata.layersRegionLabelIndexMap = getMultiNgIdsRegionsLabelIndexMap(parcellation)
-    })
-
-    this.s.push(
-      this.loadMesh$.subscribe(({ url, id, type, customFragmentColor = null }) => {
-        if (!this.interactiveViewer.viewerHandle) {
-          this.snackbar.open('No atlas loaded! Loading mesh failed!', 'Dismiss')
-        }
-        this.interactiveViewer.viewerHandle?.loadLayer({
-          [id]: {
-            type: 'mesh',
-            source: `vtk://${url}`,
-            shader: `void main(){${customFragmentColor || FRAGMENT_EMIT_RED};}`
-          }
-        })
-      })
-    )
-  }
-
-  ngOnDestroy(){
-    while (this.onDestoryCb.length > 0) this.onDestoryCb.pop()()
-    while(this.s.length > 0){
-      this.s.pop().unsubscribe()
-    }
-  }
-}
-
-export interface IInteractiveViewerInterface {
-
-  metadata: {
-    selectedTemplateBSubject: Observable<any|null>
-    selectedParcellationBSubject: Observable<any|null>
-    selectedRegionsBSubject: Observable<any[]|null>
-    loadedTemplates: any[]
-    regionsLabelIndexMap: Map<number, any> | null
-    layersRegionLabelIndexMap: Map<string, Map<number, any>>
-    datasetsBSubject: Observable<any[]>
-  }
-
-  viewerHandle?: IVIewerHandle
-
-  uiHandle: {
-    getModalHandler: () => void
-    getToastHandler: () => void
-    launchNewWidget: (manifest: IPluginManifest) => Promise<any>
-    getUserInput: (config: IGetUserInputConfig) => Promise<string>
-    getUserConfirmation: (config: IGetUserConfirmation) => Promise<any>
-    getUserToSelectARegion: (selectingMessage: any) => Promise<any>
-    getUserToSelectRoi: (selectingMessage: string, spec?: ICustomRegionSpec) => Promise<any>
-    cancelPromise: (pr: Promise<any>) => void
-  }
-
-  pluginControl: {
-    loadExternalLibraries: (libraries: string[]) => Promise<void>
-    unloadExternalLibraries: (libraries: string[]) => void
-    [key: string]: any
-  }
-}
-
-interface IGetUserConfirmation {
-  title?: string
-  message?: string
-}
-
-interface IGetUserInputConfig extends IGetUserConfirmation {
-  placeholder?: string
-  defaultValue?: string
-}
-
-export interface IUserLandmark {
-  name: string
-  position: [number, number, number]
-  id: string /* probably use the it to track and remove user landmarks */
-  highlight: boolean
-  color?: [number, number, number]
-}
-
-export enum EnumCustomRegion{
-  POINT = 'POINT',
-  PARCELLATION_REGION = 'PARCELLATION_REGION',
-}
-
-export interface ICustomRegionSpec{
-  type: string // type of EnumCustomRegion
-}
-
-export interface IVIewerHandle {
-
-  setNavigationLoc: (coordinates: [number, number, number], realSpace?: boolean) => void
-  moveToNavigationLoc: (coordinates: [number, number, number], realSpace?: boolean) => void
-  setNavigationOri: (quat: [number, number, number, number]) => void
-  moveToNavigationOri: (quat: [number, number, number, number]) => void
-  showSegment: (labelIndex: number) => void
-  hideSegment: (labelIndex: number) => void
-  showAllSegments: () => void
-  hideAllSegments: () => void
-
-  getLayersSegmentColourMap: () => Map<string, Map<number, {red: number, green: number, blue: number}>>
-
-  applyLayersColourMap: (newLayerColourMap: Map<string, Map<number, {red: number, green: number, blue: number}>>) => void
-
-  loadLayer: (layerobj: any) => any
-  removeLayer: (condition: {name: string | RegExp}) => string[]
-  setLayerVisibility: (condition: {name: string|RegExp}, visible: boolean) => void
-
-  add3DLandmarks: (landmarks: IUserLandmark[]) => void
-  remove3DLandmarks: (ids: string[]) => void
-
-  mouseEvent: Observable<{eventName: string, event: MouseEvent}>
-  mouseOverNehuba: Observable<{labelIndex: number, foundRegion: any | null}>
-  mouseOverNehubaLayers: Observable<Array<{layer: {name: string}, segment: any | number }>>
-  mouseOverNehubaUI: Observable<{ annotation: any, segments: any, landmark: any, customLandmark: any }>
-  getNgHash: () => string
-}
-
-export type TSetViewerHandle = (viewerHandle: IVIewerHandle) => void
-
-export const API_SERVICE_SET_VIEWER_HANDLE_TOKEN = new InjectionToken<TSetViewerHandle>('API_SERVICE_SET_VIEWER_HANDLE_TOKEN')
-
-export const setViewerHandleFactory = (apiService: AtlasViewerAPIServices) => {
-  return (viewerHandle: IVIewerHandle) => apiService.interactiveViewer.viewerHandle = viewerHandle
-}
diff --git a/src/atlasViewer/atlasViewer.component.ts b/src/atlasViewer/atlasViewer.component.ts
index f407f68944dad36fb049901fcc59e9e91fc77f47..15dbb49421acaec542c842562853d946422e64d6 100644
--- a/src/atlasViewer/atlasViewer.component.ts
+++ b/src/atlasViewer/atlasViewer.component.ts
@@ -12,18 +12,8 @@ import {
 } from "@angular/core";
 import { Store, select } from "@ngrx/store";
 import { Observable, Subscription, merge, timer, fromEvent } from "rxjs";
-import { map, filter, distinctUntilChanged, delay, switchMapTo, take, startWith } from "rxjs/operators";
+import { filter, delay, switchMapTo, take, startWith } from "rxjs/operators";
 
-import {
-  IavRootStoreInterface,
-  isDefined,
-  safeFilter,
-} from "../services/stateStore.service";
-import { WidgetServices } from "src/widget";
-
-import { LocalFileService } from "src/services/localFile.service";
-import { AGREE_COOKIE } from "src/services/state/uiState.store";
-import { isSame } from "src/util/fn";
 import { colorAnimation } from "./atlasViewer.animation"
 import { MouseHoverDirective } from "src/mouseoverModule";
 import {MatSnackBar, MatSnackBarRef} from "@angular/material/snack-bar";
@@ -31,10 +21,11 @@ import {MatDialog, MatDialogRef} from "@angular/material/dialog";
 import { ARIA_LABELS, CONST } from 'common/constants'
 
 import { SlServiceService } from "src/spotlight/sl-service.service";
-import { PureContantService } from "src/util";
 import { ClickInterceptorService } from "src/glue";
 import { environment } from 'src/environments/environment'
 import { DOCUMENT } from "@angular/common";
+import { userPreference } from "src/state"
+import { DARKTHEME } from "src/util/injectionTokens";
 
 /**
  * TODO
@@ -57,7 +48,6 @@ const compareFn = (it, item) => it.name === item.name
 export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit {
 
   public CONST = CONST
-  public CONTEXT_MENU_ARIA_LABEL = ARIA_LABELS.CONTEXT_MENU
   public compareFn = compareFn
 
   @ViewChild('cookieAgreementComponent', {read: TemplateRef}) public cookieAgreementComponent: TemplateRef<any>
@@ -70,66 +60,28 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit {
   public ismobile: boolean = false
   public meetsRequirement: boolean = true
 
-  public sidePanelView$: Observable<string|null>
-  private newViewer$: Observable<any>
-
   private snackbarRef: MatSnackBarRef<any>
-  public snackbarMessage$: Observable<symbol>
 
   public onhoverLandmark$: Observable<{landmarkName: string, datasets: any} | null>
 
   private subscriptions: Subscription[] = []
 
-  private selectedParcellation$: Observable<any>
   public selectedParcellation: any
 
   private cookieDialogRef: MatDialogRef<any>
 
   constructor(
-    private store: Store<IavRootStoreInterface>,
-    private widgetServices: WidgetServices,
-    private pureConstantService: PureContantService,
+    private store: Store<any>,
     private matDialog: MatDialog,
     private rd: Renderer2,
-    public localFileService: LocalFileService,
     private snackbar: MatSnackBar,
     private el: ElementRef,
     private slService: SlServiceService,
     private clickIntService: ClickInterceptorService,
-    @Inject(DOCUMENT) private document,
+    @Inject(DOCUMENT) private document: Document,
+    @Inject(DARKTHEME) private darktheme$: Observable<boolean>
   ) {
 
-    this.snackbarMessage$ = this.store.pipe(
-      select('uiState'),
-      select("snackbarMessage"),
-    )
-
-    this.sidePanelView$ = this.store.pipe(
-      select('uiState'),
-      filter(state => isDefined(state)),
-      map(state => state.focusedSidePanel),
-    )
-
-    this.newViewer$ = this.store.pipe(
-      select('viewerState'),
-      select('templateSelected'),
-      distinctUntilChanged(isSame),
-    )
-
-    this.selectedParcellation$ = this.store.pipe(
-      select('viewerState'),
-      safeFilter('parcellationSelected'),
-      map(state => state.parcellationSelected),
-      distinctUntilChanged(),
-    )
-
-    this.subscriptions.push(
-      this.selectedParcellation$.subscribe(parcellation => {
-        this.selectedParcellation = parcellation
-      }),
-
-    )
-
     const error = this.el.nativeElement.getAttribute('data-error')
 
     if (error) {
@@ -165,35 +117,13 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit {
     }
 
     this.subscriptions.push(
-      this.pureConstantService.useTouchUI$.subscribe(bool => this.ismobile = bool),
-    )
-
-    this.subscriptions.push(
-      this.snackbarMessage$.pipe(
-        // angular material issue
-        // see https://github.com/angular/angular/issues/15634
-        // and https://github.com/angular/components/issues/11357
-        delay(0),
-      ).subscribe(messageSymbol => {
-        if (this.snackbarRef) { this.snackbarRef.dismiss() }
-
-        if (!messageSymbol) { return }
-
-        const message = messageSymbol.description
-        this.snackbarRef = this.snackbar.open(message, 'Dismiss', {
-          duration: 5000,
-        })
-      }),
-    )
-
-    this.subscriptions.push(
-      this.newViewer$.subscribe(() => {
-        this.widgetServices.clearAllWidgets()
-      }),
+      this.store.pipe(
+        select(userPreference.selectors.useMobileUi),
+      ).subscribe(bool => this.ismobile = bool),
     )
 
     this.subscriptions.push(
-      this.pureConstantService.darktheme$.subscribe(flag => {
+      this.darktheme$.subscribe(flag => {
         this.rd.setAttribute(this.document.body, 'darktheme', this.meetsRequirement && flag.toString())
       }),
     )
@@ -220,9 +150,8 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit {
      * TODO avoid creating new views in lifecycle hooks in general
      */
     this.store.pipe(
-      select('uiState'),
-      select('agreedCookies'),
-      filter(agreed => !agreed),
+      select(userPreference.selectors.agreedToCookie),
+      filter(val => !val),
       delay(0),
     ).subscribe(() => {
       this.cookieDialogRef = this.matDialog.open(this.cookieAgreementComponent)
@@ -264,31 +193,23 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit {
 
   public cookieClickedOk() {
     if (this.cookieDialogRef) { this.cookieDialogRef.close() }
-    this.store.dispatch({
-      type: AGREE_COOKIE,
-    })
+    this.store.dispatch(
+      userPreference.actions.agreeCookie()
+    )
   }
 
+  private supportEmailAddress = `support@ebrains.eu`
   public quickTourFinale = {
     order: 1e6,
     descriptionMd: `That's it! We hope you enjoy your stay.
 
 ---
 
-If you have any comments or need further support, please contact us at [${this.pureConstantService.supportEmailAddress}](mailto:${this.pureConstantService.supportEmailAddress})`,
-    description: `That's it! We hope you enjoy your stay. If you have any comments or need further support, please contact us at ${this.pureConstantService.supportEmailAddress}`,
+If you have any comments or need further support, please contact us at [${this.supportEmailAddress}](mailto:${this.supportEmailAddress})`,
+    description: `That's it! We hope you enjoy your stay. If you have any comments or need further support, please contact us at ${this.supportEmailAddress}`,
     position: 'center'
   }
 
   @HostBinding('attr.version')
   public _version: string = environment.VERSION
 }
-
-export interface INgLayerInterface {
-  name: string
-  visible: boolean
-  source: string
-  type: string // image | segmentation | etc ...
-  transform?: [[number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number]] | null
-  // colormap : string
-}
diff --git a/src/atlasViewer/atlasViewer.style.css b/src/atlasViewer/atlasViewer.style.css
index 0f938ac6632a634a35ee793318a408c0e70248fd..3bbc083c049ac6228a4f187f8e599d399c3a8487 100644
--- a/src/atlasViewer/atlasViewer.style.css
+++ b/src/atlasViewer/atlasViewer.style.css
@@ -11,66 +11,27 @@
   display: block;
 }
 
-ui-nehuba-container
-{
-  position:absolute;
-  top:0;
-  left:0;
-  width:100%;
-  height:100%;
-}
-
-layout-floating-container
-{
-  width:100%;
-  height:100%;
-  overflow:hidden;
-}
-
-layout-floating-container > *
-{
-  position: absolute;
-  left: 0;
-  top: 0;
-}
-
-mat-list[dense].contextual-block
-{
-  display: inline-block;
-  background-color:rgba(200,200,200,0.8);
-}
-
-:host-context([darktheme="true"]) mat-list[dense].contextual-block
-{
-  background-color : rgba(30,30,30,0.8);
-}
-
-[fixedMouseContextualContainerDirective]
-{
-  max-width: 100%;
-}
 
 div.displayCard
 {
   opacity: 0.8;
 }
 
-mat-sidenav {
-  box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23);
-}
-
-region-menu
+.widget-canvas-container
 {
-  display:inline-block;
-}
-
-.floating-container
-{
-  max-width: 350px;
+  position: absolute;
+  top: 0;
+  left: 0;
+  z-index: 9;
+  width: 100%;
+  height: 100%;
+  pointer-events: none;
 }
-
-logo-container
+.widget-canvas
 {
-  height: 2rem;
-  opacity: 0.2;
+  position: absolute;
+  width: 0;
+  height: 0;
+  pointer-events: none;
+  overflow: visible;
 }
diff --git a/src/atlasViewer/atlasViewer.template.html b/src/atlasViewer/atlasViewer.template.html
index 5549a8e38914948e7157fcfc6cde6a4f63bd9153..ed2e2c6da37077aeea63cf59942a0b7dc7386986 100644
--- a/src/atlasViewer/atlasViewer.template.html
+++ b/src/atlasViewer/atlasViewer.template.html
@@ -1,8 +1,3 @@
-
-<!-- required for manufacturing plugin templates -->
-<div pluginFactoryDirective>
-</div>
-
 <ng-container *ngIf="meetsRequirement; else doesNotMeetReqTemplate">
 
   <ng-container *ngTemplateOutlet="viewerBody">
@@ -29,70 +24,24 @@
 <!-- atlas template -->
 <ng-template #viewerBody>
   <div class="w-100 h-100"
-    iav-media-query
     quick-tour
     [quick-tour-position]="quickTourFinale.position"
     [quick-tour-description]="quickTourFinale.description"
     [quick-tour-description-md]="quickTourFinale.descriptionMd"
     [quick-tour-order]="quickTourFinale.order"
     [quick-tour-overwrite-arrow]="emptyArrowTmpl"
-    quick-tour-severity="low"
-    #media="iavMediaQuery">
+    quick-tour-severity="low">
     <!-- prevent default is required so that user do not zoom in on UI or scroll on mobile UI -->
     <iav-cmp-viewer-container
       class="w-100 h-100 d-block"
-      [ismobile]="(media.mediaBreakPoint$ | async) > 3"
       iav-captureClickListenerDirective
       [iav-captureClickListenerDirective-captureDocument]="true"
       (iav-captureClickListenerDirective-onUnmovedClick)="mouseClickDocument($event)">
     </iav-cmp-viewer-container>
 
-    <layout-floating-container
-      zIndex="13"
-      #floatingOverlayContainer>
-      <div floatingContainerDirective>
-      </div>
-
-      <div *ngIf="(media.mediaBreakPoint$ | async) < 3"
-        class="fixed-bottom pe-none mb-2 d-flex justify-content-center">
-        <ng-container *ngTemplateOutlet="logoTmpl">
-        </ng-container>
-      </div>
-
-      <div *ngIf="!ismobile" floatingMouseContextualContainerDirective>
-
-        <div class="h-0"
-          iav-mouse-hover
-          #iavMouseHoverContextualBlock="iavMouseHover">
-        </div>
-        <mat-list dense class="contextual-block">
-
-          <mat-list-item *ngFor="let cvtOutput of iavMouseHoverContextualBlock.currentOnHoverObs$ | async | mouseoverCvt"
-            class="h-auto">
-
-            <mat-icon
-              [fontSet]="cvtOutput.icon.fontSet"
-              [fontIcon]="cvtOutput.icon.fontIcon"
-              mat-list-icon>
-            </mat-icon>
-
-            <div matLine>{{ cvtOutput.text }}</div>
-
-          </mat-list-item>
-        </mat-list>
-        <!-- TODO Potentially implementing plugin contextual info -->
-      </div>
-
-      <div class="floating-container"
-        [attr.aria-label]="CONTEXT_MENU_ARIA_LABEL"
-        fixedMouseContextualContainerDirective
-        #fixedContainer="iavFixedMouseCtxContainer">
-
-        <!-- mouse on click context menu, currently not used -->
-
-      </div>
-
-    </layout-floating-container>
+    <div class="widget-canvas-container">
+      <div widget-canvas class="widget-canvas"></div>
+    </div>
   </div>
 </ng-template>
 
@@ -101,11 +50,6 @@
   <not-supported-component></not-supported-component>
 </ng-template>
 
-<!-- logo tmpl -->
-<ng-template #logoTmpl>
-  <logo-container></logo-container>
-</ng-template>
-
 <ng-template #idleOverlay>
   <tryme-component></tryme-component>
 </ng-template>
diff --git a/src/atlasViewerExports/export.html b/src/atlasViewerExports/export.html
deleted file mode 100644
index b4a41a088e065668815706796260dc8f76e1faf9..0000000000000000000000000000000000000000
--- a/src/atlasViewerExports/export.html
+++ /dev/null
@@ -1,169 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-  <meta charset="UTF-8">
-  <meta name="viewport" content="width=720, initial-scale=1.0">
-  <title>Interactive Viewer Exported Components</title>
-  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
-  
-  <script src = "https://unpkg.com/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-  <script src = "export.js"></script>
-
-  <script>
-    document.addEventListener('DOMContentLoaded',()=>{
-      const script = `
-const inputItem = {
-  "name" : "drinks",
-  "children" : [{
-    "name":"coffee",
-    "children":[{
-      "name":"flatwhite",
-      "children":[]
-    },{
-      "name":"espresso",
-      "children":[]
-    }]
-  },{
-    "name" : "tea",
-    "children" : [{
-      "name" : "green tea",
-      "children" : [],
-    },{
-      "name" : "black tea",
-      "children" : []
-    }]
-  }] 
-}
-const tree = document.getElementById('tree-element')
-tree.setAttribute('input-item',JSON.stringify(inputItem))
-`
-      const treeSampleBox = document.getElementById('tree-element-sample-box')
-      treeSampleBox.setAttribute('script-input',script)
-      eval(script)
-
-      const tree2 = document.getElementById('tree-element')
-      tree2.addEventListener('mouseclicktree',(ev)=>console.warn(ev))
-
-      const markdownIncludeString = `
-You will also need \`bootstrap 3.3.7\` for formatting and \`custom-elements-es5-adapter\` for es5 shim:
-
-\`\`\`
-<${''}script src = "https://unpkg.com/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"><${''}/script>
-<${''}link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
-\`\`\`
-
-To use any of the components, include \`export.js\` in the header:
-
-\`\`\`
-<${''}script src = "third_party/atlasViewer/dist/export/export.js"></${''}script>
-\`\`\`
-
-`
-      const markdownInclude = document.getElementById('markdown-include')
-      markdownInclude.setAttribute('markdown',markdownIncludeString)
-
-      const correctMarkDown = `
-\`\`\`
-const correctMarkDown = \`
-<\$\{''\}script>
-  console.log('NOT GOTCHA')
-<\$\{''\}/script>
-\`
-
-const markdownCorrect = document.getElementById('markdown-id')
-markdownCorrect.setAttribute('markdown',correctmarkDown)
-\`\`\`
-`
-      const markdownCorrect = document.getElementById('markdown-correct')
-      markdownCorrect.setAttribute('markdown',correctMarkDown)
-    })
-  </script>
-</head>
-<body>
-
-  <div class = "jumbotron">
-    <div class = "container">
-      <div class = "row">
-        <h1>interactive viewer components</h1>
-      </div>
-      <div class = "row">
-<markdown-element id = "markdown-include">
-</markdown-element>
-      </div>
-    </div>
-  </div>
-  <div id = "container" class = "container-fluid">
-    <div class = "row">
-      <div class = "col-md-3">
-
-<sample-box sample-box-title = "readmore component">
-<readmore-element>
-  Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
-</readmore-element>
-</sample-box>
-      </div>
-      <div class = "col-md-3">
-<sample-box sample-box-title = "markdown component">
-<markdown-element>
-# Heading1
-## Heading2
-### Heading3
-
-this is a paragraph
-
-- a dot point
-- a second dot point
-</markdown-element>
-</sample-box>
-<h3>
-  n.b.
-</h3>
-<p>
-<markdown-element>
-- first attempts to parse `element.getAttribute('markdown')` then `element.innerHTML`, if non-empty.
-- major gotcha: if markdown is rendered as a part of innerHTML, the dom will be rendered briefly. As a result, triple ticked script tags **will** will be evaluated. For example, the following script, if enclosed between `markdown-element` tags, will still be evaluated:
-
-```
-<script>
-console.log('GOTCHA')
-</script>
-```
-
-- To include script tags, pass it as an attribute instead:
-
-</markdown-element>
-<markdown-element id = "markdown-correct">
-</markdown-element>
-<markdown-element>
-- The `${''}` is required to breakup the `&lt;/script&gt;` tag, or the script tag will be terminated prematurely.
-</markdown-element>
-</p>
-      </div>
-      <div class = "col-md-3">
-<sample-box sample-box-title = "panel component">
-<panel-element collapse-body = "false" body-collapsable = "true" show-body = "true" show-footer = "true" iv-parse-attribute>
-  <div style = "padding: 0.5em;" heading>
-    panel heading
-  </div>
-  <div style = "padding: 0.5em;" body>
-    Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
-  </div>
-  <div style = "opacity:0.7;padding: 0.5em;" footer>
-    panel footer
-  </div>
-</panel-element>
-</sample-box>
-      </div>
-      <div class = "col-md-3">
-<sample-box sample-box-title = "tree component" id = "tree-element-sample-box">
-<tree-element children-expanded = "true" id = 'tree-element' treebase>
-
-</tree-element>
-</sample-box>
-      </div>
-    </div>
-  </div>
-</body>
-<footer>
-</footer>
-</html>
\ No newline at end of file
diff --git a/src/atlasViewerExports/export.module.ts b/src/atlasViewerExports/export.module.ts
deleted file mode 100644
index 1ebe2eabff0294e2b77e9aa6b5adfb9ca45122cc..0000000000000000000000000000000000000000
--- a/src/atlasViewerExports/export.module.ts
+++ /dev/null
@@ -1,48 +0,0 @@
-import { Injector, NgModule } from "@angular/core";
-import { createCustomElement } from '@angular/elements'
-import { FormsModule } from "@angular/forms";
-import { BrowserModule } from "@angular/platform-browser";
-import { BrowserAnimationsModule } from '@angular/platform-browser/animations'
-import { ComponentsModule } from "../components/components.module";
-import { MarkdownDom } from '../components/markdown/markdown.component'
-import { ParseAttributeDirective } from "../components/parseAttribute.directive";
-import { TreeComponent } from "../components/tree/tree.component";
-import { SampleBoxUnit } from "./sampleBox/sampleBox.component";
-
-@NgModule({
-  imports : [
-    BrowserModule,
-    BrowserAnimationsModule,
-    FormsModule,
-    ComponentsModule,
-  ],
-  declarations : [
-    SampleBoxUnit,
-
-    /* parse element attributes from string to respective datatypes */
-    ParseAttributeDirective,
-  ],
-  entryComponents : [
-    SampleBoxUnit,
-
-    MarkdownDom,
-    TreeComponent,
-  ],
-})
-
-export class ExportModule {
-  constructor(public injector: Injector) {
-    const sampleBox = createCustomElement(SampleBoxUnit, {injector: this.injector})
-    customElements.define('sample-box', sampleBox)
-
-    const markDown = createCustomElement(MarkdownDom, {injector : this.injector })
-    customElements.define('markdown-element', markDown)
-
-    const tree = createCustomElement(TreeComponent, {injector : this.injector })
-    customElements.define('tree-element', tree)
-
-  }
-
-  /* eslint-disable-next-line @typescript-eslint/no-empty-function */
-  public ngDoBootstrap() {}
-}
diff --git a/src/atlasViewerExports/main.export.aot.ts b/src/atlasViewerExports/main.export.aot.ts
deleted file mode 100644
index c27b91b4ea8d2d86ff7deb6c192f58d7c235ee02..0000000000000000000000000000000000000000
--- a/src/atlasViewerExports/main.export.aot.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import 'zone.js'
-
-import { platformBrowserDynamic } from "@angular/platform-browser-dynamic";
-import { ExportModule } from "./export.module";
-
-platformBrowserDynamic().bootstrapModule(ExportModule)
diff --git a/src/atlasViewerExports/main.export.ts b/src/atlasViewerExports/main.export.ts
deleted file mode 100644
index 2bdb1c047520c3ffca666cab10a4f5fba952c510..0000000000000000000000000000000000000000
--- a/src/atlasViewerExports/main.export.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import { platformBrowserDynamic } from "@angular/platform-browser-dynamic";
-import 'reflect-metadata'
-import 'zone.js'
-import { ExportModule } from "./export.module";
-
-platformBrowserDynamic().bootstrapModule(ExportModule)
diff --git a/src/atlasViewerExports/sampleBox/sampleBox.component.ts b/src/atlasViewerExports/sampleBox/sampleBox.component.ts
deleted file mode 100644
index 3a819430405bf38c6e1951d2910103dec3b5fb4b..0000000000000000000000000000000000000000
--- a/src/atlasViewerExports/sampleBox/sampleBox.component.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-import {
-  Component,
-  ElementRef,
-  Input,
-  OnChanges,
-  OnInit,
-  Renderer2,
-  ViewChild,
-} from '@angular/core'
-
-@Component({
-  selector : 'sample-box',
-  templateUrl : './sampleBox.template.html',
-  styleUrls : [
-    './sampleBox.style.css',
-  ],
-})
-
-export class SampleBoxUnit implements OnInit, OnChanges {
-  @Input() public sampleBoxTitle = ``
-  @Input() public scriptInput
-
-  @ViewChild('ngContent', {read: ElementRef}) public ngContent: ElementRef
-
-  public escapedHtml: string = ``
-  public escapedScript: string = ``
-
-  private scriptEl: HTMLScriptElement
-
-  constructor(private rd2: Renderer2) {
-    this.scriptEl = this.rd2.createElement('script')
-  }
-
-  public ngOnInit() {
-    this.escapedHtml = this.ngContent.nativeElement.innerHTML
-  }
-
-  public ngOnChanges() {
-    this.escapedScript = this.scriptInput
-    if ( this.scriptInput ) {
-      this.scriptEl.innerText = this.scriptInput
-    }
-  }
-}
diff --git a/src/atlasViewerExports/sampleBox/sampleBox.template.html b/src/atlasViewerExports/sampleBox/sampleBox.template.html
deleted file mode 100644
index b94af69fe74182a7fc5abbbdf9670fd2d3b08237..0000000000000000000000000000000000000000
--- a/src/atlasViewerExports/sampleBox/sampleBox.template.html
+++ /dev/null
@@ -1,19 +0,0 @@
-<h2>
-  {{ sampleBoxTitle }}
-</h2>
-<hr />
-<h3>
-  Result:
-</h3>
-<div class = "well" #ngContent>
-  <ng-content>
-  </ng-content>
-</div>
-<h3>
-  HTML:
-</h3>
-<pre>{{ escapedHtml }}</pre>
-<h3 *ngIf = "scriptInput">
-  Script:
-</h3>
-<pre *ngIf = "scriptInput">{{ escapedScript }}</pre>
diff --git a/src/auth/auth.service.spec.ts b/src/auth/auth.service.spec.ts
index 504e7299ca852190d15e4218da246f132bbc6315..b23dc32cea6ee06d5b247c1893873443faf04ff6 100644
--- a/src/auth/auth.service.spec.ts
+++ b/src/auth/auth.service.spec.ts
@@ -1,7 +1,7 @@
 import { HttpClientTestingModule, HttpTestingController } from "@angular/common/http/testing"
+import { APP_INITIALIZER } from "@angular/core"
 import { TestBed } from "@angular/core/testing"
 import { hot } from "jasmine-marbles"
-import { PureContantService } from "src/util"
 import { AuthService } from "./auth.service"
 
 describe('>auth.service.ts', () => {
@@ -14,10 +14,13 @@ describe('>auth.service.ts', () => {
         providers: [
           AuthService,
           {
-            provide: PureContantService,
-            useValue: {
-              backendUrl: `http://localhost:3000/`
-            }
+            provide: APP_INITIALIZER,
+            useFactory: (authSvc: AuthService) => {
+              authSvc.authReloadState()
+              return () => Promise.resolve()
+            },
+            multi: true,
+            deps: [ AuthService ]
           }
         ]
       })
@@ -38,7 +41,7 @@ describe('>auth.service.ts', () => {
 
       it('> if http response errors, user$ should be stream of null', () => {
 
-        const resp = ctrl.expectOne('http://localhost:3000/user')
+        const resp = ctrl.expectOne('user')
         resp.error(null, {
           status: 404,
         })
@@ -53,7 +56,7 @@ describe('>auth.service.ts', () => {
 
       it('> if http response contains truthy error key, user should return stream of null', () => {
 
-        const resp = ctrl.expectOne('http://localhost:3000/user')
+        const resp = ctrl.expectOne('user')
         resp.flush({
           error: true
         })
@@ -72,7 +75,7 @@ describe('>auth.service.ts', () => {
           name: 'foobar',
           id: 'baz'
         }
-        const resp = ctrl.expectOne('http://localhost:3000/user')
+        const resp = ctrl.expectOne('user')
         resp.flush(mockUser)
 
         expect(
diff --git a/src/auth/auth.service.ts b/src/auth/auth.service.ts
index bb610daceac50e7a2ae11ef8fe0919f025354fa3..e2067301c80904efaafbc25527a5e58773ce9558 100644
--- a/src/auth/auth.service.ts
+++ b/src/auth/auth.service.ts
@@ -2,8 +2,7 @@ import { HttpClient } from "@angular/common/http";
 import { Injectable, OnDestroy } from "@angular/core";
 import { Observable, of, Subscription } from "rxjs";
 import { catchError, map, shareReplay } from "rxjs/operators";
-import { PureContantService } from "src/util";
-import { BACKENDURL } from "src/util/constants";
+import { environment } from "src/environments/environment"
 
 const IV_REDIRECT_TOKEN = `IV_REDIRECT_TOKEN`
 
@@ -38,9 +37,8 @@ export class AuthService implements OnDestroy {
 
   constructor(
     private httpClient: HttpClient,
-    private constantSvc: PureContantService,
   ) {
-    this.user$ = this.httpClient.get<TUserRouteResp>(`${this.constantSvc.backendUrl}user`).pipe(
+    this.user$ = this.httpClient.get<TUserRouteResp>(`${environment.BACKEND_URL || ''}user`).pipe(
       map(json => {
         if (json.error) {
           throw new Error(json.message || 'User not loggedin.')
diff --git a/src/components/components.module.ts b/src/components/components.module.ts
index c8d9ff210b5530a6e001dcd983a3fb8487c560cf..1a0150f4b785f88253f307a123e60cfe5f1cb428 100644
--- a/src/components/components.module.ts
+++ b/src/components/components.module.ts
@@ -3,27 +3,17 @@ import { NgModule } from '@angular/core'
 import { FormsModule } from '@angular/forms'
 import { BrowserAnimationsModule } from '@angular/platform-browser/animations'
 
-import {  MarkdownDom } from './markdown/markdown.component';
+import { MarkdownModule } from './markdown';
 
 import { CommonModule } from '@angular/common';
 import { AngularMaterialModule } from 'src/sharedModules';
 import { UtilModule } from 'src/util';
 import { SafeHtmlPipe } from './safeHtml.pipe'
-import { TreeSearchPipe } from './treeSearch.pipe';
 import { ConfirmDialogComponent } from './confirmDialog/confirmDialog.component';
 import { DialogComponent } from './dialog/dialog.component';
-import { AppendSiblingFlagPipe } from './flatTree/appendSiblingFlag.pipe';
-import { ClusteringPipe } from './flatTree/clustering.pipe';
-import { FilterCollapsePipe } from './flatTree/filterCollapse.pipe';
-import { FlattenTreePipe } from './flatTree/flattener.pipe';
-import { FlatTreeComponent } from './flatTree/flatTree.component';
-import { HighlightPipe } from './flatTree/highlight.pipe';
-import { RenderPipe } from './flatTree/render.pipe';
-import { TreeComponent } from './tree/tree.component';
-import { TreeBaseDirective } from './tree/treeBase.directive';
 import { IAVVerticalButton } from './vButton/vButton.component';
 import { DynamicMaterialBtn } from './dynamicMaterialBtn/dynamicMaterialBtn.component';
-import { SpinnerCmp } from './spinner/spinner.component';
+import { SpinnerModule } from "./spinner"
 import { ReadmoreModule } from './readmore';
 import { TileCmp } from './tile/tile.component';
 
@@ -36,48 +26,32 @@ import { TileCmp } from './tile/tile.component';
     AngularMaterialModule,
     UtilModule,
     ReadmoreModule,
+    SpinnerModule,
+    MarkdownModule,
   ],
   declarations : [
     /* components */
-    MarkdownDom,
-    TreeComponent,
-    FlatTreeComponent,
     DialogComponent,
     ConfirmDialogComponent,
     IAVVerticalButton,
     DynamicMaterialBtn,
-    SpinnerCmp,
     TileCmp,
 
-    /* directive */
-    TreeBaseDirective,
-
     /* pipes */
     SafeHtmlPipe,
-    TreeSearchPipe,
-    FlattenTreePipe,
-    RenderPipe,
-    HighlightPipe,
-    AppendSiblingFlagPipe,
-    ClusteringPipe,
-    FilterCollapsePipe,
   ],
   exports : [
     BrowserAnimationsModule,
     ReadmoreModule,
-
-    MarkdownDom,
-    TreeComponent,
-    FlatTreeComponent,
+    SpinnerModule,
+    MarkdownModule,
+    
     DialogComponent,
     ConfirmDialogComponent,
     IAVVerticalButton,
     DynamicMaterialBtn,
-    SpinnerCmp,
     TileCmp,
 
-    TreeSearchPipe,
-    TreeBaseDirective,
   ],
 })
 
diff --git a/src/components/dynamicMaterialBtn/dynamicMaterialBtn.stories.ts b/src/components/dynamicMaterialBtn/dynamicMaterialBtn.stories.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3c44ad2d5ebf4dfc28212b83c650a4b3283acaba
--- /dev/null
+++ b/src/components/dynamicMaterialBtn/dynamicMaterialBtn.stories.ts
@@ -0,0 +1,50 @@
+import { CommonModule } from "@angular/common"
+import { Meta, moduleMetadata, Story } from "@storybook/angular"
+import { AngularMaterialModule } from "src/sharedModules"
+import {
+  DynamicMaterialBtn
+} from "./dynamicMaterialBtn.component"
+
+export default {
+  component: DynamicMaterialBtn,
+  decorators: [
+    moduleMetadata({
+      imports: [
+        CommonModule,
+        AngularMaterialModule,
+      ],
+    })
+  ],
+} as Meta
+
+const Template: Story<DynamicMaterialBtn> = (args: DynamicMaterialBtn) => ({
+  template: `<iav-dynamic-mat-button>Dynamic Button</iav-dynamic-mat-button>`,
+  props: args
+})
+
+export const Default = Template.bind({})
+Default.args = {
+  matBtnDisabled: false
+}
+
+export const RaiseButton = Template.bind({})
+RaiseButton.args = {
+  ...Default.args,
+  matBtnStyle: "mat-raised-button"
+}
+
+export const RaisePrimaryButton = Template.bind({})
+RaisePrimaryButton.args = {
+  ...Default.args,
+  matBtnStyle: "mat-raised-button",
+  matBtnColor: "primary",
+}
+
+
+export const RaisePrimaryDisabledButton = Template.bind({})
+RaisePrimaryDisabledButton.args = {
+  ...Default.args,
+  matBtnStyle: "mat-raised-button",
+  matBtnColor: "primary",
+  matBtnDisabled: true,
+}
diff --git a/src/components/flatHierarchy/const.ts b/src/components/flatHierarchy/const.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b823fab8b090db0122332b2e120b53ab48e18b77
--- /dev/null
+++ b/src/components/flatHierarchy/const.ts
@@ -0,0 +1,5 @@
+export type TreeNode<T> = {
+  node: T
+  level: number
+  expandable: boolean
+}
\ No newline at end of file
diff --git a/src/components/flatHierarchy/index.ts b/src/components/flatHierarchy/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b276f3038f3f8c46e23bcb6eb59bc12798c1ecc4
--- /dev/null
+++ b/src/components/flatHierarchy/index.ts
@@ -0,0 +1 @@
+export { SxplrFlatHierarchyModule } from "./module"
\ No newline at end of file
diff --git a/src/components/flatHierarchy/module.ts b/src/components/flatHierarchy/module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..298b0f6675aca984f44cac8f1470d3a63e282d21
--- /dev/null
+++ b/src/components/flatHierarchy/module.ts
@@ -0,0 +1,25 @@
+import { ScrollingModule } from "@angular/cdk/scrolling";
+import { CdkTreeModule } from "@angular/cdk/tree";
+import { CommonModule } from "@angular/common";
+import { NgModule } from "@angular/core";
+import { AngularMaterialModule } from "src/sharedModules";
+import { FlatHierarchySpacer } from "./spacer.pipe";
+import { SxplrFlatHierarchyTreeView } from "./treeView/treeView.component";
+
+@NgModule({
+  imports: [
+    CommonModule,
+    CdkTreeModule,
+    ScrollingModule,
+    AngularMaterialModule,
+  ],
+  declarations: [
+    SxplrFlatHierarchyTreeView,
+    FlatHierarchySpacer,
+  ],
+  exports: [
+    SxplrFlatHierarchyTreeView
+  ]
+})
+
+export class SxplrFlatHierarchyModule{}
\ No newline at end of file
diff --git a/src/components/flatHierarchy/spacer.pipe.ts b/src/components/flatHierarchy/spacer.pipe.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a7533b68c759ed72c1a56b87a762409a4d8a5f2b
--- /dev/null
+++ b/src/components/flatHierarchy/spacer.pipe.ts
@@ -0,0 +1,13 @@
+import { Pipe, PipeTransform } from "@angular/core";
+import { TreeNode } from "./const"
+
+@Pipe({
+  name: 'flatHierarchySpacer',
+  pure: true
+})
+
+export class FlatHierarchySpacer implements PipeTransform{
+  public transform<T>(inputNode: TreeNode<T>, ...args: any[]) {
+    return Array(inputNode.level).fill(null)
+  }
+}
diff --git a/src/components/flatHierarchy/treeView.stories.ts b/src/components/flatHierarchy/treeView.stories.ts
new file mode 100644
index 0000000000000000000000000000000000000000..68eb30565c76f7989c37e3774c649e0e4608a071
--- /dev/null
+++ b/src/components/flatHierarchy/treeView.stories.ts
@@ -0,0 +1,116 @@
+import { CommonModule } from "@angular/common"
+import { HttpClientModule } from "@angular/common/http"
+import { Component, EventEmitter } from "@angular/core"
+import { action } from "@storybook/addon-actions"
+import { Meta, moduleMetadata, Story } from "@storybook/angular"
+import { SapiRegionModel } from "src/atlasComponents/sapi"
+import { atlasId, provideDarkTheme, getParcRegions, parcId, spaceId } from "src/atlasComponents/sapi/stories.base"
+import { AngularMaterialModule } from "src/sharedModules"
+import { SxplrFlatHierarchyModule } from "./module"
+
+
+@Component({
+  selector: `hierarchy-flatview-wrapper`,
+  template: `
+  <ng-template #tmplRef let-region>
+    <div class="mat-body sxplr-d-flex sxplr-align-items-center sxplr-h-100 region-tmpl">
+      {{ region.name }}
+    </div>
+  </ng-template>
+  <sxplr-flat-hierarchy-tree-view
+    [sxplr-flat-hierarchy-tree-view-show-control]="showControl"
+    [sxplr-flat-hierarchy-nodes]="regions"
+    [sxplr-flat-hierarchy-is-parent]="isParent"
+    [sxplr-flat-hierarchy-tree-view-node-label-toggles]="nodeLabelToggles"
+    [sxplr-flat-hierarchy-tree-view-lineheight]="lineHeight"
+    [sxplr-flat-hierarchy-render-node-tmpl]="tmplRef"
+    (sxplr-flat-hierarchy-tree-view-node-clicked)="nodeClicked.emit($event)">
+  </sxplr-flat-hierarchy-tree-view>
+  
+  `,
+  styles: [
+    `
+    .region-tmpl
+    {
+      text-overflow: clip;
+      white-space: nowrap;
+    }
+    `
+  ]
+})
+
+class HierarchyFlatViewWrapper{
+  regions: SapiRegionModel[] = []
+  showControl = false
+  nodeLabelToggles = true
+  lineHeight: number = 35
+  nodeClicked = new EventEmitter<SapiRegionModel>()
+  isParent(region: SapiRegionModel, parentRegion: SapiRegionModel) {
+    return region.hasParent?.some(parent => parent['@id'] === parentRegion["@id"])
+  }
+}
+
+export default {
+  component: HierarchyFlatViewWrapper,
+  decorators: [
+    moduleMetadata({
+      imports: [
+        CommonModule,
+        HttpClientModule,
+        SxplrFlatHierarchyModule,
+        AngularMaterialModule,
+      ],
+      providers: [
+        ...provideDarkTheme,
+      ],
+      declarations: []
+    })
+  ],
+} as Meta
+
+const Template: Story<HierarchyFlatViewWrapper> = (args: HierarchyFlatViewWrapper, { loaded, parameters }) => {
+  const { 
+    regions
+  } = loaded
+  
+  const {
+    
+  } = parameters
+
+  const {
+    showControl,
+    nodeLabelToggles,
+    lineHeight,
+  } = args
+  return ({
+    props: {
+      regions,
+      showControl: !!showControl,
+      nodeLabelToggles: !!nodeLabelToggles,
+      lineHeight: lineHeight || 35,
+      nodeClicked: {
+        emit: action("nodeClicked")
+      }
+    },
+  })
+}
+Template.loaders = []
+
+const asyncLoader = async (atlasId: string, parcId: string, spaceId: string) => {
+  const regions = await getParcRegions(atlasId, parcId, spaceId)
+  return {
+    regions
+  }
+}
+
+export const Default = Template.bind({})
+Default.loaders = [
+  async () => {
+    const {
+      regions
+    } = await asyncLoader(atlasId.human, parcId.human.jba29, spaceId.human.mni152)
+    return {
+      regions
+    }
+  }
+]
diff --git a/src/components/flatHierarchy/treeView/treeControl.ts b/src/components/flatHierarchy/treeView/treeControl.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7471dfbb839cb36c23413d026c2f9c8f607af6dd
--- /dev/null
+++ b/src/components/flatHierarchy/treeView/treeControl.ts
@@ -0,0 +1,100 @@
+
+type IsParent<T> = (child: T, parent: T) => boolean
+
+export class Tree<T extends Record<string, unknown>>{
+
+  private _nodes: T[] = []
+  protected set nodes(nodes: T[]) {
+    if (nodes === this._nodes) return
+    this._nodes = nodes
+    this.resetWeakMaps()
+  }
+  protected get nodes() {
+    return this._nodes
+  }
+
+  private _isParent: IsParent<T> = (c, p) => false
+  protected set isParent(fn: IsParent<T>){
+    if (fn === this._isParent) return
+    this._isParent = fn
+    this.resetWeakMaps()
+  }
+  protected get isParent(){
+    return this._isParent
+  }
+  public someAncestor(node: T, predicate: (anc: T) => boolean) {
+    const parent = this.getParent(node)
+    if (!parent) {
+      return false
+    }
+    if (predicate(parent)) {
+      return true
+    }
+    return this.someAncestor(parent, predicate)
+  }
+  public getParent(node: T): T {
+    if (!this.parentWeakMap.has(node)) {
+      for (const parentNode of this.nodes) {
+        if (this.isParent(node, parentNode)) {
+          this.parentWeakMap.set(node, parentNode)
+          break
+        }
+      }
+    }
+    return this.parentWeakMap.get(node)
+  }
+  public getChildren(node: T): T[] {
+    if (!this.childrenWeakMap.has(node)) {
+      const children = this.nodes.filter(childNode => this.isParent(childNode, node))
+      this.childrenWeakMap.set(node, children)
+      for (const c of children) {
+        this.parentWeakMap.set(c, node)
+      }
+    }
+    return this.childrenWeakMap.get(node)
+  }
+  public getLevel(node: T): number {
+    let level = -1, cursor = node
+    const maxLevel = 32
+    do {
+      if (level >= maxLevel) {
+        level = 0
+        break
+      }
+      level++
+      cursor = this.getParent(cursor)
+    } while (!!cursor)
+    return level
+  }
+  protected childrenWeakMap = new WeakMap<T, T[]>()
+  protected parentWeakMap = new WeakMap<T, T>()
+  protected nodeAncestorIsLast = new WeakMap<T, boolean[]>()
+
+  get rootNodes() {
+    const root = this.nodes.filter(node => !this.getParent(node))
+    for (const el of root) {
+      this.parentWeakMap.set(el, null)
+      this.nodeAncestorIsLast.set(el, [])
+    }
+    return root
+  }
+
+  constructor(
+    _nodes: T[] = [],
+    _isParent: IsParent<T> = (c, p) => false
+  ){
+    this._nodes = _nodes
+    this._isParent = _isParent
+  }
+
+  private resetWeakMaps() {
+
+    /**
+     * on new nodes passed, reset all maps
+     * otherwise, tree will show stale data and will not update
+     */
+    this.childrenWeakMap = new WeakMap()
+    this.parentWeakMap = new WeakMap()
+    this.nodeAncestorIsLast = new WeakMap()
+  }
+}
diff --git a/src/components/flatHierarchy/treeView/treeView.component.ts b/src/components/flatHierarchy/treeView/treeView.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f67f7cdc8a157e42c763d63021cf052f594a3c1e
--- /dev/null
+++ b/src/components/flatHierarchy/treeView/treeView.component.ts
@@ -0,0 +1,131 @@
+import { FlatTreeControl } from "@angular/cdk/tree";
+import { MatTreeFlatDataSource, MatTreeFlattener } from "@angular/material/tree"
+import { ChangeDetectionStrategy, Component, EventEmitter, HostBinding, Input, OnChanges, Output, SimpleChanges, TemplateRef } from "@angular/core";
+import { TreeNode } from "../const"
+import { Tree } from "./treeControl"
+
+@Component({
+  selector: `sxplr-flat-hierarchy-tree-view`,
+  templateUrl: `./treeView.template.html`,
+  styleUrls: [
+    `./treeView.style.css`
+  ],
+  changeDetection: ChangeDetectionStrategy.OnPush,
+  exportAs: 'flatHierarchyTreeView'
+})
+
+export class SxplrFlatHierarchyTreeView<T extends Record<string, unknown>> extends Tree<T> implements OnChanges{
+  @HostBinding('class')
+  class = 'sxplr-custom-cmp'
+
+  @Input('sxplr-flat-hierarchy-nodes')
+  sxplrNodes: T[] = []
+
+  @Input('sxplr-flat-hierarchy-is-parent')
+  sxplrIsParent: (child: T, parent: T) => boolean
+
+  @Input('sxplr-flat-hierarchy-render-node-tmpl')
+  renderNodeTmplRef: TemplateRef<T>
+
+  @Input('sxplr-flat-hierarchy-tree-view-lineheight')
+  lineHeight: number = 35
+
+  @Input('sxplr-flat-hierarchy-tree-view-show-control')
+  showControl: boolean = false
+
+  @Input('sxplr-flat-hierarchy-tree-view-node-label-toggles')
+  nodeLabelToggles: boolean = true
+
+  @Input('sxplr-flat-hierarchy-tree-view-expand-on-init')
+  expandOnInit: boolean = true
+
+  @Output('sxplr-flat-hierarchy-tree-view-node-clicked')
+  nodeClicked = new EventEmitter<T>()
+
+  ngOnChanges(changes: SimpleChanges): void {
+    if (changes.sxplrNodes || changes.sxplrIsParent) {
+      this.nodes = this.sxplrNodes
+      this.isParent = this.sxplrIsParent
+      this.dataSource.data = this.rootNodes
+      if (this.expandOnInit) {
+        this.treeControl.expandAll()
+      }
+    }
+  }
+
+  private getNodeAncestorIsLast(node: T, visited = new WeakSet<T>() ): boolean[] {
+    if (!this.nodeAncestorIsLast.has(node)) {
+      if (visited.has(node)) {
+        console.error(`getNodeAncestorIsLast visited node twice:`, node)
+        throw new Error(`getNodeAncestorIsLast visited node twice. Check console for more details.`)
+      }
+      visited.add(node)
+
+      const parent = this.getParent(node)
+      const children = this.getChildren(parent)
+      if (!children.includes(node)) {
+        console.error(`getNodeAncestorIsLast node is not included in the parent's children!`, node, parent, children)
+        throw new Error(`getNodeAncestorIsLast node is not included in the parent's children! Check console for more details.`)
+      }
+      const isLast = children.indexOf(node) === (children.length - 1)
+
+      const ancestors = this.getNodeAncestorIsLast(parent, visited)
+      this.nodeAncestorIsLast.set(node, [ ...ancestors, isLast ])
+    }
+    return this.nodeAncestorIsLast.get(node)
+  }
+
+  public treeControl = new FlatTreeControl<TreeNode<T>>(
+    ({ level }) => level,
+    ({ expandable }) => expandable,
+  )
+  
+  private treeFlattener = new MatTreeFlattener<T, TreeNode<T>>(
+    (node: T, level: number) => {
+      return {
+        node,
+        level,
+        expandable: this.getChildren(node).length > 0,
+      }
+    },
+    ({ level }) => level,
+    ({ expandable }) => expandable,
+    node => this.getChildren(node)
+  )
+
+  public dataSource = new MatTreeFlatDataSource<T, TreeNode<T>>(
+    this.treeControl,
+    this.treeFlattener
+  )
+
+  hasChild(_: number, node: TreeNode<T>) {
+    return node.expandable
+  }
+
+  getSpacerClass(node: TreeNode<T>, spacerIdx: number){
+    const arrBool = this.getNodeAncestorIsLast(node.node).slice(0, -1)
+    if (arrBool[spacerIdx]) {
+      return `lvl-hide-left-border`
+    }
+    return ``
+  }
+
+  handleClickNode(node: TreeNode<T>){
+    if (this.nodeLabelToggles) {
+      this.treeControl.toggle(node)
+    }
+    this.nodeClicked.emit(node.node)
+  }
+
+  expandAll(){
+    this.treeControl.expandAll()
+  }
+
+  collaseAll(){
+    this.treeControl.collapseAll()
+  }
+
+  constructor(){
+    super()
+  }
+}
diff --git a/src/components/flatHierarchy/treeView/treeView.style.css b/src/components/flatHierarchy/treeView/treeView.style.css
new file mode 100644
index 0000000000000000000000000000000000000000..3ab293efba0d79a7b5bbe555693ed97b7a94a009
--- /dev/null
+++ b/src/components/flatHierarchy/treeView/treeView.style.css
@@ -0,0 +1,48 @@
+cdk-virtual-scroll-viewport
+{
+  height: 100%;
+  min-height: 24rem;
+  overflow-x: hidden;
+}
+
+.virtual-scroll-container
+{
+  display: flex;
+  align-items: center;
+}
+
+.ph-container
+{
+  transform: translate(20px, -50%);
+  flex: auto 0 0;
+  height: 100%;
+}
+
+.ph-container .ph
+{
+  height: 100%;
+  width: 2rem;
+  display: inline-block;
+}
+
+.ph-container .lvl:not(.lvl-hide-left-border)
+{
+  border-left: 1px dashed currentColor;
+}
+
+.ph-container .lvl:last-child
+{
+  border-bottom: 1px dashed currentColor;
+}
+
+
+cdk-tree-node .node-render-tmpl
+{
+  flex: 0 1 1;
+  display: inline-block;
+}
+
+.label-toggles:hover
+{
+  cursor: default;
+}
diff --git a/src/components/flatHierarchy/treeView/treeView.template.html b/src/components/flatHierarchy/treeView/treeView.template.html
new file mode 100644
index 0000000000000000000000000000000000000000..c35e36cf8cd85e639d99b8ca6bbfd8dd5d466b65
--- /dev/null
+++ b/src/components/flatHierarchy/treeView/treeView.template.html
@@ -0,0 +1,70 @@
+<ng-template [ngIf]="showControl">
+  <button
+    mat-button
+    (click)="treeControl.collapseAll()">
+    Collapse All
+  </button>
+
+  <button
+    mat-button
+    (click)="treeControl.expandAll()">
+    Expand All
+  </button>
+</ng-template>
+
+
+<cdk-virtual-scroll-viewport [itemSize]="lineHeight">
+
+  <div class="virtual-scroll-container"
+    *cdkVirtualFor="let node of dataSource"
+    [style.height.px]="lineHeight">
+
+    <!-- directory ruling template -->
+    <ng-template
+      [ngTemplateOutlet]="phTmpl"
+      [ngTemplateOutletContext]="{
+        $implicit: node
+      }">
+    </ng-template>
+
+    <!-- expandable button -->
+    <ng-template [ngIf]="node.expandable" [ngIfElse]="btnFallBackTmpl">
+      <button mat-icon-button
+        [ngClass]="{
+          'r-270': !treeControl.isExpanded(node)
+        }"
+        (click)="treeControl.toggle(node)">
+        <i class="fas fa-chevron-down"></i>
+      </button>
+    </ng-template>
+
+    <!-- non expandable button -->
+    <ng-template #btnFallBackTmpl>
+      <button mat-icon-button disabled></button>
+    </ng-template>
+
+
+    <!-- template to render the node -->
+    <div class="node-render-tmpl"
+      [ngClass]="nodeLabelToggles ? 'label-toggles' : ''"
+      (click)="handleClickNode(node)">
+      <ng-template
+        [ngTemplateOutlet]="renderNodeTmplRef"
+        [ngTemplateOutletContext]="{
+          $implicit: node.node
+        }">
+      </ng-template>
+    </div>
+
+  </div>
+
+</cdk-virtual-scroll-viewport>
+
+<ng-template #phTmpl let-node>
+  <div class="ph-container sxplr-pe-none">
+    <div class="ph lvl"
+      [ngClass]="getSpacerClass(node, index)"
+      *ngFor="let spacer of (node | flatHierarchySpacer); index as index">
+    </div>
+  </div>
+</ng-template>
diff --git a/src/components/flatTree/appendSiblingFlag.pipe.ts b/src/components/flatTree/appendSiblingFlag.pipe.ts
deleted file mode 100644
index c1a43fbd0411d714f6a1109f2d7379402d439879..0000000000000000000000000000000000000000
--- a/src/components/flatTree/appendSiblingFlag.pipe.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-import { Pipe, PipeTransform } from "@angular/core";
-
-@Pipe({
-  name : 'appendSiblingFlagPipe',
-})
-
-export class AppendSiblingFlagPipe implements PipeTransform {
-  public transform(objs: any[]): any[] {
-    return objs
-      .reduceRight((acc, curr) =>  ({
-        acc : acc.acc.concat(Object.assign({}, curr, {
-          siblingFlags : curr.lvlId.split('_').map((v, idx) => typeof acc.flags[idx] !== 'undefined'
-            ? acc.flags[idx]
-            : false)
-            .slice(1)
-            .map(v => !v),
-        })),
-        flags: curr.lvlId.split('_').map((_, idx) => acc.flags[idx] ).slice(0, -1).concat(true),
-      }), { acc: [], flags : Array(256).fill(false) })
-      .acc.reverse()
-  }
-}
diff --git a/src/components/flatTree/clustering.pipe.ts b/src/components/flatTree/clustering.pipe.ts
deleted file mode 100644
index 2d0d11294b46564d4737087abf5f620b20b56b58..0000000000000000000000000000000000000000
--- a/src/components/flatTree/clustering.pipe.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { Pipe, PipeTransform } from "@angular/core";
-
-// TODO deprecate?
-
-@Pipe({
-  name : 'clusteringPipe',
-})
-
-export class ClusteringPipe implements PipeTransform {
-  public transform(array: any[], num: number = 100): any[][] {
-    return array.reduce((acc, curr, idx, arr) => idx % num === 0
-      ? acc.concat([arr.slice(idx, idx + num)])
-      : acc , [])
-  }
-}
diff --git a/src/components/flatTree/filterCollapse.pipe.ts b/src/components/flatTree/filterCollapse.pipe.ts
deleted file mode 100644
index e936d7c7b486a70d1db83c9fbb4fa9b5125c7f98..0000000000000000000000000000000000000000
--- a/src/components/flatTree/filterCollapse.pipe.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-import { Pipe, PipeTransform } from "@angular/core";
-
-@Pipe({
-  name: 'filterCollapsePipe',
-})
-
-export class FilterCollapsePipe implements PipeTransform {
-  public transform(array: any[], collapsedLevels: Set<string>, uncollapsedLevels: Set<string>, defaultCollapse: boolean ) {
-    const isCollapsedById = (id) => {
-
-      return collapsedLevels.has(id)
-        ? true
-        : uncollapsedLevels.has(id)
-          ? false
-          : !defaultCollapse
-    }
-    const returnArray =  array.filter(item => {
-      return !item.lvlId.split('_')
-        .filter((v, idx, arr) => idx < arr.length - 1 )
-        .reduce((acc, curr) => acc
-          .concat(acc.length === 0
-            ? curr
-            : acc[acc.length - 1].concat(`_${curr}`)), [])
-        .some(id => isCollapsedById(id))
-    })
-    return returnArray
-  }
-}
diff --git a/src/components/flatTree/filterRowsByVisibility.pipe.ts b/src/components/flatTree/filterRowsByVisibility.pipe.ts
deleted file mode 100644
index f589fbf8b9a9e05bcfc0a1fce15fee53f2af32d2..0000000000000000000000000000000000000000
--- a/src/components/flatTree/filterRowsByVisibility.pipe.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import { Pipe, PipeTransform } from "@angular/core";
-
-// TODO fix typo
-
-@Pipe({
-  name : 'filterRowsByVisbilityPipe',
-})
-
-export class FilterRowsByVisbilityPipe implements PipeTransform {
-  public transform(rows: any[], getChildren: (item: any) => any[], filterFn: (item: any) => boolean) {
-
-    return rows.filter(row => this.recursive(row, getChildren, filterFn) )
-  }
-
-  private recursive(single: any, getChildren: (item: any) => any[], filterFn: (item: any) => boolean): boolean {
-    return filterFn(single) || (getChildren && getChildren(single).some(c => this.recursive(c, getChildren, filterFn)))
-  }
-}
diff --git a/src/components/flatTree/flatTree.component.ts b/src/components/flatTree/flatTree.component.ts
deleted file mode 100644
index d8bd95fb0d5d780595caa03c1695e2763033ee8e..0000000000000000000000000000000000000000
--- a/src/components/flatTree/flatTree.component.ts
+++ /dev/null
@@ -1,107 +0,0 @@
-import {CdkVirtualScrollViewport} from "@angular/cdk/scrolling";
-import {AfterViewChecked, ChangeDetectionStrategy, Component, EventEmitter, Input, Output, ViewChild} from "@angular/core";
-import { IFlattenedTreeInterface } from "./flattener.pipe";
-
-/**
- * TODO to be replaced by virtual scrolling when ivy is in stable
- */
-
-@Component({
-  selector : 'flat-tree-component',
-  templateUrl : './flatTree.template.html',
-  styleUrls : [
-    './flatTree.style.css',
-  ],
-  changeDetection: ChangeDetectionStrategy.OnPush,
-})
-
-export class FlatTreeComponent implements AfterViewChecked {
-  @Input() public inputItem: any = {
-    name : 'Untitled',
-    children : [],
-  }
-  @Input() public childrenExpanded: boolean = true
-
-  @Input() public useDefaultList: boolean = false
-
-  @Output() public treeNodeClick: EventEmitter<any> = new EventEmitter()
-
-  /* highly non-performant. rerenders each time on mouseover or mouseout */
-  // @Output() treeNodeEnter : EventEmitter<any> = new EventEmitter()
-  // @Output() treeNodeLeave : EventEmitter<any> = new EventEmitter()
-
-  @Input() public renderNode: (item: any) => string = (item) => item.name
-  @Input() public findChildren: (item: any) => any[] = (item) => item.children ? item.children : []
-  @Input() public searchFilter: (item: any) => boolean | null = () => true
-
-  @ViewChild('flatTreeVirtualScrollViewPort') public virtualScrollViewPort: CdkVirtualScrollViewport
-  @Output() public totalRenderedListChanged = new EventEmitter<{ previous: number, current: number }>()
-  private totalDataLength: number = null
-
-  public flattenedItems: any[] = []
-
-  public getClass(level: number) {
-    return [...Array(level + 1)].map((v, idx) => `render-node-level-${idx}`).join(' ')
-  }
-
-  public collapsedLevels: Set<string> = new Set()
-  public uncollapsedLevels: Set<string> = new Set()
-
-  public ngAfterViewChecked() {
-    /**
-     * if useDefaultList is true, virtualscrollViewPort will be undefined
-     */
-    if (!this.virtualScrollViewPort) {
-      return
-    }
-    const currentTotalDataLength = this.virtualScrollViewPort.getDataLength()
-    const previousDataLength = this.totalDataLength
-
-    this.totalRenderedListChanged.emit({
-      current: currentTotalDataLength,
-      previous: previousDataLength,
-    })
-    this.totalDataLength = currentTotalDataLength
-  }
-
-  public toggleCollapse(flattenedItem: IFlattenedTreeInterface) {
-    if (this.isCollapsed(flattenedItem)) {
-      this.collapsedLevels.delete(flattenedItem.lvlId)
-      this.uncollapsedLevels.add(flattenedItem.lvlId)
-    } else {
-      this.collapsedLevels.add(flattenedItem.lvlId)
-      this.uncollapsedLevels.delete(flattenedItem.lvlId)
-    }
-    this.collapsedLevels = new Set(this.collapsedLevels)
-    this.uncollapsedLevels = new Set(this.uncollapsedLevels)
-  }
-
-  public isCollapsed(flattenedItem: IFlattenedTreeInterface): boolean {
-    return this.isCollapsedById(flattenedItem.lvlId)
-  }
-
-  public isCollapsedById(id: string): boolean {
-    return this.collapsedLevels.has(id)
-      ? true
-      : this.uncollapsedLevels.has(id)
-        ? false
-        : !this.childrenExpanded
-  }
-
-  public collapseRow(flattenedItem: IFlattenedTreeInterface): boolean {
-    return flattenedItem.lvlId.split('_')
-      .filter((v, idx, arr) => idx < arr.length - 1 )
-      .reduce((acc, curr) => acc
-        .concat(acc.length === 0
-          ? curr
-          : acc[acc.length - 1].concat(`_${curr}`)), [])
-      .some(id => this.isCollapsedById(id))
-  }
-
-  public handleTreeNodeClick(event: MouseEvent, inputItem: any) {
-    this.treeNodeClick.emit({
-      event,
-      inputItem,
-    })
-  }
-}
diff --git a/src/components/flatTree/flatTree.style.css b/src/components/flatTree/flatTree.style.css
deleted file mode 100644
index f501aada1d1758cdc2bcc57c1d9c12889282bb64..0000000000000000000000000000000000000000
--- a/src/components/flatTree/flatTree.style.css
+++ /dev/null
@@ -1,186 +0,0 @@
-:host
-{
-  display: flex;
-  flex-direction: column;
-  height:100%;
-}
-
-cdk-virtual-scroll-viewport,
-.default-container
-{
-  flex: 1 0 auto;
-  overflow-x: hidden;
-}
-
-.render-node-text
-{
-  white-space: nowrap;
-  cursor:default;
-}
-
-.padding-block-container
-{
-  position:absolute;
-  left:0;
-  top:0;
-  height:1.5em;
-  width: 0px;
-  white-space:nowrap;
-}
-
-span[renderText]
-{
-  white-space: nowrap;
-  overflow: hidden;
-}
-
-.padding-block
-{
-  display: inline-block;
-  width: 1em;
-  height:1.5em;
-  position:relative;
-}
-
-.padding-block[hidemargin="false"]:before
-{
-  pointer-events: none;
-  content: ' ';
-  position:absolute;
-  width:100%;
-  height:100%;
-  left:0.5em;
-  top:-0.75em;
-  border-left:rgba(128,128,128,0.6) 1px dashed;
-}
-
-.render-node-text
-{
-  opacity: 0.8;
-}
-
-.render-node-text:hover
-{
-  opacity: 1.0;
-}
-
-.render-node-level-1
-{
-  position:relative;
-  white-space: nowrap;
-}
-
-.render-node-level-1:before
-{
-  pointer-events: none;
-  content: ' ';
-  height:1.5em;
-  width: 1.5em;
-  bottom: 0.25em;
-  left: -0.5em;
-  position: absolute;
-  border-left: rgba(128,128,128,0.6) 1px dashed;
-  border-bottom: rgba(128,128,128,0.6) 1px dashed;
-}
-
-.render-node-level-0 
-{
-  margin-left: 0em;
-}
-.render-node-level-1
-{
-  margin-left: 1em;
-}
-.render-node-level-2
-{
-  margin-left: 2em;
-}
-.render-node-level-3
-{
-  margin-left: 3em;
-}
-.render-node-level-4
-{
-  margin-left: 4em;
-}
-.render-node-level-5
-{
-  margin-left: 5em;
-}
-.render-node-level-6
-{
-  margin-left: 6em;
-}
-.render-node-level-7
-{
-  margin-left: 7em;
-}
-.render-node-level-8
-{
-  margin-left: 8em;
-}
-.render-node-level-9
-{
-  margin-left: 9em;
-}
-.render-node-level-10
-{
-  margin-left: 10em;
-}
-
-
-
-.render-node-level-0  > .padding-block-container
-{
-  left: -0em;
-}
-.render-node-level-1 > .padding-block-container
-{
-  left: -1em;
-}
-.render-node-level-2 > .padding-block-container
-{
-  left: -2em;
-}
-.render-node-level-3 > .padding-block-container
-{
-  left: -3em;
-}
-.render-node-level-4 > .padding-block-container
-{
-  left: -4em;
-}
-.render-node-level-5 > .padding-block-container
-{
-  left: -5em;
-}
-.render-node-level-6 > .padding-block-container
-{
-  left: -6em;
-}
-.render-node-level-7 > .padding-block-container
-{
-  left: -7em;
-}
-.render-node-level-8 > .padding-block-container
-{
-  left: -8em;
-}
-.render-node-level-9 > .padding-block-container
-{
-  left: -9em;
-}
-.render-node-level-10 > .padding-block-container
-{
-  left: -10em;
-}
-
-.r-270
-{
-  transform: rotate(270deg);
-}
-
-[renderNode]
-{
-  height: 15px;
-}
diff --git a/src/components/flatTree/flatTree.template.html b/src/components/flatTree/flatTree.template.html
deleted file mode 100644
index 893c539a9ff029933606880e3e37776e3d7fa0c1..0000000000000000000000000000000000000000
--- a/src/components/flatTree/flatTree.template.html
+++ /dev/null
@@ -1,79 +0,0 @@
-<cdk-virtual-scroll-viewport
-  *ngIf="!useDefaultList"
-  (wheel)="$event.stopPropagation()"
-  itemSize="15"
-  #flatTreeVirtualScrollViewPort>
-
-  <div
-    *cdkVirtualFor="let flattenedItem of (inputItem | flattenTreePipe : findChildren | filterRowsByVisbilityPipe : findChildren : searchFilter | appendSiblingFlagPipe | filterCollapsePipe : collapsedLevels : uncollapsedLevels : childrenExpanded )"
-    [ngClass]="getClass(flattenedItem.flattenedTreeLevel)"
-    class="text-nowrap"
-    [attr.flattenedtreelevel]="flattenedItem.flattenedTreeLevel" 
-    [attr.collapsed]="flattenedItem.collapsed ? flattenedItem.collapsed : false"
-    [attr.lvlId]="flattenedItem.lvlId"
-    renderNode>
-
-    <span class="padding-block-container">
-      <span
-        *ngFor="let block of flattenedItem.siblingFlags"
-        [attr.hidemargin]="block"
-        class="padding-block">
-
-      </span>
-    </span>
-    <span
-      *ngIf="findChildren(flattenedItem).length > 0; else noChildren"
-      (click)="$event.stopPropagation(); toggleCollapse(flattenedItem)" >
-      <i [ngClass]="isCollapsed(flattenedItem) ? 'r-270' : ''" class="fas fa-chevron-down"></i>
-    </span>
-    <span
-      (click)="handleTreeNodeClick($event, flattenedItem)"
-      class="render-node-text"
-      [innerHtml]="flattenedItem | renderPipe : renderNode ">
-    </span>
-  </div>
-</cdk-virtual-scroll-viewport>
-
-<div
-  *ngIf="useDefaultList"
-  class="overflow-auto">
-
-  <div class="overflow-hidden default-container">
-    <div
-      *ngFor="let flattenedItem of (inputItem | flattenTreePipe : findChildren | filterRowsByVisbilityPipe : findChildren : searchFilter | appendSiblingFlagPipe )" 
-      [ngClass]="getClass(flattenedItem.flattenedTreeLevel)"
-      class="text-nowrap"
-      [attr.flattenedtreelevel]="flattenedItem.flattenedTreeLevel" 
-      [attr.collapsed]="flattenedItem.collapsed ? flattenedItem.collapsed : false"
-      [attr.lvlId]="flattenedItem.lvlId"
-      [hidden]="collapseRow(flattenedItem) "
-      renderNode>
-  
-      <span class="padding-block-container">
-        <span
-          *ngFor="let block of flattenedItem.siblingFlags"
-          [attr.hidemargin]="block"
-          class="padding-block">
-  
-        </span>
-      </span>
-      <span
-        *ngIf="findChildren(flattenedItem).length > 0; else noChildren"
-        (click)="$event.stopPropagation(); toggleCollapse(flattenedItem)" >
-        <i [ngClass]="isCollapsed(flattenedItem) ? 'r-270' : ''" class="fas fa-chevron-down"></i>
-      </span>
-      <span
-        (click)="handleTreeNodeClick($event, flattenedItem)"
-        class="render-node-text"
-        [innerHtml]="flattenedItem | renderPipe : renderNode ">
-      </span>
-    </div>
-  </div>
-</div>
-
-
-<ng-template #noChildren>
-  <i class="fas fa-none">
-
-  </i>
-</ng-template>
\ No newline at end of file
diff --git a/src/components/flatTree/flattener.pipe.ts b/src/components/flatTree/flattener.pipe.ts
deleted file mode 100644
index d8d017a541e89359ba81f61204adf1040a57883e..0000000000000000000000000000000000000000
--- a/src/components/flatTree/flattener.pipe.ts
+++ /dev/null
@@ -1,38 +0,0 @@
-import { Pipe, PipeTransform } from "@angular/core";
-
-@Pipe({
-  name : 'flattenTreePipe',
-})
-
-export class FlattenTreePipe implements PipeTransform {
-  public transform(root: any, findChildren: (root: any) => any[]): any&IFlattenedTreeInterface[] {
-    return this.recursiveFlatten(root, findChildren, 0, '0')
-  }
-
-  private recursiveFlatten(obj, findChildren, flattenedTreeLevel, lvlId) {
-    return [
-      this.attachLvlAndLvlIdAndSiblingFlag(
-        obj,
-        flattenedTreeLevel,
-        lvlId,
-      ),
-    ].concat(
-      ...findChildren(obj)
-        .map((c, idx) => this.recursiveFlatten(c, findChildren, flattenedTreeLevel + 1, `${lvlId}_${idx}` )),
-    )
-  }
-
-  private attachLvlAndLvlIdAndSiblingFlag(obj: any, flattenedTreeLevel: number, lvlId: string) {
-    return Object.assign({}, obj, {
-      flattenedTreeLevel,
-      collapsed : typeof obj.collapsed === 'undefined' ? false : true,
-      lvlId,
-    })
-  }
-
-}
-
-export interface IFlattenedTreeInterface {
-  flattenedTreeLevel: number
-  lvlId: string
-}
diff --git a/src/components/flatTree/highlight.pipe.ts b/src/components/flatTree/highlight.pipe.ts
deleted file mode 100644
index 5976ba20b0abf0739390e6b2497a620c0f347735..0000000000000000000000000000000000000000
--- a/src/components/flatTree/highlight.pipe.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import { Pipe, PipeTransform } from "@angular/core";
-import { DomSanitizer } from "@angular/platform-browser";
-
-@Pipe({
-  name : 'highlightPipe',
-})
-
-export class HighlightPipe implements PipeTransform {
-  constructor(private sanitizer: DomSanitizer) {
-
-  }
-  public transform(input: string, searchTerm: string) {
-    return searchTerm && searchTerm !== ''
-      ? this.sanitizer.bypassSecurityTrustHtml(input.replace(new RegExp( searchTerm, 'gi'), (s) => `<span class = "highlight">${s}</span>`))
-      : input
-  }
-}
diff --git a/src/components/flatTree/render.pipe.ts b/src/components/flatTree/render.pipe.ts
deleted file mode 100644
index 0b2707fcd9c135fad134ed9ed3d094eb43d28582..0000000000000000000000000000000000000000
--- a/src/components/flatTree/render.pipe.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { Pipe, PipeTransform } from "@angular/core";
-
-@Pipe({
-  name : 'renderPipe',
-})
-
-export class RenderPipe implements PipeTransform {
-  public transform(node: any, renderFunction: (node: any) => string): string {
-    return renderFunction(node)
-  }
-}
diff --git a/src/components/markdown/index.ts b/src/components/markdown/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8751618833a80782c23e4f6d8f8fda2401e2c455
--- /dev/null
+++ b/src/components/markdown/index.ts
@@ -0,0 +1,2 @@
+export { MarkdownModule } from "./module"
+export { MarkdownDom } from "./markdownCmp/markdown.component"
\ No newline at end of file
diff --git a/src/components/markdown/markdown.component.ts b/src/components/markdown/markdownCmp/markdown.component.ts
similarity index 73%
rename from src/components/markdown/markdown.component.ts
rename to src/components/markdown/markdownCmp/markdown.component.ts
index bd5ea00320923c15e4517232021822b91fd8e630..7084626a5ed37fb4162d610edfe232fcf3852381 100644
--- a/src/components/markdown/markdown.component.ts
+++ b/src/components/markdown/markdownCmp/markdown.component.ts
@@ -1,4 +1,5 @@
-import { ChangeDetectionStrategy, Component, ElementRef, Input, ViewChild, ChangeDetectorRef, AfterViewChecked, OnChanges } from '@angular/core'
+import { ChangeDetectionStrategy, Component, ElementRef, Input, ViewChild, ChangeDetectorRef, AfterViewChecked, OnChanges, SecurityContext } from '@angular/core'
+import { DomSanitizer } from '@angular/platform-browser'
 import * as showdown from 'showdown'
 
 @Component({
@@ -19,7 +20,8 @@ export class MarkdownDom implements OnChanges {
   })
 
   constructor(
-    private cdr: ChangeDetectorRef
+    private cdr: ChangeDetectorRef,
+    private ds: DomSanitizer,
   ) {
     this.cdr.detach()
     this.converter.setFlavor('github')
@@ -32,9 +34,13 @@ export class MarkdownDom implements OnChanges {
   }
 
   ngOnChanges(){
-    this.innerHtml = this.converter.makeHtml(
+    const innerHtml = this.converter.makeHtml(
       this.getMarkdown()
     )
+    this.innerHtml = this.ds.sanitize(
+      SecurityContext.HTML,
+      innerHtml
+    )
     this.cdr.detectChanges()
   }
 
diff --git a/src/components/markdown/markdownCmp/markdown.style.css b/src/components/markdown/markdownCmp/markdown.style.css
new file mode 100644
index 0000000000000000000000000000000000000000..0645dc01a568740a2c3d2f51c486adcb7f111a02
--- /dev/null
+++ b/src/components/markdown/markdownCmp/markdown.style.css
@@ -0,0 +1,4 @@
+:host
+{
+  display: block;
+}
\ No newline at end of file
diff --git a/src/components/markdown/markdown.template.html b/src/components/markdown/markdownCmp/markdown.template.html
similarity index 67%
rename from src/components/markdown/markdown.template.html
rename to src/components/markdown/markdownCmp/markdown.template.html
index f4a643338901034ec807c46393c68e4c7b8b29df..ae217e43cef6c30161ce1d6ff048bd8272ab69b9 100644
--- a/src/components/markdown/markdown.template.html
+++ b/src/components/markdown/markdownCmp/markdown.template.html
@@ -1,4 +1,4 @@
-<div [innerHTML]="innerHtml | safeHtml">
+<div [innerHTML]="innerHtml">
 </div>
 <div class="d-none" #ngContentWrapper>
   <ng-content>
diff --git a/src/components/markdown/module.ts b/src/components/markdown/module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7dd8368aea81ebc68f7091175d83971796a00ffb
--- /dev/null
+++ b/src/components/markdown/module.ts
@@ -0,0 +1,17 @@
+import { CommonModule } from "@angular/common";
+import { NgModule } from "@angular/core";
+import { MarkdownDom } from "./markdownCmp/markdown.component";
+
+@NgModule({
+  imports: [
+    CommonModule,
+  ],
+  declarations: [
+    MarkdownDom
+  ],
+  exports: [
+    MarkdownDom
+  ]
+})
+
+export class MarkdownModule{}
diff --git a/src/components/spinner/index.ts b/src/components/spinner/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b6eb0631154fb743ba8e6f4e4c0ef3b1afadd4f0
--- /dev/null
+++ b/src/components/spinner/index.ts
@@ -0,0 +1,6 @@
+export {
+  SpinnerModule
+} from "./module"
+export {
+  SpinnerCmp
+} from "./spinnerCmp/spinner.component"
\ No newline at end of file
diff --git a/src/components/spinner/module.ts b/src/components/spinner/module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..676496b3d78b0ba6e07924d5ff702af34b4e1b58
--- /dev/null
+++ b/src/components/spinner/module.ts
@@ -0,0 +1,17 @@
+import { CommonModule } from "@angular/common";
+import { NgModule } from "@angular/core";
+import { SpinnerCmp } from "./spinnerCmp/spinner.component";
+
+@NgModule({
+  imports: [
+    CommonModule
+  ],
+  declarations: [
+    SpinnerCmp
+  ],
+  exports: [
+    SpinnerCmp
+  ]
+})
+
+export class SpinnerModule{}
\ No newline at end of file
diff --git a/src/components/spinner/spinner.component.ts b/src/components/spinner/spinnerCmp/spinner.component.ts
similarity index 100%
rename from src/components/spinner/spinner.component.ts
rename to src/components/spinner/spinnerCmp/spinner.component.ts
diff --git a/src/components/spinner/spinner.template.html b/src/components/spinner/spinnerCmp/spinner.template.html
similarity index 100%
rename from src/components/spinner/spinner.template.html
rename to src/components/spinner/spinnerCmp/spinner.template.html
diff --git a/src/components/tile/tile.component.ts b/src/components/tile/tile.component.ts
index fcab669508d12e06852675c41e7a20e38bc72843..80379e4b1120b6de4988f15cfbedd73c365c93a0 100644
--- a/src/components/tile/tile.component.ts
+++ b/src/components/tile/tile.component.ts
@@ -1,4 +1,4 @@
-import { Component, EventEmitter, HostBinding, HostListener, Input, OnChanges, Output } from "@angular/core";
+import { ChangeDetectionStrategy, Component, EventEmitter, HostBinding, HostListener, Input, OnChanges, Output } from "@angular/core";
 
 @Component({
   selector: 'tile-cmp',
@@ -6,7 +6,8 @@ import { Component, EventEmitter, HostBinding, HostListener, Input, OnChanges, O
   styleUrls: [
     './tile.style.css'
   ],
-  exportAs: 'tileCmp'
+  exportAs: 'tileCmp',
+  changeDetection: ChangeDetectionStrategy.OnPush
 })
 
 export class TileCmp implements OnChanges{
diff --git a/src/components/tile/tile.style.css b/src/components/tile/tile.style.css
index e877c720820a2a9b6fbf6a0d988fad5b90385054..40d1830348c3e18559a2d2c75668e4b03cbcd3dc 100644
--- a/src/components/tile/tile.style.css
+++ b/src/components/tile/tile.style.css
@@ -9,6 +9,7 @@
 
 .tile-selected {
   border: 2px solid #FED363;
+  box-sizing: border-box;
 }
 
 :host,
@@ -17,6 +18,7 @@ img
   display: block;
   width: 100%;
   height: 100%;
+  max-width: 16rem;
 }
 
 img
diff --git a/src/components/tile/tile.template.html b/src/components/tile/tile.template.html
index 44cc134d2a135d88ad24d04c1631cb68ed827b5e..1187193efd379373fabdb461f9f4883251fb5e41 100644
--- a/src/components/tile/tile.template.html
+++ b/src/components/tile/tile.template.html
@@ -1,7 +1,7 @@
-<div class="position-relative">
+<div class="sxplr-position-relative">
   <!-- info icon -->
   <div *ngIf="tileShowInfo"
-    class="position-absolute right-0">
+    class="sxplr-position-absolute right-0">
     <div mat-icon-button
       iav-stop="click"
       (click)="onInfoClick.emit($event)"
@@ -9,7 +9,7 @@
         backgroundColor: darktheme ? 'white' : 'black',
         color: darktheme ? 'black': 'white'
       }"
-      class="mat-elevation-z2 iv-custom-comp info-button">
+      class="mat-elevation-z2 sxplr-custom-cmp info-button">
       <small>
         <i class="fas fa-info"></i>
       </small>
@@ -18,8 +18,12 @@
 
 
   <!-- directory icon -->
-  <div *ngIf="isDir" class="position-absolute bottom-0 right-0">
-    <i class="fas fa-folder folder-container fa-2x"></i>
+  <div *ngIf="isDir" class="sxplr-position-absolute bottom-0 right-0"
+    [ngClass]="{
+      'darktheme': darktheme,
+      'lighttheme': !darktheme
+    }">
+    <i class="fas fa-folder folder-container fa-2x sxplr-custom-cmp text"></i>
   </div>
 
   <img [src]="tileImgSrc"
@@ -30,6 +34,6 @@
     draggable="false">
 </div>
 
-<div class="tile-text">
+<div class="tile-text mat-body">
   {{ tileText }}
 </div>
diff --git a/src/components/tree/tree.animation.ts b/src/components/tree/tree.animation.ts
deleted file mode 100644
index eca9be9c9530b480c4fdc7e696cfa252c61e2728..0000000000000000000000000000000000000000
--- a/src/components/tree/tree.animation.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-import { animate, state, style, transition, trigger } from "@angular/animations";
-
-export const treeAnimations = trigger('collapseState', [
-  state('collapsed',
-    style({
-      'margin-top' : '-{{ fullHeight }}px',
-    }),
-    { params : { fullHeight : 0 } },
-  ),
-  state('visible',
-    style({
-      'margin-top' : '0px',
-    }),
-    { params : { fullHeight : 0 } },
-  ),
-  transition('collapsed => visible', [
-    animate('180ms'),
-  ]),
-  transition('visible => collapsed', [
-    animate('180ms'),
-  ]),
-])
diff --git a/src/components/tree/tree.component.ts b/src/components/tree/tree.component.ts
deleted file mode 100644
index b462dbc2c314626f1052bc552af39ed62ae5b6db..0000000000000000000000000000000000000000
--- a/src/components/tree/tree.component.ts
+++ /dev/null
@@ -1,154 +0,0 @@
-import { AfterContentChecked, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, HostBinding, Input, OnChanges, OnDestroy, OnInit, Optional, Output, QueryList, ViewChild, ViewChildren } from "@angular/core";
-import { Subscription } from "rxjs";
-import { ParseAttributeDirective } from "../parseAttribute.directive";
-import { treeAnimations } from "./tree.animation";
-import { TreeService } from "./treeService.service";
-
-@Component({
-  selector : 'tree-component',
-  templateUrl : './tree.template.html',
-  styleUrls : [
-    './tree.style.css',
-  ],
-  animations : [
-    treeAnimations,
-  ],
-  changeDetection: ChangeDetectionStrategy.OnPush,
-})
-
-export class TreeComponent extends ParseAttributeDirective implements OnChanges, OnInit, OnDestroy, AfterContentChecked {
-  @Input() public inputItem: any = {
-    name : 'Untitled',
-    children : [],
-  }
-  @Input() public childrenExpanded: boolean = true
-
-  @Output() public mouseentertree: EventEmitter<any> = new EventEmitter()
-  @Output() public mouseleavetree: EventEmitter<any> = new EventEmitter()
-  @Output() public mouseclicktree: EventEmitter<any> = new EventEmitter()
-
-  @ViewChildren(TreeComponent) public treeChildren: QueryList<TreeComponent>
-  @ViewChild('childrenContainer', { read : ElementRef }) public childrenContainer: ElementRef
-
-  constructor(
-    private cdr: ChangeDetectorRef,
-    @Optional() public treeService: TreeService,
-  ) {
-    super()
-  }
-
-  public subscriptions: Subscription[] = []
-
-  public ngOnInit() {
-    if ( this.treeService ) {
-      this.subscriptions.push(
-        this.treeService.markForCheck.subscribe(() => this.cdr.markForCheck()),
-      )
-    }
-  }
-
-  public ngOnDestroy() {
-    this.subscriptions.forEach(s => s.unsubscribe())
-  }
-
-  public _fullHeight: number = 9999
-
-  set fullHeight(num: number) {
-    this._fullHeight = num
-  }
-
-  get fullHeight() {
-    return this._fullHeight
-  }
-
-  public ngAfterContentChecked() {
-    this.fullHeight = this.childrenContainer ? this.childrenContainer.nativeElement.offsetHeight : 0
-    this.cdr.detectChanges()
-  }
-
-  public mouseenter(ev: MouseEvent) {
-    this.treeService.mouseenter.next({
-      inputItem : this.inputItem,
-      node : this,
-      event : ev,
-    })
-  }
-
-  public mouseleave(ev: MouseEvent) {
-    this.treeService.mouseleave.next({
-      inputItem : this.inputItem,
-      node : this,
-      event : ev,
-    })
-  }
-
-  public mouseclick(ev: MouseEvent) {
-    this.treeService.mouseclick.next({
-      inputItem : this.inputItem,
-      node : this,
-      event : ev,
-    })
-  }
-
-  get chevronClass(): string {
-    return this.children ?
-      this.children.length > 0 ?
-        this.childrenExpanded ?
-          'fa-chevron-down' :
-          'fa-chevron-right' :
-        'fa-none' :
-      'fa-none'
-  }
-
-  public handleEv(event: Event) {
-    event.preventDefault();
-    event.stopPropagation();
-  }
-
-  public toggleChildrenShow(event: Event) {
-    this.childrenExpanded = !this.childrenExpanded
-    event.stopPropagation()
-    event.preventDefault()
-  }
-
-  get children(): any[] {
-    return this.treeService ?
-      this.treeService.findChildren(this.inputItem) :
-      this.inputItem.children
-  }
-
-  @HostBinding('attr.filterHidden')
-  get visibilityOnFilter(): boolean {
-    return this.treeService ?
-      this.treeService.searchFilter(this.inputItem) :
-      true
-  }
-  public handleMouseEnter(fullObj: any) {
-
-    this.mouseentertree.emit(fullObj)
-
-    if (this.treeService) {
-      this.treeService.mouseenter.next(fullObj)
-    }
-  }
-
-  public handleMouseLeave(fullObj: any) {
-
-    this.mouseleavetree.emit(fullObj)
-
-    if (this.treeService) {
-      this.treeService.mouseleave.next(fullObj)
-    }
-  }
-
-  public handleMouseClick(fullObj: any) {
-
-    this.mouseclicktree.emit(fullObj)
-
-    if (this.treeService) {
-      this.treeService.mouseclick.next(fullObj)
-    }
-  }
-
-  public defaultSearchFilter = () => true
-}
diff --git a/src/components/tree/tree.style.css b/src/components/tree/tree.style.css
deleted file mode 100644
index b05982290b10f2674d9868aa9d958274d762979d..0000000000000000000000000000000000000000
--- a/src/components/tree/tree.style.css
+++ /dev/null
@@ -1,111 +0,0 @@
-tree-component
-{
-  display:block;
-  margin-left:1em;
-}
-
-div[itemContainer]
-{
-  display:flex;
-}
-
-div[itemContainer] > [fas]
-{
-  flex: 0 0 1.2em;
-  align-self: center;
-  text-align: center;
-  z-index: 1;
-}
-
-div[itemContainer] > [itemName]
-{
-  flex: 1 1 0px;
-  white-space: nowrap;
-}
-
-div[itemContainer] > span[itemName]:hover
-{
-  cursor:default;
-  color:rgba(219, 181, 86,1);
-}
-
-/* dashed guiding line */
-
-tree-component:not(:last-child) > div[itemMasterContainer]:before
-{
-  
-  pointer-events: none;
-  top:-0.8em;
-  left:-0.5em;
-  height:100%;
-  box-sizing: border-box;
-
-  border-left: rgba(255,255,255,1) 1px dashed;
-}
-
-tree-component:not(:last-child) div[itemMasterContainer] > [itemContainer]
-{
-  position:relative;
-}
-
-
-tree-component:not(:last-child) div[itemMasterContainer] > [itemContainer]:before
-{
-  pointer-events: none;
-  content : '';
-  position:absolute;
-  width:1.5em;
-  height:100%;
-  top:-50%;
-  left:-0.5em;
-  z-index: 0;
-}
-
-tree-component:last-child div[itemMasterContainer] > [itemContainer]
-{
-  position:relative;
-}
-
-tree-component:last-child div[itemMasterContainer] > [itemContainer]:before
-{
-  pointer-events: none;
-  content : '';
-  position:absolute;
-  width:1.5em;
-  height:100%;
-  top:-50%;
-  left:-0.5em;
-  z-index: 0;
-}
-
-tree-component:not(:last-child) div[itemMasterContainer]:before
-{
-  border-left: rgba(128,128,128,0.6) 1px dashed;
-}
-
-tree-component:not(:last-child) div[itemMasterContainer] > [itemContainer]:before
-{
-  border-bottom: rgba(128,128,128,0.6) 1px dashed;
-}
-
-tree-component div[itemMasterContainer]:last-child > [itemContainer]:before
-{
-  border-bottom: rgba(128,128,128,0.6) 1px dashed;
-  border-left : rgba(128,128,128,0.6) 1px dashed;
-}
-
-div[itemMasterContainer]
-{
-  position:relative;
-}
-div[itemMasterContainer]:before
-{
-  content:' ';
-  position:absolute;
-  z-index: 0;
-}
-
-div[childrenOverflowContainer]
-{
-  overflow:hidden;
-}
\ No newline at end of file
diff --git a/src/components/tree/tree.template.html b/src/components/tree/tree.template.html
deleted file mode 100644
index 10b6318bfe862cd8da5813da78aabd16e3ff6782..0000000000000000000000000000000000000000
--- a/src/components/tree/tree.template.html
+++ /dev/null
@@ -1,30 +0,0 @@
-<div
-  itemMasterContainer>
-  <div itemContainer>
-    <i
-      (click) = "toggleChildrenShow($event)"
-      [ngClass] = "chevronClass"
-      class = "fas"
-      fas>
-    </i>
-    <span 
-      (mouseleave)="handleMouseLeave({inputItem:inputItem,node:this});handleEv($event)"
-      (mouseenter)="handleMouseEnter({inputItem:inputItem,node:this});handleEv($event)"
-      (click)="handleMouseClick({inputItem:inputItem,node:this});handleEv($event)"
-      [innerHTML] = "treeService ? treeService.renderNode(inputItem) : inputItem.name"
-      itemName>
-    </span>
-  </div>
-  <div childrenOverflowContainer>
-    <div
-      [@collapseState] = "{ value :  childrenExpanded ? 'visible' : 'collapsed' , params : { fullHeight : fullHeight }}"
-      #childrenContainer>
-      <tree-component
-        *ngFor = "let child of ( treeService ? (treeService.findChildren(inputItem) | treeSearch : treeService.searchFilter : treeService.findChildren ) : inputItem.children )"
-        [childrenExpanded] = "childrenExpanded"
-        [inputItem] = "child">
-    
-      </tree-component>
-    </div>
-  </div>
-</div>
\ No newline at end of file
diff --git a/src/components/tree/treeBase.directive.ts b/src/components/tree/treeBase.directive.ts
deleted file mode 100644
index 43fdd5954cb1b4c0145aa5a9ffb10c4d5979f46f..0000000000000000000000000000000000000000
--- a/src/components/tree/treeBase.directive.ts
+++ /dev/null
@@ -1,54 +0,0 @@
-import { ChangeDetectorRef, Directive, EventEmitter, Input, OnChanges, OnDestroy, Output } from "@angular/core";
-import { Subscription } from "rxjs";
-import { TreeService } from "./treeService.service";
-
-@Directive({
-  selector : '[treebase]',
-  host : {
-    style : `
-
-    `,
-  },
-  providers : [
-    TreeService,
-  ],
-})
-
-export class TreeBaseDirective implements OnDestroy, OnChanges {
-  @Output() public treeNodeClick: EventEmitter<any> = new EventEmitter()
-  @Output() public treeNodeEnter: EventEmitter<any> = new EventEmitter()
-  @Output() public treeNodeLeave: EventEmitter<any> = new EventEmitter()
-
-  @Input() public renderNode: (item: any) => string = (item) => item.name
-  @Input() public findChildren: (item: any) => any[] = (item) => item.children
-  @Input() public searchFilter: (item: any) => boolean | null = () => true
-
-  private subscriptions: Subscription[] = []
-
-  constructor(
-    public changeDetectorRef: ChangeDetectorRef,
-    public treeService: TreeService,
-  ) {
-    this.subscriptions.push(
-      this.treeService.mouseclick.subscribe((obj) => this.treeNodeClick.emit(obj)),
-    )
-    this.subscriptions.push(
-      this.treeService.mouseenter.subscribe((obj) => this.treeNodeEnter.emit(obj)),
-    )
-    this.subscriptions.push(
-      this.treeService.mouseleave.subscribe((obj) => this.treeNodeLeave.emit(obj)),
-    )
-  }
-
-  public ngOnChanges() {
-    this.treeService.findChildren = this.findChildren
-    this.treeService.renderNode = this.renderNode
-    this.treeService.searchFilter = this.searchFilter
-
-    this.treeService.markForCheck.next(true)
-  }
-
-  public ngOnDestroy() {
-    this.subscriptions.forEach(s => s.unsubscribe())
-  }
-}
diff --git a/src/components/tree/treeService.service.ts b/src/components/tree/treeService.service.ts
deleted file mode 100644
index 2dc6bfc4dc1810038bdb008907a5d2dd5e0c27d9..0000000000000000000000000000000000000000
--- a/src/components/tree/treeService.service.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import { Injectable } from "@angular/core";
-import { Subject } from "rxjs";
-
-@Injectable()
-export class TreeService {
-  public mouseclick: Subject<any> = new Subject()
-  public mouseenter: Subject<any> = new Subject()
-  public mouseleave: Subject<any> = new Subject()
-
-  public findChildren: (item: any) => any[] = (item) => item.children
-  public searchFilter: (item: any) => boolean | null = () => true
-  public renderNode: (item: any) => string = (item) => item.name
-
-  public searchTerm: string = ``
-
-  public markForCheck: Subject<any> = new Subject()
-}
diff --git a/src/components/treeSearch.pipe.spec.ts b/src/components/treeSearch.pipe.spec.ts
deleted file mode 100644
index 18056277559c98ff54eff92ddc298e0528b95b54..0000000000000000000000000000000000000000
--- a/src/components/treeSearch.pipe.spec.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-/* eslint-disable */
-
-import { TreeSearchPipe } from './treeSearch.pipe'
-
-describe('TreeSearchPipe works as intended', () => {
-  const pipe = new TreeSearchPipe()
-  /* TODO add tests for tree search pipe */
-})
diff --git a/src/components/treeSearch.pipe.ts b/src/components/treeSearch.pipe.ts
deleted file mode 100644
index 63c70a8e023bc4e395bf767e44428f7b9fcba308..0000000000000000000000000000000000000000
--- a/src/components/treeSearch.pipe.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import { Pipe, PipeTransform } from "@angular/core";
-
-@Pipe({
-  name : 'treeSearch',
-})
-
-export class TreeSearchPipe implements PipeTransform {
-  public transform(array: any[]|null, filterFn: (item: any) => boolean, getChildren: (item: any) => any[]): any[] {
-    const transformSingle = (item: any): boolean =>
-      filterFn(item) ||
-      (getChildren(item)
-        ? getChildren(item).some(transformSingle)
-        : false)
-    return array
-      ? array.filter(transformSingle)
-      : []
-  }
-}
diff --git a/src/components/vButton/vButton.component.ts b/src/components/vButton/vButton.component.ts
index f2720905b3ff3bb9ff75a5b01d9add65643b552e..3dbd98aa23dc24da26dfc564e52859cdd99ea5b7 100644
--- a/src/components/vButton/vButton.component.ts
+++ b/src/components/vButton/vButton.component.ts
@@ -19,7 +19,7 @@ export class IAVVerticalButton{
   private color$ = new Subject<TIVBColor>()
   public class$ = this.color$.pipe(
     startWith('default'),
-    map(colorCls => `d-flex flex-column align-items-center iv-custom-comp ${colorCls} h-100`)
+    map(colorCls => `d-flex flex-column align-items-center sxplr-custom-cmp ${colorCls} h-100`)
   )
 
   @Input()
diff --git a/src/databrowser.fallback.ts b/src/databrowser.fallback.ts
deleted file mode 100644
index 86c9c626f104954b9f28f9b3992894c6b2ea3a71..0000000000000000000000000000000000000000
--- a/src/databrowser.fallback.ts
+++ /dev/null
@@ -1,114 +0,0 @@
-import { InjectionToken } from "@angular/core"
-import { Observable } from "rxjs"
-import { IHasId } from "./util/interfaces"
-
-/**
- * TODO gradually move to relevant.
- */
-
-export const OVERRIDE_IAV_DATASET_PREVIEW_DATASET_FN = new InjectionToken<(file: any, dataset: any) => void>('OVERRIDE_IAV_DATASET_PREVIEW_DATASET_FN')
-export const GET_KGDS_PREVIEW_INFO_FROM_ID_FILENAME = new InjectionToken<({ datasetSchema, datasetId, filename }) => Observable<any|null>>('GET_KGDS_PREVIEW_INFO_FROM_ID_FILENAME')
-export const kgTos = `The interactive viewer queries HBP Knowledge Graph Data Platform ("KG") for published datasets.
-
-
-Access to the data and metadata provided through KG requires that you cite and acknowledge said data and metadata according to the Terms and Conditions of the Platform.
-
-
-Citation requirements are outlined <https://www.humanbrainproject.eu/en/explore-the-brain/search-terms-of-use#citations> .
-
-
-Acknowledgement requirements are outlined <https://www.humanbrainproject.eu/en/explore-the-brain/search-terms-of-use#acknowledgements>
-
-
-These outlines are based on the authoritative Terms and Conditions are found <https://www.humanbrainproject.eu/en/explore-the-brain/search-terms-of-use>
-
-
-If you do not accept the Terms & Conditions you are not permitted to access or use the KG to search for, to submit, to post, or to download any materials found there-in.
-`
-
-export function getKgSchemaIdFromFullId(fullId: string): [string, string]{
-  if (!fullId) { return [null, null] }
-  const match = /([\w\-.]*\/[\w\-.]*\/[\w\-.]*\/[\w\-.]*)\/([\w\-.]*)$/.exec(fullId)
-  if (!match) { return [null, null] }
-  return [match[1], match[2]]
-}
-
-
-export interface IKgReferenceSpace {
-  name: string
-}
-
-export interface IKgPublication {
-  name: string
-  doi: string
-  cite: string
-}
-
-export interface IKgParcellationRegion {
-  id?: string
-  name: string
-}
-
-export interface IKgActivity {
-  methods: string[]
-  preparation: string[]
-  protocols: string[]
-}
-
-export interface IKgDataEntry {
-  activity: IKgActivity[]
-  name: string
-  description: string
-  license: string[]
-  licenseInfo: string[]
-  parcellationRegion: IKgParcellationRegion[]
-  formats: string[]
-  custodians: string[]
-  contributors: string[]
-  referenceSpaces: IKgReferenceSpace[]
-  files: File[]
-  publications: IKgPublication[]
-  embargoStatus: IHasId[]
-
-  methods: string[]
-  protocols: string[]
-
-  preview?: boolean
-
-  /**
-   * TODO typo, should be kgReferences
-   */
-  kgReference: string[]
-
-  id: string
-  fullId: string
-}
-
-export type TypePreviewDispalyed = (file, dataset) => Observable<boolean>
-export const IAV_DATASET_PREVIEW_ACTIVE = new InjectionToken<TypePreviewDispalyed>('IAV_DATASET_PREVIEW_ACTIVE')
-
-
-export enum EnumPreviewFileTypes{
-  NIFTI,
-  IMAGE,
-  CHART,
-  OTHER,
-  VOLUMES,
-}
-
-export interface DatasetPreview {
-  datasetId: string
-  filename: string
-}
-
-export function determinePreviewFileType(previewFile: any): EnumPreviewFileTypes {
-  if (!previewFile) throw new Error(`previewFile is required to determine the file type`)
-  const { mimetype, data } = previewFile
-  const chartType = data && data['chart.js'] && data['chart.js'].type
-  const registerdVolumes = data && data['iav-registered-volumes']
-  if ( mimetype === 'application/nifti' ) { return EnumPreviewFileTypes.NIFTI }
-  if ( /^image/.test(mimetype)) { return EnumPreviewFileTypes.IMAGE }
-  if ( /application\/json/.test(mimetype) && (chartType === 'line' || chartType === 'radar')) { return EnumPreviewFileTypes.CHART }
-  if ( /application\/json/.test(mimetype) && !!registerdVolumes) { return EnumPreviewFileTypes.VOLUMES }
-  return EnumPreviewFileTypes.OTHER
-}
diff --git a/src/environments/environment.common.ts b/src/environments/environment.common.ts
index 83bad8bcb9a8fe4ec1e40eb4e419d41dc7748294..f4d5c96275db63b6e84e76a7223c597139fba508 100644
--- a/src/environments/environment.common.ts
+++ b/src/environments/environment.common.ts
@@ -4,8 +4,7 @@ export const environment = {
   VERSION: 'unknown version',
   PRODUCTION: true,
   BACKEND_URL: null,
-  DATASET_PREVIEW_URL: 'https://hbp-kg-dataset-previewer.apps.hbp.eu/v2',
-  BS_REST_URL: 'https://siibra-api-rc.apps.hbp.eu/v1_0',
+  BS_REST_URL: 'https://siibra-api-latest.apps-dev.hbp.eu/v2_0',
   SPATIAL_TRANSFORM_BACKEND: 'https://hbp-spatial-backend.apps.hbp.eu',
   MATOMO_URL: null,
   MATOMO_ID: null,
diff --git a/src/environments/parseEnv.js b/src/environments/parseEnv.js
index dbd985314dc0c472611870143293d584b95e1e11..b05909d596c85cc1875650602cafb8a5fa2fba3a 100644
--- a/src/environments/parseEnv.js
+++ b/src/environments/parseEnv.js
@@ -7,7 +7,6 @@ const main = async () => {
   const pathToEnvFile = path.join(__dirname, './environment.prod.ts')
   const {
     BACKEND_URL,
-    DATASET_PREVIEW_URL,
     STRICT_LOCAL,
     MATOMO_URL,
     MATOMO_ID,
@@ -19,7 +18,6 @@ const main = async () => {
   
   console.log(`[parseEnv.js] parse envvar:`, {
     BACKEND_URL,
-    DATASET_PREVIEW_URL,
     STRICT_LOCAL,
     MATOMO_URL,
     MATOMO_ID,
@@ -43,7 +41,6 @@ export const environment = {
   VERSION: ${version},
   BS_REST_URL: ${JSON.stringify(BS_REST_URL)},
   BACKEND_URL: ${JSON.stringify(BACKEND_URL)},
-  DATASET_PREVIEW_URL: ${JSON.stringify(DATASET_PREVIEW_URL)},
   STRICT_LOCAL: ${JSON.stringify(STRICT_LOCAL)},
   MATOMO_URL: ${JSON.stringify(MATOMO_URL)},
   MATOMO_ID: ${JSON.stringify(MATOMO_ID)},
diff --git a/src/extra_styles.css b/src/extra_styles.css
index 928bcf43c3700cef7c1b006cc7b85ae1bedda609..35a70999b7b44433ec39f25835dac0ceb5bff1a7 100644
--- a/src/extra_styles.css
+++ b/src/extra_styles.css
@@ -293,6 +293,11 @@ markdown-dom p
   transform: rotate(270deg)!important;
 }
 
+.ws-normal
+{
+  white-space: normal!important;
+}
+
 .ws-no-wrap
 {
   white-space: nowrap!important;
@@ -579,61 +584,11 @@ markdown-dom p
   flex-grow:1;
 }
 
-.transform-origin-left-center
-{
-  transform-origin: 0% 50%;
-}
-
-.transform-origin-center
-{
-  transform-origin: 50% 50%;
-}
-
 .box-shadow-none
 {
   box-shadow: none!important;
 }
 
-.translate-x-2
-{
-  transform: translateX(1em);
-}
-
-.translate-x-3
-{
-  transform: translateX(1.5em);
-}
-
-.translate-x-4
-{
-  transform: translateX(2em);
-}
-
-.translate-x-6
-{
-  transform: translateX(3em);
-}
-
-.translate-x-4-n
-{
-  transform: translateX(-2em);
-}
-
-.translate-x-6-n
-{
-  transform: translateX(-3em);
-}
-
-.translate-x-7-n
-{
-  transform: translateX(-3.5em);
-}
-
-.translate-x-8-n
-{
-  transform: translateX(-4em);
-}
-
 /* this is required to physically link label with side bar */
 .mat-drawer-content-overflow-visible > mat-drawer-content,
 /* this is required to show the popout info of template and parcellation */
@@ -642,11 +597,6 @@ markdown-dom p
   overflow: visible!important;
 }
 
-.overflow-visible
-{
-  overflow: visible!important;
-}
-
 mat-icon[fontset="fas"],
 mat-icon[fontset="far"]
 {
@@ -751,27 +701,11 @@ kg-dataset-previewer > img
   width: 100%;
 }
 
-.hover-grab
-{
-  opacity: 0.5;
-  transition: opacity 200ms ease-in-out;
-  cursor: move;
-}
-
-.hover-grab:hover
-{
-  opacity: 1.0;
-}
-
 .m-15
 {
   margin: 15px;
 }
 
-.m-1px
-{
-  margin:1px;
-}
 
 .ml-15px-n
 {
@@ -873,10 +807,6 @@ mat-list.sm mat-list-item
 {
   height:50px;
 }
-.scale-80
-{
-  transform: scale(0.8);
-}
 
 .grid
 {
@@ -904,4 +834,50 @@ mat-list.sm mat-list-item
 .v-align-top
 {
   vertical-align: top;
-}
\ No newline at end of file
+}
+
+.text-overflow-ellipses
+{
+  text-overflow: ellipsis;
+}
+
+iav-cmp-viewer-container .mat-chip-list-wrapper
+{
+  flex-wrap: nowrap;
+}
+
+sxplr-sapiviews-features-ieeg-ieegdataset .mat-chip-list-wrapper
+{
+  overflow-y: hidden;
+  overflow-x: auto;
+}
+
+iav-cmp-viewer-container poly-update-cmp .mat-chip-list-wrapper
+{
+  flex-wrap: wrap;
+}
+
+quick-tour-unit svg
+{
+  pointer-events: none;
+  stroke-width: 3px;
+  stroke: rgb(255, 255, 255);
+  stroke-linecap: round;
+  stroke-linejoin: round;
+}
+
+sxplr-sapiviews-features-receptor-autoradiograph canvas
+{
+  max-width: 100%;
+  max-height: 100%;
+}
+
+how-to-cite img
+{
+  width: 100%;
+}
+
+.mat-menu-panel.parc-smart-chip-menu-panel
+{
+  max-width: 100vw;
+}
diff --git a/src/getFileInput/fileInputModal/fileInputModal.template.html b/src/getFileInput/fileInputModal/fileInputModal.template.html
index 4fdc365d5169260206961acf60c0e117f29ce582..b491fb597fcda41d3b63c39881e7262b896efb89 100644
--- a/src/getFileInput/fileInputModal/fileInputModal.template.html
+++ b/src/getFileInput/fileInputModal/fileInputModal.template.html
@@ -34,7 +34,7 @@
       </label>
       <input (change)="handleFileInputChange($event)"
         type="file"
-        class="position-absolute left-0 top-0 w-0 h-0 invisible"
+        class="position-absolute left-0 tosxplr-p-0 w-0 h-0 invisible"
         name="file-input"
         id="file-input"
         #fileInput>
diff --git a/src/glue.spec.ts b/src/glue.spec.ts
index 63009e7a8914a09f7bf31672669438cce70cb4f2..e398540319d21c6cf282f31bbe73359a39e760c6 100644
--- a/src/glue.spec.ts
+++ b/src/glue.spec.ts
@@ -1,16 +1,5 @@
-import { TestBed, tick, fakeAsync, discardPeriodicTasks } from "@angular/core/testing"
-import { DatasetPreviewGlue, glueSelectorGetUiStatePreviewingFiles, glueActionRemoveDatasetPreview, datasetPreviewMetaReducer, glueActionAddDatasetPreview, GlueEffects, ClickInterceptorService } from "./glue"
-import { provideMockStore, MockStore } from "@ngrx/store/testing"
-import { getRandomHex, getIdObj } from 'common/util'
-import { EnumWidgetTypes, TypeOpenedWidget, uiActionSetPreviewingDatasetFiles, uiStatePreviewingDatasetFilesSelector } from "./services/state/uiState.store.helper"
-import { hot } from "jasmine-marbles"
-import { HttpClientTestingModule, HttpTestingController } from "@angular/common/http/testing"
-import { glueActionToggleDatasetPreview } from './glue'
-import { DS_PREVIEW_URL } from 'src/util/constants'
-import { NgLayersService } from "./ui/layerbrowser/ngLayerService.service"
-import { GET_KGDS_PREVIEW_INFO_FROM_ID_FILENAME } from "./databrowser.fallback"
-import { viewerStateSelectedTemplateSelector } from "./services/state/viewerState/selectors"
-import { generalActionError } from "./services/stateStore.helper"
+import { ClickInterceptorService } from "./glue"
+import { getRandomHex } from 'common/util'
 
 const mockActionOnSpyReturnVal0 = { 
   id: getRandomHex(),
@@ -91,587 +80,6 @@ const dataset1 = {
 }
 
 describe('> glue.ts', () => {
-  describe('> DatasetPreviewGlue', () => {
-
-    const initialState = {
-      uiState: {
-        previewingDatasetFiles: []
-      },
-      viewerState: {
-        regionsSelected: []
-      }
-    }
-    beforeEach(() => {
-      actionOnWidgetSpy = jasmine.createSpy('actionOnWidget').and.returnValues(
-        mockActionOnSpyReturnVal0,
-        mockActionOnSpyReturnVal1
-      )
-
-      TestBed.configureTestingModule({
-        imports: [
-          HttpClientTestingModule,
-        ],
-        providers: [
-          DatasetPreviewGlue,
-          provideMockStore({
-            initialState
-          }),
-          NgLayersService
-        ]
-      })
-    })
-
-    afterEach(() => {
-      actionOnWidgetSpy.calls.reset()
-      const ctrl = TestBed.inject(HttpTestingController)
-      ctrl.verify()
-    })
-
-    describe('> #datasetPreviewDisplayed', () => {
-
-      it('> correctly emits true when store changes', () => {
-        const glue = TestBed.inject(DatasetPreviewGlue)
-        const store = TestBed.inject(MockStore)
-
-        const obs = glue.datasetPreviewDisplayed(file1)
-
-        store.setState({
-          uiState: {
-            previewingDatasetFiles: []
-          }
-        })
-        const uiStateSelector = store.overrideSelector(
-          glueSelectorGetUiStatePreviewingFiles,
-          []
-        )
-
-        uiStateSelector.setResult([ file1 ] )
-        store.refreshState()
-        expect(obs).toBeObservable(
-          hot('a', {
-            a: true,
-            b: false
-          })
-        )
-      })
-
-
-      it('> correctly emits false when store changes', () => {
-        const store = TestBed.inject(MockStore)
-
-        const glue = TestBed.inject(DatasetPreviewGlue)
-        store.setState({
-          uiState: {
-            previewingDatasetFiles: [ file2 ]
-          }
-        })
-        const obs = glue.datasetPreviewDisplayed(file1)
-        store.refreshState()
-        
-        expect(obs).toBeObservable(
-          hot('b', {
-            a: true,
-            b: false
-          })
-        )
-      })
-    })
-
-    describe('> #displayDatasetPreview', () => {
-
-      it('> calls dispatch', () => {
-
-        const glue = TestBed.inject(DatasetPreviewGlue)
-        const mockStore = TestBed.inject(MockStore)
-        const dispatchSpy = spyOn(mockStore, 'dispatch').and.callThrough()
-
-        glue.displayDatasetPreview(file1, dataset1 as any)
-        
-        expect(dispatchSpy).toHaveBeenCalled()
-      })
-
-      it('> dispatches glueActionToggleDatasetPreview with the correct filename', () => {
-
-        const glue = TestBed.inject(DatasetPreviewGlue)
-        const mockStore = TestBed.inject(MockStore)
-        const dispatchSpy = spyOn(mockStore, 'dispatch').and.callThrough()
-
-        glue.displayDatasetPreview(file1, dataset1 as any)
-        
-        const args = dispatchSpy.calls.allArgs()
-        const [ action ] = args[0]
-
-        expect(action.type).toEqual(glueActionToggleDatasetPreview.type)
-        expect((action as any).datasetPreviewFile.filename).toEqual(file1.filename)
-      })
-
-      it('> uses datasetId of file if present', () => {
-        
-        const glue = TestBed.inject(DatasetPreviewGlue)
-        const mockStore = TestBed.inject(MockStore)
-        const dispatchSpy = spyOn(mockStore, 'dispatch').and.callThrough()
-
-        glue.displayDatasetPreview(file1, dataset1 as any)
-        
-        const args = dispatchSpy.calls.allArgs()
-        const [ action ] = args[0]
-
-        expect((action as any).datasetPreviewFile.datasetId).toEqual(file1.datasetId)
-      })
-
-      it('> falls back to dataset fullId if datasetId not present on file', () => {
-
-        const glue = TestBed.inject(DatasetPreviewGlue)
-        const mockStore = TestBed.inject(MockStore)
-        const dispatchSpy = spyOn(mockStore, 'dispatch').and.callThrough()
-
-        const { datasetId, ...noDsIdFile1 } = file1
-        glue.displayDatasetPreview(noDsIdFile1 as any, dataset1 as any)
-        
-        const { fullId } = dataset1
-        const { kgId } = getIdObj(fullId)
-
-        const args = dispatchSpy.calls.allArgs()
-        const [ action ] = args[0]
-
-        expect((action as any).datasetPreviewFile.datasetId).toEqual(kgId)
-      })
-    })
-
-    describe('> http interceptor', () => {
-      it('> on no state, does not call', fakeAsync(() => {
-        
-        const store = TestBed.inject(MockStore)
-        const ctrl = TestBed.inject(HttpTestingController)
-        const glue = TestBed.inject(DatasetPreviewGlue)
-
-        store.setState({
-          uiState: {
-            previewingDatasetFiles: []
-          }
-        })
-
-        const { datasetId, filename } = file1
-        // debounce at 100ms
-        tick(200)
-        ctrl.expectNone({})
-
-        discardPeriodicTasks()
-      }))
-      it('> on set state, calls end point to fetch full data', fakeAsync(() => {
-
-        const store = TestBed.inject(MockStore)
-        const ctrl = TestBed.inject(HttpTestingController)
-        const glue = TestBed.inject(DatasetPreviewGlue)
-
-        store.setState({
-          uiState: {
-            previewingDatasetFiles: [ file1 ]
-          }
-        })
-
-        const { datasetId, filename } = file1
-        // debounce at 100ms
-        tick(200)
-
-        const req = ctrl.expectOne(`${DS_PREVIEW_URL}/${encodeURIComponent('minds/core/dataset/v1.0.0')}/${datasetId}/${encodeURIComponent(filename)}`)
-        req.flush(nifti)
-        discardPeriodicTasks()
-      }))
-
-      it('> if returns 404, should be handled gracefully', fakeAsync(() => {
-
-        const ctrl = TestBed.inject(HttpTestingController)
-        const glue = TestBed.inject(DatasetPreviewGlue)
-
-        const { datasetId, filename } = file3
-
-        const obs$ = glue.getDatasetPreviewFromId({ datasetId, filename })
-        let expectedVal = 'defined'
-        obs$.subscribe(val => expectedVal = val)
-        tick(200)
-
-        const req = ctrl.expectOne(`${DS_PREVIEW_URL}/${encodeURIComponent('minds/core/dataset/v1.0.0')}/${encodeURIComponent(datasetId)}/${encodeURIComponent(filename)}`)
-        req.flush(null, { status: 404, statusText: 'Not found' })
-
-        expect(expectedVal).toBeNull()
-        discardPeriodicTasks()
-      }))
-    })
-  })
-
-
-  describe('> datasetPreviewMetaReducer', () => {
-    
-    const obj1: TypeOpenedWidget = {
-      type: EnumWidgetTypes.DATASET_PREVIEW,
-      data: file1
-    }
-
-    const stateEmpty = {
-      uiState: {
-        previewingDatasetFiles: []
-      }
-    } as { uiState: { previewingDatasetFiles: {datasetId: string, filename: string}[] } }
-
-    const stateObj1 = {
-      uiState: {
-        previewingDatasetFiles: [ file1 ]
-      }
-    } as { uiState: { previewingDatasetFiles: {datasetId: string, filename: string}[] } }
-
-    const reducer = jasmine.createSpy('reducer')
-    const metaReducer = datasetPreviewMetaReducer(reducer)
-
-    afterEach(() => {
-      reducer.calls.reset()
-    })
-    describe('> on glueActionAddDatasetPreview', () => {
-      describe('> if preview does not yet exist in state', () => {
-        beforeEach(() => {
-          metaReducer(stateEmpty, glueActionAddDatasetPreview({ datasetPreviewFile: file1 }))
-        })
-
-        it('> expect reducer to be called once', () => {
-          expect(reducer).toHaveBeenCalled()
-          expect(reducer.calls.count()).toEqual(1)
-        })
-
-        it('> expect call sig of reducer call to be correct', () => {
-
-          const [ args ] = reducer.calls.allArgs()
-          expect(args[0]).toEqual(stateEmpty)
-          expect(args[1].type).toEqual(uiActionSetPreviewingDatasetFiles.type)
-          expect(args[1].previewingDatasetFiles).toEqual([ file1 ])
-        })
-      })
-      
-      describe('> if preview already exist in state', () => {
-        beforeEach(() => {
-          metaReducer(stateObj1, glueActionAddDatasetPreview({ datasetPreviewFile: file1 }))
-        })
-        it('> should still call reducer', () => {
-          expect(reducer).toHaveBeenCalled()
-          expect(reducer.calls.count()).toEqual(1)
-        })
-
-        it('> there should now be two previews in dispatched action', () => {
-          
-          const [ args ] = reducer.calls.allArgs()
-          expect(args[0]).toEqual(stateObj1)
-          expect(args[1].type).toEqual(uiActionSetPreviewingDatasetFiles.type)
-          expect(args[1].previewingDatasetFiles).toEqual([ file1, file1 ])
-        })
-      })
-    })
-    describe('> on glueActionRemoveDatasetPreview', () => {
-      it('> removes id as expected', () => {
-        metaReducer(stateObj1, glueActionRemoveDatasetPreview({ datasetPreviewFile: file1 }))
-        expect(reducer).toHaveBeenCalled()
-        expect(reducer.calls.count()).toEqual(1)
-        const [ args ] = reducer.calls.allArgs()
-        expect(args[0]).toEqual(stateObj1)
-        expect(args[1].type).toEqual(uiActionSetPreviewingDatasetFiles.type)
-        expect(args[1].previewingDatasetFiles).toEqual([ ])
-      })
-    })
-  })
-
-  describe('> GlueEffects', () => {
-
-    /**
-     * related to previews
-     */
-    const mockTemplate = {
-      fullId: 'bar'
-    }
-    const mockPreviewFileIds = {
-      datasetId: 'foo',
-      filename: 'bar'
-    }
-    const mockPreviewFileIds2 = {
-      datasetId: 'foo2',
-      filename: 'bar2'
-    }
-    const mockPreviewFileIds3 = {
-      datasetId: 'foo3',
-      filename: 'bar3'
-    }
-    const mockPreviewFileIds4 = {
-      datasetId: 'foo4',
-      filename: 'bar4'
-    }
-    const previewFileNoRefSpace = {
-      name: 'bla bla 4',
-      datasetId: 'foo4',
-      filename: 'bar4'
-    }
-    const fittingMockPreviewFile = {
-      name: 'bla bla2',
-      datasetId: 'foo2',
-      filename: 'bar2',
-      referenceSpaces: [{
-        fullId: 'bar'
-      }]
-    }
-    const mockPreviewFile = {
-      name: 'bla bla',
-      datasetId: 'foo',
-      filename: 'bar',
-      referenceSpaces: [{
-        fullId: 'hello world'
-      }]
-    }
-
-    const defaultState = {
-      viewerState: {
-        templateSelected: null,
-        parcellationSelected: null,
-        regionsSelected: []
-      },
-      uiState: {
-        previewingDatasetFiles: []
-      }
-    }
-
-    const mockGetDatasetPreviewFromId = jasmine.createSpy('getDatasetPreviewFromId')
-
-    beforeEach(() => {
-      TestBed.configureTestingModule({
-        providers: [
-          GlueEffects,
-          provideMockStore({
-            initialState: defaultState
-          }),
-          {
-            provide: GET_KGDS_PREVIEW_INFO_FROM_ID_FILENAME,
-            useValue: mockGetDatasetPreviewFromId
-          }
-        ]
-      })
-      mockGetDatasetPreviewFromId.withArgs(mockPreviewFileIds2).and.returnValue(
-        hot('(a|)', {
-          a: fittingMockPreviewFile
-        })
-      )
-      mockGetDatasetPreviewFromId.withArgs({ datasetId: 'foo', filename: 'bar' }).and.returnValue(
-        hot('(a|)', {
-          a: mockPreviewFile
-        })
-      )
-      mockGetDatasetPreviewFromId.withArgs(mockPreviewFileIds3).and.returnValue(
-        hot('(a|)', {
-          a: null
-        })
-      )
-      mockGetDatasetPreviewFromId.withArgs(mockPreviewFileIds4).and.returnValue(
-        hot('(a|)', {
-          a: previewFileNoRefSpace
-        })
-      )
-    })
-
-    afterEach(() => {
-      mockGetDatasetPreviewFromId.calls.reset()
-    })
-
-    describe('> regionTemplateParcChange$', () => {
-
-      const copiedState0 = JSON.parse(JSON.stringify(defaultState))
-      copiedState0.viewerState.regionsSelected = [{ name: 'coffee' }]
-      copiedState0.viewerState.parcellationSelected = { name: 'chicken' }
-      copiedState0.viewerState.templateSelected = { name: 'spinach' }
-
-      const generateTest = (m1, m2) => {
-
-        const mockStore = TestBed.inject(MockStore)
-        mockStore.setState(copiedState0)
-        const glueEffects = TestBed.inject(GlueEffects)
-        /**
-         * couldn't get jasmine-marble to coopoerate
-         * TODO test proper with jasmine marble (?)
-         */
-        let numOfEmit = 0
-        const sub = glueEffects.regionTemplateParcChange$.subscribe(() => {
-          numOfEmit += 1
-        })
-
-        const copiedState1 = JSON.parse(JSON.stringify(copiedState0))
-        m1(copiedState1)
-        mockStore.setState(copiedState1)
-        expect(numOfEmit).toEqual(1)
-
-        const copiedState2 = JSON.parse(JSON.stringify(copiedState0))
-        m2(copiedState2)
-        mockStore.setState(copiedState2)
-        expect(numOfEmit).toEqual(2)
-
-        sub.unsubscribe()
-      }
-      
-      it('> on change of region, should emit', () => {
-        generateTest(
-          copiedState1 => copiedState1.viewerState.regionsSelected = [{ name: 'cake' }],
-          copiedState2 => copiedState2.viewerState.regionsSelected = [{ name: 't bone' }]
-        )
-      })
-
-      it('> on change of parcellation, should emit', () => {
-        generateTest(
-          copiedState1 => copiedState1.viewerState.parcellationSelected = { name: 'pizza' },
-          copiedState2 => copiedState2.viewerState.parcellationSelected = { name: 'pizza on pineapple' }
-        )
-      })
-
-      it('> on change of template, should emit', () => {
-        generateTest(
-          copiedState1 => copiedState1.viewerState.templateSelected = { name: 'calzone' },
-          copiedState2 => copiedState2.viewerState.templateSelected = { name: 'calzone on pineapple' }
-        )
-      })
-    })
-    
-  
-    describe('> unsuitablePreviews$', () => {
-
-      it('> calls injected getDatasetPreviewFromId', () => {
-        const mockStore = TestBed.inject(MockStore)
-        mockStore.overrideSelector(viewerStateSelectedTemplateSelector, mockTemplate)
-        mockStore.overrideSelector(uiStatePreviewingDatasetFilesSelector, [mockPreviewFileIds2])
-
-        const glueEffects = TestBed.inject(GlueEffects)
-        expect(glueEffects.unsuitablePreviews$).toBeObservable(
-          hot('')
-        )
-        /**
-         * calling twice, once to check if the dataset preview can be retrieved, the other to check the referenceSpace
-         */
-        expect(mockGetDatasetPreviewFromId).toHaveBeenCalledTimes(2)
-        expect(mockGetDatasetPreviewFromId).toHaveBeenCalledWith(mockPreviewFileIds2)
-      })
-
-      it('> if getDatasetPreviewFromId throws in event stream, handles gracefully', () => {
-        const mockStore = TestBed.inject(MockStore)
-        mockStore.overrideSelector(viewerStateSelectedTemplateSelector, mockTemplate)
-        mockStore.overrideSelector(uiStatePreviewingDatasetFilesSelector, [mockPreviewFileIds3])
-
-        const glueEffects = TestBed.inject(GlueEffects)
-        
-        expect(glueEffects.unsuitablePreviews$).toBeObservable(
-          hot('a', {
-            a: [ mockPreviewFileIds3 ]
-          })
-        )
-      })
-
-      describe('> filtering out dataset previews that do not satisfy reference space requirements', () => {
-        it('> if reference spaces does not match the selected reference template, will emit', () => {
-          const mockStore = TestBed.inject(MockStore)
-
-          mockStore.overrideSelector(viewerStateSelectedTemplateSelector, mockTemplate)
-          mockStore.overrideSelector(uiStatePreviewingDatasetFilesSelector, [mockPreviewFileIds])
-          const glueEffects = TestBed.inject(GlueEffects)
-          expect(glueEffects.unsuitablePreviews$).toBeObservable(
-            hot('a', {
-              a: [ mockPreviewFile ]
-            })
-          )
-        })
-      })
-
-      describe('> keeping dataset previews that satisfy reference space criteria', () => {
-        it('> if ref space is undefined, keep preview', () => {
-
-          const mockStore = TestBed.inject(MockStore)
-          mockStore.overrideSelector(viewerStateSelectedTemplateSelector, mockTemplate)
-          mockStore.overrideSelector(uiStatePreviewingDatasetFilesSelector, [mockPreviewFileIds4])
-          const glueEffects = TestBed.inject(GlueEffects)
-          expect(glueEffects.unsuitablePreviews$).toBeObservable(
-            hot('')
-          )
-        })
-
-        it('> if ref space is defined, and matches, keep preview', () => {
-
-          const mockStore = TestBed.inject(MockStore)
-          mockStore.overrideSelector(viewerStateSelectedTemplateSelector, mockTemplate)
-          mockStore.overrideSelector(uiStatePreviewingDatasetFilesSelector, [mockPreviewFileIds2])
-          const glueEffects = TestBed.inject(GlueEffects)
-          expect(glueEffects.unsuitablePreviews$).toBeObservable(
-            hot('')
-          )
-        })
-      })  
-    
-    })
-
-    describe('> uiRemoveUnsuitablePreviews$', () => {
-      it('> emits whenever unsuitablePreviews$ emits', () => {
-        const mockStore = TestBed.inject(MockStore)
-        mockStore.overrideSelector(viewerStateSelectedTemplateSelector, mockTemplate)
-        mockStore.overrideSelector(uiStatePreviewingDatasetFilesSelector, [mockPreviewFileIds])
-        const glueEffects = TestBed.inject(GlueEffects)
-        expect(glueEffects.uiRemoveUnsuitablePreviews$).toBeObservable(
-          hot('a', {
-            a: generalActionError({
-              message: `Dataset previews ${mockPreviewFile.name} cannot be displayed.`
-            })
-          })
-        )
-      })
-    })
-    
-    describe('> filterDatasetPreviewByTemplateSelected$', () => {
-
-      it('> remove 1 preview datasetfile depending on unsuitablepreview$', () => {
-        const mockStore = TestBed.inject(MockStore)
-
-        mockStore.overrideSelector(viewerStateSelectedTemplateSelector, mockTemplate)
-        mockStore.overrideSelector(uiStatePreviewingDatasetFilesSelector, [mockPreviewFileIds])
-        const glueEffects = TestBed.inject(GlueEffects)
-        expect(glueEffects.filterDatasetPreviewByTemplateSelected$).toBeObservable(
-          hot('a', {
-            a: uiActionSetPreviewingDatasetFiles({
-              previewingDatasetFiles: [  ]
-            })
-          })
-        )
-
-      })
-      it('> remove 1 preview datasetfile (get preview info fail) depending on unsuitablepreview$', () => {
-        const mockStore = TestBed.inject(MockStore)
-
-        mockStore.overrideSelector(viewerStateSelectedTemplateSelector, mockTemplate)
-        mockStore.overrideSelector(uiStatePreviewingDatasetFilesSelector, [mockPreviewFileIds3])
-        const glueEffects = TestBed.inject(GlueEffects)
-        expect(glueEffects.filterDatasetPreviewByTemplateSelected$).toBeObservable(
-          hot('a', {
-            a: uiActionSetPreviewingDatasetFiles({
-              previewingDatasetFiles: [  ]
-            })
-          })
-        )
-
-      })
-      it('> remove 2 preview datasetfile depending on unsuitablepreview$', () => {
-        const mockStore = TestBed.inject(MockStore)
-
-        mockStore.overrideSelector(viewerStateSelectedTemplateSelector, mockTemplate)
-        mockStore.overrideSelector(uiStatePreviewingDatasetFilesSelector, [mockPreviewFileIds, mockPreviewFileIds2, mockPreviewFileIds4])
-        const glueEffects = TestBed.inject(GlueEffects)
-        expect(glueEffects.filterDatasetPreviewByTemplateSelected$).toBeObservable(
-          hot('a', {
-            a: uiActionSetPreviewingDatasetFiles({
-              previewingDatasetFiles: [ mockPreviewFileIds2, mockPreviewFileIds4 ]
-            })
-          })
-        )
-
-      })
-      
-    })
-
-  })
 
   describe('> ClickInterceptorService', () => {
     /**
diff --git a/src/glue.ts b/src/glue.ts
index 027858745e6ac5223431e586aa6bd016e1ff3611..89b97a7f5647a8ff7e99a7bcf1dfa52837ab9c85 100644
--- a/src/glue.ts
+++ b/src/glue.ts
@@ -1,447 +1,6 @@
-import { uiActionSetPreviewingDatasetFiles, IDatasetPreviewData, uiStatePreviewingDatasetFilesSelector } from "./services/state/uiState.store.helper"
-import { OnDestroy, Injectable, Inject, InjectionToken } from "@angular/core"
-import { DatasetPreview, determinePreviewFileType, EnumPreviewFileTypes, IKgDataEntry, getKgSchemaIdFromFullId, GET_KGDS_PREVIEW_INFO_FROM_ID_FILENAME } from "./databrowser.fallback"
-import { Subscription, Observable, forkJoin, of, merge, combineLatest } from "rxjs"
-import { select, Store, ActionReducer, createAction, props, createSelector, Action } from "@ngrx/store"
-import { startWith, map, shareReplay, pairwise, debounceTime, distinctUntilChanged, tap, switchMap, withLatestFrom, mapTo, switchMapTo, filter, skip, catchError } from "rxjs/operators"
-import { getIdObj } from 'common/util'
-import { MatDialogRef } from "@angular/material/dialog"
-import { HttpClient } from "@angular/common/http"
-import { DS_PREVIEW_URL } from 'src/util/constants'
-import { ngViewerActionAddNgLayer, ngViewerActionRemoveNgLayer } from "./services/state/ngViewerState.store.helper"
-import { ARIA_LABELS } from 'common/constants'
-import { Effect } from "@ngrx/effects"
-import { viewerStateSelectedRegionsSelector, viewerStateSelectedTemplateSelector, viewerStateSelectedParcellationSelector } from "./services/state/viewerState/selectors"
-import { ngViewerActionClearView } from './services/state/ngViewerState/actions'
-import { generalActionError } from "./services/stateStore.helper"
+import { Injectable } from "@angular/core"
 import { RegDeregController } from "./util/regDereg.base"
 
-const prvFilterNull = ({ prvToDismiss, prvToShow }) => ({
-  prvToDismiss: prvToDismiss.filter(v => !!v),
-  prvToShow: prvToShow.filter(v => !!v),
-})
-
-export const glueActionToggleDatasetPreview = createAction(
-  '[glue] toggleDatasetPreview',
-  props<{ datasetPreviewFile: IDatasetPreviewData }>()
-)
-
-export const glueActionAddDatasetPreview = createAction(
-  '[glue] addDatasetPreview',
-  props<{ datasetPreviewFile: IDatasetPreviewData }>()
-)
-
-export const glueActionRemoveDatasetPreview = createAction(
-  '[glue] removeDatasetPreview',
-  props<{ datasetPreviewFile: IDatasetPreviewData }>()
-)
-
-export const glueSelectorGetUiStatePreviewingFiles = createSelector(
-  (state: any) => state.uiState,
-  uiState => uiState.previewingDatasetFiles
-)
-
-export interface IDatasetPreviewGlue{
-  datasetPreviewDisplayed(file: DatasetPreview, dataset: IKgDataEntry): Observable<boolean>
-  displayDatasetPreview(previewFile: DatasetPreview, dataset: IKgDataEntry): void
-}
-
-@Injectable({
-  providedIn: 'root'
-})
-
-export class GlueEffects {
-  
-  public regionTemplateParcChange$ = merge(
-    this.store$.pipe(
-      select(viewerStateSelectedRegionsSelector),
-      map(rs => (rs || []).map(r => r['name']).sort().join(',')),
-      distinctUntilChanged(),
-      skip(1),
-    ),
-    this.store$.pipe(
-      select(viewerStateSelectedTemplateSelector),
-      map(tmpl => tmpl
-        ? tmpl['@id'] || tmpl['name']
-        : null),
-      distinctUntilChanged(),
-      skip(1)
-    ),
-    this.store$.pipe(
-      select(viewerStateSelectedParcellationSelector),
-      map(parc => parc
-        ? parc['@id'] || parc['name']
-        : null),
-      distinctUntilChanged(),
-      skip(1)
-    )
-  ).pipe(
-    mapTo(true)
-  )
-
-  @Effect()
-  resetDatasetPreview$: Observable<any> = this.store$.pipe(
-    select(uiStatePreviewingDatasetFilesSelector),
-    distinctUntilChanged(),
-    filter(previews => previews?.length > 0),
-    switchMapTo(this.regionTemplateParcChange$)
-  ).pipe(
-    mapTo(uiActionSetPreviewingDatasetFiles({
-      previewingDatasetFiles: []
-    }))
-  )
-
-  unsuitablePreviews$: Observable<any> = merge(
-    /**
-     * filter out the dataset previews, whose details cannot be fetchd from getdatasetPreviewFromId method
-     */
-
-    this.store$.pipe(
-      select(uiStatePreviewingDatasetFilesSelector),
-      switchMap(previews => 
-        forkJoin(
-          previews.map(prev => this.getDatasetPreviewFromId(prev).pipe(
-            // filter out the null's 
-            filter(val => !val),
-            mapTo(prev)
-          ))
-        ).pipe(
-          filter(previewFiles => previewFiles.length > 0)
-        )
-      )
-    ),
-    /**
-     * filter out the dataset previews, whose details can be fetched from getDatasetPreviewFromId method
-     */
-    combineLatest([
-      this.store$.pipe(
-        select(viewerStateSelectedTemplateSelector)
-      ),
-      this.store$.pipe(
-        select(uiStatePreviewingDatasetFilesSelector),
-        switchMap(previews => 
-          forkJoin(
-            previews.map(prev => this.getDatasetPreviewFromId(prev).pipe(
-              filter(val => !!val)
-            ))
-          ).pipe(
-            // filter out the null's 
-            filter(previewFiles => previewFiles.length > 0)
-          )
-        ),
-      )
-    ]).pipe(
-      map(([ templateSelected, previewFiles ]) => 
-        previewFiles.filter(({ referenceSpaces }) => 
-          // if referenceSpaces of the dataset preview is undefined, assume it is suitable for all reference spaces
-          (!referenceSpaces)
-            ? false
-            : !referenceSpaces.some(({ fullId }) => fullId === '*' || fullId === templateSelected.fullId)
-        )
-      ),
-    ) 
-  ).pipe(
-    filter(arr => arr.length > 0),
-    shareReplay(1),
-  )
-
-  @Effect()
-  uiRemoveUnsuitablePreviews$: Observable<any> = this.unsuitablePreviews$.pipe(
-    map(previews => generalActionError({
-      message: `Dataset previews ${previews.map(v => v.name)} cannot be displayed.`
-    }))
-  )
-
-  @Effect()
-  filterDatasetPreviewByTemplateSelected$: Observable<any> = this.unsuitablePreviews$.pipe(
-    withLatestFrom(
-      this.store$.pipe(
-        select(uiStatePreviewingDatasetFilesSelector),
-      )
-    ),
-    map(([ unsuitablePreviews, previewFiles ]) => uiActionSetPreviewingDatasetFiles({
-      previewingDatasetFiles: previewFiles.filter(
-        ({ datasetId: dsId, filename: fName }) => !unsuitablePreviews.some(
-          ({ datasetId, filename }) => datasetId === dsId && fName === filename
-        )
-      )
-    }))
-  )
-
-  @Effect()
-  resetConnectivityMode: Observable<any> = this.store$.pipe(
-    select(viewerStateSelectedRegionsSelector),
-    pairwise(),
-    filter(([o, n]) => o.length > 0 && n.length === 0),
-    mapTo(
-      ngViewerActionClearView({
-        payload: {
-          'Connectivity': false
-        }
-      })
-    )
-  )
-
-  constructor(
-    private store$: Store<any>,
-    @Inject(GET_KGDS_PREVIEW_INFO_FROM_ID_FILENAME) private getDatasetPreviewFromId: (arg) => Observable<any|null>
-  ){
-  }
-}
-
-@Injectable({
-  providedIn: 'root'
-})
-
-export class DatasetPreviewGlue implements IDatasetPreviewGlue, OnDestroy{
-  
-  static readonly DEFAULT_DIALOG_OPTION = {
-    ariaLabel: ARIA_LABELS.DATASET_FILE_PREVIEW,
-    hasBackdrop: false,
-    disableClose: true,
-    autoFocus: false,
-    panelClass: 'mat-card-sm',
-    height: '50vh',
-    width: '350px',
-    position: {
-      left: '5px'
-    },
-  }
-
-  static GetDatasetPreviewId(data: IDatasetPreviewData ){
-    const { datasetSchema = 'minds/core/dataset/v1.0.0', datasetId, filename } = data
-    return `${datasetSchema}/${datasetId}:${filename}`
-  }
-
-  static GetDatasetPreviewFromId(id: string): IDatasetPreviewData{
-    const re = /([a-f0-9-]+):(.+)$/.exec(id)
-    if (!re) throw new Error(`id cannot be decoded: ${id}`)
-    return { datasetId: re[1], filename: re[2] }
-  }
-
-  static PreviewFileIsInCorrectSpace(previewFile, templateSelected): boolean{
-
-    const re = getKgSchemaIdFromFullId(
-      (templateSelected && templateSelected.fullId) || ''
-    )
-    const templateId = re && re[0] && `${re[0]}/${re[1]}`
-    const { referenceSpaces } = previewFile
-    return referenceSpaces.findIndex(({ fullId }) => fullId === '*' || fullId === templateId) >= 0
-  }
-
-  private subscriptions: Subscription[] = []
-  private openedPreviewMap = new Map<string, {id: string, matDialogRef: MatDialogRef<any>}>()
-
-  private previewingDatasetFiles$: Observable<IDatasetPreviewData[]> = this.store$.pipe(
-    select(glueSelectorGetUiStatePreviewingFiles),
-    startWith([]),
-    shareReplay(1),
-  )
-
-  public _volumePreview$ = this.previewingDatasetFiles$.pipe(
-    switchMap(arr => arr.length > 0
-      ? forkJoin(arr.map(v => this.getDatasetPreviewFromId(v)))
-      : of([])),
-    map(arr => arr.filter(v => determinePreviewFileType(v) === EnumPreviewFileTypes.VOLUMES))
-  )
-
-  private diffPreviewingDatasetFiles$= this.previewingDatasetFiles$.pipe(
-    debounceTime(100),
-    startWith([] as IDatasetPreviewData[]),
-    pairwise(),
-    map(([ oldPreviewWidgets, newPreviewWidgets ]) => {
-      const oldPrvWgtIdSet = new Set(oldPreviewWidgets.map(DatasetPreviewGlue.GetDatasetPreviewId))
-      const newPrvWgtIdSet = new Set(newPreviewWidgets.map(DatasetPreviewGlue.GetDatasetPreviewId))
-      
-      const prvToShow = newPreviewWidgets.filter(obj => !oldPrvWgtIdSet.has(DatasetPreviewGlue.GetDatasetPreviewId(obj)))
-      const prvToDismiss = oldPreviewWidgets.filter(obj => !newPrvWgtIdSet.has(DatasetPreviewGlue.GetDatasetPreviewId(obj)))
-
-      return { prvToShow, prvToDismiss }
-    }),
-  )
-
-  ngOnDestroy(){
-    while(this.subscriptions.length > 0){
-      this.subscriptions.pop().unsubscribe()
-    }
-  }
-
-  private sharedDiffObs$ = this.diffPreviewingDatasetFiles$.pipe(
-    switchMap(({ prvToShow, prvToDismiss }) => {
-      return forkJoin({
-        prvToShow: prvToShow.length > 0 
-          ? forkJoin(prvToShow.map(val => this.getDatasetPreviewFromId(val)))
-          : of([]),
-        prvToDismiss: prvToDismiss.length > 0 
-          ? forkJoin(prvToDismiss.map(val => this.getDatasetPreviewFromId(val)))
-          : of([])
-      })
-    }),
-    map(prvFilterNull),
-    shareReplay(1)
-  )
-
-  private getDiffDatasetFilesPreviews(filterFn: (prv: any) => boolean = () => true): Observable<{prvToShow: any[], prvToDismiss: any[]}>{
-    return this.sharedDiffObs$.pipe(
-      map(({ prvToDismiss, prvToShow }) => {
-        return {
-          prvToShow: prvToShow.filter(filterFn),
-          prvToDismiss: prvToDismiss.filter(filterFn),
-        }
-      })
-    )
-  }
-
-  private fetchedDatasetPreviewCache: Map<string, Observable<any>> = new Map()
-  public getDatasetPreviewFromId({ datasetSchema = 'minds/core/dataset/v1.0.0', datasetId, filename }: IDatasetPreviewData){
-    const dsPrvId = DatasetPreviewGlue.GetDatasetPreviewId({ datasetSchema, datasetId, filename })
-    const cachedPrv$ = this.fetchedDatasetPreviewCache.get(dsPrvId)
-    const filteredDsId = /[a-f0-9-]+$/.exec(datasetId)
-    if (cachedPrv$) return cachedPrv$
-    const url = `${DS_PREVIEW_URL}/${encodeURIComponent(datasetSchema)}/${filteredDsId}/${encodeURIComponent(filename)}`
-    const filedetail$ = this.http.get(url, { responseType: 'json' }).pipe(
-      map(json => {
-        return {
-          ...json,
-          filename,
-          datasetId,
-          datasetSchema
-        }
-      }),
-      catchError((_err, _obs) => of(null))
-    )
-    this.fetchedDatasetPreviewCache.set(dsPrvId, filedetail$)
-    return filedetail$.pipe(
-      tap(val => this.fetchedDatasetPreviewCache.set(dsPrvId, of(val)))
-    )
-  }
-
-  constructor(
-    private store$: Store<any>,
-    private http: HttpClient,
-  ){
-
-    // managing dataset previews without UI
-
-    // managing registeredVolumes
-    this.subscriptions.push(
-      this.getDiffDatasetFilesPreviews(
-        dsPrv => determinePreviewFileType(dsPrv) === EnumPreviewFileTypes.VOLUMES
-      ).pipe(
-        withLatestFrom(this.store$.pipe(
-          select(state => state?.viewerState?.templateSelected || null),
-          distinctUntilChanged(),
-        ))
-      ).subscribe(([ { prvToShow, prvToDismiss }, templateSelected ]) => {
-        const filterdPrvs = prvToShow.filter(prv => DatasetPreviewGlue.PreviewFileIsInCorrectSpace(prv, templateSelected))
-        for (const prv of filterdPrvs) {
-          const { volumes } = prv['data']['iav-registered-volumes']
-          this.store$.dispatch(ngViewerActionAddNgLayer({
-            layer: volumes
-          }))
-        }
-
-        for (const prv of prvToDismiss) {
-          const { volumes } = prv['data']['iav-registered-volumes']
-          this.store$.dispatch(ngViewerActionRemoveNgLayer({
-            layer: volumes
-          }))
-        }
-      })
-    )
-  }
-  
-  public datasetPreviewDisplayed(file: DatasetPreview, dataset?: IKgDataEntry){
-    if (!file) return of(false)
-    return this.previewingDatasetFiles$.pipe(
-      map(datasetPreviews => {
-        const { filename, datasetId } = file
-        const { fullId } = dataset || {}
-        const { kgId } = getIdObj(fullId) || {}
-
-        return datasetPreviews.findIndex(({ datasetId: dsId, filename: fName }) => {
-          return (datasetId || kgId) === dsId && fName === filename
-        }) >= 0
-      })
-    )
-  }
-
-  public displayDatasetPreview(previewFile: DatasetPreview, dataset: IKgDataEntry){
-    const { filename, datasetId } = previewFile
-    const { fullId } = dataset
-    const { kgId, kgSchema } = getIdObj(fullId)
-
-    const datasetPreviewFile = {
-      datasetSchema: kgSchema,
-      datasetId: datasetId || kgId,
-      filename
-    }
-
-    this.store$.dispatch(glueActionToggleDatasetPreview({ datasetPreviewFile }))
-  }
-}
-
-export function datasetPreviewMetaReducer(reducer: ActionReducer<any>): ActionReducer<any>{
-  return function (state, action) {
-    switch(action.type) {
-    case glueActionToggleDatasetPreview.type: {
-
-      const previewingDatasetFiles = (state?.uiState?.previewingDatasetFiles || []) as IDatasetPreviewData[]
-      const ids = new Set(previewingDatasetFiles.map(DatasetPreviewGlue.GetDatasetPreviewId))
-      const { datasetPreviewFile } = action as Action & { datasetPreviewFile: IDatasetPreviewData }
-      const newId = DatasetPreviewGlue.GetDatasetPreviewId(datasetPreviewFile)
-      if (ids.has(newId)) {
-        const removeId = DatasetPreviewGlue.GetDatasetPreviewId(datasetPreviewFile)
-        const filteredOpenedWidgets = previewingDatasetFiles.filter(obj => {
-          const id = DatasetPreviewGlue.GetDatasetPreviewId(obj)
-          return id !== removeId
-        })
-        return reducer(state, uiActionSetPreviewingDatasetFiles({ previewingDatasetFiles: filteredOpenedWidgets }))
-      } else {
-        return reducer(state, uiActionSetPreviewingDatasetFiles({ previewingDatasetFiles: [ ...previewingDatasetFiles, datasetPreviewFile ] }))
-      }
-    }
-    case glueActionAddDatasetPreview.type: {
-      const previewingDatasetFiles = (state?.uiState?.previewingDatasetFiles || []) as IDatasetPreviewData[]
-      const { datasetPreviewFile } = action as Action & { datasetPreviewFile: IDatasetPreviewData }
-      return reducer(state, uiActionSetPreviewingDatasetFiles({ previewingDatasetFiles: [ ...previewingDatasetFiles, datasetPreviewFile] }))
-    }
-    case glueActionRemoveDatasetPreview.type: {
-      const previewingDatasetFiles = (state?.uiState?.previewingDatasetFiles || []) as IDatasetPreviewData[]
-      const { datasetPreviewFile } = action as any
-
-      const removeId = DatasetPreviewGlue.GetDatasetPreviewId(datasetPreviewFile)
-      const filteredOpenedWidgets = previewingDatasetFiles.filter(obj => {
-        const id = DatasetPreviewGlue.GetDatasetPreviewId(obj)
-        return id !== removeId
-      })
-      return reducer(state, uiActionSetPreviewingDatasetFiles({ previewingDatasetFiles: filteredOpenedWidgets }))
-    }
-    default: return reducer(state, action)
-    }
-  }
-}
-
-export const SAVE_USER_DATA = new InjectionToken<TypeSaveUserData>('SAVE_USER_DATA')
-
-type TypeSaveUserData = (key: string, value: string) => void
-
-export const gluActionFavDataset = createAction(
-  '[glue] favDataset',
-  props<{dataentry: Partial<IKgDataEntry>}>()
-)
-export const gluActionUnfavDataset = createAction(
-  '[glue] favDataset',
-  props<{dataentry: Partial<IKgDataEntry>}>()
-)
-export const gluActionToggleDataset = createAction(
-  '[glue] favDataset',
-  props<{dataentry: Partial<IKgDataEntry>}>()
-)
-export const gluActionSetFavDataset = createAction(
-  '[glue] favDataset',
-  props<{dataentries: Partial<IKgDataEntry>[]}>()
-)
 
 @Injectable({
   providedIn: 'root'
@@ -467,28 +26,3 @@ export class ClickInterceptorService extends RegDeregController<any, boolean>{
     // called when the call has not been intercepted
   }
 }
-
-export type _TPLIVal = {
-  name: string
-  filename: string
-  datasetSchema: string
-  datasetId: string
-  data: {
-    'iav-registered-volumes': {
-      volumes: {
-        name: string
-        source: string
-        shader: string
-        transform: any
-        opacity: string
-      }[]
-    }
-  }
-  referenceSpaces: {
-    name: string
-    fullId: string
-  }[]
-  mimetype: 'application/json'
-}
-
-export const _PLI_VOLUME_INJ_TOKEN = new InjectionToken<Observable<_TPLIVal[]>>('_PLI_VOLUME_INJ_TOKEN')
diff --git a/src/index.html b/src/index.html
index d1c2ffdeaeb72510b344f3053835e93ac1a2e1e9..56e924d2b7181e2344f5bb912174a8013a52bde2 100644
--- a/src/index.html
+++ b/src/index.html
@@ -13,10 +13,10 @@
   <link rel="icon" type="image/png" href="assets/favicons/favicon-128-light.png"/>
   <script src="extra_js.js"></script>
   <script src="https://unpkg.com/kg-dataset-previewer@1.2.0/dist/kg-dataset-previewer/kg-dataset-previewer.js" defer></script>
-  <script src="https://unpkg.com/three-surfer@0.0.10/dist/bundle.js" defer></script>
+  <script src="https://unpkg.com/three-surfer@0.0.11/dist/bundle.js" defer></script>
   <script type="module" src="https://unpkg.com/ng-layer-tune@0.0.6/dist/ng-layer-tune/ng-layer-tune.esm.js"></script>
-  
-  <title>Interactive Atlas Viewer</title>
+  <script type="module" src="https://unpkg.com/hbp-connectivity-component@0.6.2/dist/connectivity-component/connectivity-component.js" ></script>
+  <title>Siibra Explorer</title>
 </head>
 <body>
   <atlas-viewer>
diff --git a/src/keyframesModule/service.ts b/src/keyframesModule/service.ts
index b807aa15ba8414c823bf48ab132fb01c29c19590..98575c6b3fc1f6de5439791eaf01d0609c580d92 100644
--- a/src/keyframesModule/service.ts
+++ b/src/keyframesModule/service.ts
@@ -1,11 +1,8 @@
 import { Injectable, OnDestroy } from "@angular/core";
-import { MatDialog } from "@angular/material/dialog";
 import { Store } from "@ngrx/store";
 import { BehaviorSubject, Subscription } from "rxjs";
 import { distinctUntilChanged } from "rxjs/operators";
-import { viewerStateSetViewerMode } from "src/services/state/viewerState.store.helper";
-import { KEYFRAME_VIEWMODE } from "./constants";
-import { KeyFrameCtrlCmp } from "./keyframeCtrl/keyframeCtrl.component";
+import { actions } from "src/state/atlasSelection";
 
 @Injectable()
 export class KeyFrameService implements OnDestroy {
@@ -42,8 +39,10 @@ export class KeyFrameService implements OnDestroy {
 
       // TODO enable side bar when ready
       this.store.dispatch(
-        viewerStateSetViewerMode({
-          payload: flag && KEYFRAME_VIEWMODE
+        actions.setViewerMode({
+          viewerMode: flag
+            ? "key frame"
+            : null
         })
       )
     })
diff --git a/src/layouts/currentLayout/currentLayout.component.ts b/src/layouts/currentLayout/currentLayout.component.ts
index be31bf00347ec1bee75eba254c3ff866b7ac4304..0e6a4b56db66d3afbd888e7bb66a9bace4a0a8a5 100644
--- a/src/layouts/currentLayout/currentLayout.component.ts
+++ b/src/layouts/currentLayout/currentLayout.component.ts
@@ -1,9 +1,5 @@
-import { Component } from "@angular/core";
-import { select, Store } from "@ngrx/store";
-import { Observable } from "rxjs";
-import { startWith } from "rxjs/operators";
-import { SUPPORTED_PANEL_MODES } from "src/services/state/ngViewerState.store";
-import { ngViewerSelectorPanelMode } from "src/services/state/ngViewerState/selectors";
+import { ChangeDetectionStrategy, Component, Input } from "@angular/core";
+import { userInterface } from "src/state"
 
 @Component({
   selector: 'current-layout',
@@ -11,19 +7,20 @@ import { ngViewerSelectorPanelMode } from "src/services/state/ngViewerState/sele
   styleUrls: [
     './currentLayout.style.css',
   ],
+  changeDetection: ChangeDetectionStrategy.OnPush,
 })
 
 export class CurrentLayout {
 
-  public supportedPanelModes = SUPPORTED_PANEL_MODES
-  public panelMode$: Observable<string>
-
-  constructor(
-    private store$: Store<any>,
-  ) {
-    this.panelMode$ = this.store$.pipe(
-      select(ngViewerSelectorPanelMode),
-      startWith(SUPPORTED_PANEL_MODES[0]),
-    )
+  @Input('current-layout-use-layout')
+  public useLayout: userInterface.PanelMode = "FOUR_PANEL"
+  
+  public panelModes: Record<string, userInterface.PanelMode> = {
+    FOUR_PANEL: "FOUR_PANEL",
+    H_ONE_THREE: "H_ONE_THREE",
+    SINGLE_PANEL: "SINGLE_PANEL",
+    V_ONE_THREE: "V_ONE_THREE"
   }
+
+  public panelMode: userInterface.PanelMode = null
 }
diff --git a/src/layouts/currentLayout/currentLayout.template.html b/src/layouts/currentLayout/currentLayout.template.html
index 9575932ed1de52ea23341d6570a48b32225c9160..1079302ee3c7ae96177427ef21ad262d96f1a2ad 100644
--- a/src/layouts/currentLayout/currentLayout.template.html
+++ b/src/layouts/currentLayout/currentLayout.template.html
@@ -1,7 +1,7 @@
-<ng-container [ngSwitch]="panelMode$ | async">
+<ng-container [ngSwitch]="useLayout">
   <layout-four-panel
-    *ngSwitchCase="supportedPanelModes[0]"
-    class="d-block w-100 h-100">
+    *ngSwitchCase="panelModes.FOUR_PANEL"
+    class="d-block sxplr-w-100 sxplr-h-100">
     <ng-container cell-i>
       <ng-content *ngTemplateOutlet="celli"></ng-content>
     </ng-container>
@@ -16,8 +16,8 @@
     </ng-container>
   </layout-four-panel>
   <layout-horizontal-one-three
-    *ngSwitchCase="supportedPanelModes[1]"
-    class="d-block w-100 h-100">
+    *ngSwitchCase="panelModes.H_ONE_THREE"
+    class="d-block sxplr-w-100 sxplr-h-100">
     <ng-container cell-i>
       <ng-content *ngTemplateOutlet="celli"></ng-content>
     </ng-container>
@@ -32,8 +32,8 @@
     </ng-container>
   </layout-horizontal-one-three>
   <layout-vertical-one-three
-    *ngSwitchCase="supportedPanelModes[2]"
-    class="d-block w-100 h-100">
+    *ngSwitchCase="panelModes.V_ONE_THREE"
+    class="d-block sxplr-w-100 sxplr-h-100">
     <ng-container cell-i>
       <ng-content *ngTemplateOutlet="celli"></ng-content>
     </ng-container>
@@ -48,8 +48,8 @@
     </ng-container>
   </layout-vertical-one-three>
   <layout-single-panel
-    *ngSwitchCase="supportedPanelModes[3]"
-    class="d-block w-100 h-100">
+    *ngSwitchCase="panelModes.SINGLE_PANEL"
+    class="d-block sxplr-w-100 sxplr-h-100">
     <ng-container cell-i>
       <ng-content *ngTemplateOutlet="celli"></ng-content>
     </ng-container>
@@ -64,7 +64,7 @@
     </ng-container>
   </layout-single-panel>
   <div *ngSwitchDefault>
-    A panel mode which I have never seen before ...
+    A panel mode which I have never seen before ... {{ useLayout }}
   </div>
 </ng-container>
 
diff --git a/src/layouts/floating/floating.component.ts b/src/layouts/floating/floating.component.ts
deleted file mode 100644
index bb8ec5176576e4d6c0b2c06db51aea133237a5b9..0000000000000000000000000000000000000000
--- a/src/layouts/floating/floating.component.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { Component, HostBinding, Input } from "@angular/core";
-
-@Component({
-  selector : 'layout-floating-container',
-  templateUrl : './floating.template.html',
-  styleUrls : [
-    `./floating.style.css`,
-  ],
-})
-
-export class FloatingLayoutContainer {
-  @HostBinding('style.z-index')
-  @Input()
-  public zIndex: number = 5
-}
diff --git a/src/layouts/floating/floating.style.css b/src/layouts/floating/floating.style.css
deleted file mode 100644
index 0cb15ee43e183a4d6bb193608840da0b4886934f..0000000000000000000000000000000000000000
--- a/src/layouts/floating/floating.style.css
+++ /dev/null
@@ -1,15 +0,0 @@
-:host
-{
-  position: absolute;
-  top:0;
-  left:0;
-  width:100%;
-  height:100%;
-  display:block;
-  pointer-events: none;
-}
-
-:host *
-{
-  pointer-events: all;
-}
\ No newline at end of file
diff --git a/src/layouts/floating/floating.template.html b/src/layouts/floating/floating.template.html
deleted file mode 100644
index d7b4509bdd7e7b9ac0c84063fdc18c606fb96a9f..0000000000000000000000000000000000000000
--- a/src/layouts/floating/floating.template.html
+++ /dev/null
@@ -1,2 +0,0 @@
-<ng-content>
-</ng-content>
\ No newline at end of file
diff --git a/src/layouts/fourCorners/fourCorners.component.ts b/src/layouts/fourCorners/fourCorners.component.ts
index 6bae516c960e2c6b3e7344b8bb46274a310d1c95..4591f7d869e27d448e144876e498b7626d7716fa 100644
--- a/src/layouts/fourCorners/fourCorners.component.ts
+++ b/src/layouts/fourCorners/fourCorners.component.ts
@@ -1,11 +1,12 @@
-import { Component, Input } from "@angular/core";
+import { ChangeDetectionStrategy, Component, Input } from "@angular/core";
 
 @Component({
   selector: 'iav-layout-fourcorners',
   templateUrl: './fourCorners.template.html',
   styleUrls: [
     './fourCorners.style.css'
-  ]
+  ],
+  changeDetection: ChangeDetectionStrategy.OnPush
 })
 
 export class FourCornersCmp{
diff --git a/src/layouts/fourCorners/fourCorners.template.html b/src/layouts/fourCorners/fourCorners.template.html
index c0e98df279d32db83b96227fa439642ff79175cc..6f837d73ffd82b84b4f6bef6f592f38b94ea7012 100644
--- a/src/layouts/fourCorners/fourCorners.template.html
+++ b/src/layouts/fourCorners/fourCorners.template.html
@@ -1,4 +1,4 @@
-<div class="position-absolute top-0 left-0 w-100 h-100">
+<div class="position-absolute tosxplr-p-0 left-0 w-100 h-100">
   <ng-content select="[iavLayoutFourCornersContent]"></ng-content>
 </div>
 
diff --git a/src/layouts/layout.module.ts b/src/layouts/layout.module.ts
index 0923ccb50102bfcde21c45a8f8bafe250ba31863..40cedc4f12736cadd766c749d55ab3e1139b4a43 100644
--- a/src/layouts/layout.module.ts
+++ b/src/layouts/layout.module.ts
@@ -3,7 +3,6 @@ import { BrowserModule } from "@angular/platform-browser";
 import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
 import { ComponentsModule } from "../components/components.module";
 import { CurrentLayout } from "./currentLayout/currentLayout.component";
-import { FloatingLayoutContainer } from "./floating/floating.component";
 import { FourCornersCmp } from "./fourCorners/fourCorners.component";
 import { FourPanelLayout } from "./layouts/fourPanel/fourPanel.component";
 import { HorizontalOneThree } from "./layouts/h13/h13.component";
@@ -17,7 +16,6 @@ import { VerticalOneThree } from "./layouts/v13/v13.component";
     ComponentsModule,
   ],
   declarations : [
-    FloatingLayoutContainer,
     FourCornersCmp,
     CurrentLayout,
 
@@ -28,7 +26,6 @@ import { VerticalOneThree } from "./layouts/v13/v13.component";
   ],
   exports : [
     BrowserAnimationsModule,
-    FloatingLayoutContainer,
     FourCornersCmp,
     CurrentLayout,
     FourPanelLayout,
diff --git a/src/main-common.ts b/src/main-common.ts
index 3bfc08878e14626e0a9cc0c95d26806ed27a47e3..f927f25516b9280f7f21416551c27a338117177e 100644
--- a/src/main-common.ts
+++ b/src/main-common.ts
@@ -28,7 +28,6 @@ import '!!file-loader?name=version.css!src/version.css'
 import 'zone.js'
 import { enableProdMode } from '@angular/core';
 
-import * as ConnectivityComponent from 'hbp-connectivity-component/dist/loader'
 import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'
 import { MainModule } from './main.module';
 
@@ -39,5 +38,3 @@ if (PRODUCTION) { console.log(`Siibra Explorer: ${VERSION}::${GIT_HASH}`) }
 
 
 platformBrowserDynamic().bootstrapModule(MainModule)
-
-ConnectivityComponent.defineCustomElements(window)
\ No newline at end of file
diff --git a/src/main.module.ts b/src/main.module.ts
index 93dd5e3db578cc3a369755cbe82c430fca6f32ea..7d26a8ee52400fa2acd8e9cbd0a670ddd06c5670 100644
--- a/src/main.module.ts
+++ b/src/main.module.ts
@@ -2,84 +2,66 @@ import { DragDropModule } from '@angular/cdk/drag-drop'
 import { CommonModule } from "@angular/common";
 import { APP_INITIALIZER, NgModule } from "@angular/core";
 import { FormsModule } from "@angular/forms";
-import { StoreModule, ActionReducer, Store } from "@ngrx/store";
+import { Store, select } from "@ngrx/store";
 import { AngularMaterialModule } from 'src/sharedModules'
 import { AtlasViewer } from "./atlasViewer/atlasViewer.component";
 import { ComponentsModule } from "./components/components.module";
 import { LayoutModule } from "./layouts/layout.module";
-import { ngViewerState, pluginState, uiState, userConfigState, UserConfigStateUseEffect, viewerConfigState, viewerState } from "./services/stateStore.service";
 import { UIModule } from "./ui/ui.module";
 
 import { HttpClientModule } from "@angular/common/http";
-import { EffectsModule } from "@ngrx/effects";
 import { AtlasWorkerService } from "./atlasViewer/atlasViewer.workerService.service";
 import { WINDOW_MESSAGING_HANDLER_TOKEN } from 'src/messaging/types'
 
-import { ConfirmDialogComponent } from "./components/confirmDialog/confirmDialog.component";
-import { DialogComponent } from "./components/dialog/dialog.component";
 import { DialogService } from "./services/dialogService.service";
-import { UseEffects } from "./services/effect/effect";
-import { LocalFileService } from "./services/localFile.service";
-import { NgViewerUseEffect } from "./services/state/ngViewerState.store";
-import { ViewerStateUseEffect } from "./services/state/viewerState.store";
 import { UIService } from "./services/uiService.service";
-import { ViewerStateControllerUseEffect } from "src/state";
-import { FloatingContainerDirective } from "./util/directives/floatingContainer.directive";
-import { FloatingMouseContextualContainerDirective } from "./util/directives/floatingMouseContextualContainer.directive";
-import { ClickInterceptor, CLICK_INTERCEPTOR_INJECTOR, PureContantService, UtilModule } from "src/util";
+import { ClickInterceptor, CLICK_INTERCEPTOR_INJECTOR, UtilModule } from "src/util";
 import { SpotLightModule } from 'src/spotlight/spot-light.module'
 import { TryMeComponent } from "./ui/tryme/tryme.component";
-import { UiStateUseEffect } from "src/services/state/uiState.store";
-import { PluginServiceUseEffect } from './services/effect/pluginUseEffect';
-import { TemplateCoordinatesTransformation } from "src/services/templateCoordinatesTransformation.service";
-import { NewTemplateUseEffect } from './services/effect/newTemplate.effect';
+import { InterSpaceCoordXformSvc } from "src/atlasComponents/sapi/core/space/interSpaceCoordXform.service";
 import { WidgetModule } from 'src/widget';
 import { PluginModule } from './plugin/plugin.module';
 import { LoggingModule } from './logging/logging.module';
 import { AuthService } from './auth'
 
 import 'src/theme.scss'
-import { DatasetPreviewGlue, datasetPreviewMetaReducer, IDatasetPreviewGlue, GlueEffects, ClickInterceptorService, _PLI_VOLUME_INJ_TOKEN } from './glue';
-import { viewerStateHelperReducer, viewerStateMetaReducers, ViewerStateHelperEffect } from './services/state/viewerState.store.helper';
+import { ClickInterceptorService } from './glue';
 import { TOS_OBS_INJECTION_TOKEN } from './ui/kgtos';
-import { UiEffects } from './services/state/uiState/ui.effects';
 import { MesssagingModule } from './messaging/module';
-import { ParcellationRegionModule } from './atlasComponents/parcellationRegion';
-import { ViewerModule, VIEWERMODULE_DARKTHEME } from './viewerModule';
+import { ViewerModule } from './viewerModule';
 import { CookieModule } from './ui/cookieAgreement/module';
 import { KgTosModule } from './ui/kgtos/module';
-import { MouseoverModule } from './mouseoverModule/mouseover.module';
 import { AtlasViewerRouterModule } from './routerModule';
 import { MessagingGlue } from './messagingGlue';
 import { BS_ENDPOINT } from './util/constants';
 import { QuickTourModule } from './ui/quickTour';
 import { of } from 'rxjs';
-import { debounceTime, filter, map } from 'rxjs/operators';
-import { GET_KGDS_PREVIEW_INFO_FROM_ID_FILENAME, OVERRIDE_IAV_DATASET_PREVIEW_DATASET_FN, kgTos, IAV_DATASET_PREVIEW_ACTIVE } from './databrowser.fallback'
-import { CANCELLABLE_DIALOG } from './util/interfaces';
+import { CANCELLABLE_DIALOG, CANCELLABLE_DIALOG_OPTS } from './util/interfaces';
 import { environment } from 'src/environments/environment' 
 import { NotSupportedCmp } from './notSupportedCmp/notSupported.component';
-import { RouterService } from './routerModule/router.service';
-import { ngViewerActionAddNgLayer, ngViewerActionRemoveNgLayer } from './services/state/ngViewerState.store.helper';
+import {
+  atlasSelection,
+  RootStoreModule,
+  getStoreEffects,
+} from "./state"
+import { DARKTHEME } from './util/injectionTokens';
+import { map } from 'rxjs/operators';
+import { EffectsModule } from '@ngrx/effects';
 
-export function debug(reducer: ActionReducer<any>): ActionReducer<any> {
-  return function(state, action) {
-    console.log('state', state);
-    console.log('action', action);
- 
-    return reducer(state, action);
-  };
-}
+// TODO check if there is a more logical place import put layerctrl effects ts
+import { LayerCtrlEffects } from './viewerModule/nehuba/layerCtrl.service/layerCtrl.effects';
+import { NehubaNavigationEffects } from './viewerModule/nehuba/navigation.service/navigation.effects';
+import { CONST } from "common/constants"
 
 @NgModule({
-  imports : [
+  imports: [
     FormsModule,
     CommonModule,
     LayoutModule,
     ComponentsModule,
     DragDropModule,
     UIModule,
-    
+
     AngularMaterialModule,
     UtilModule,
     WidgetModule,
@@ -88,74 +70,36 @@ export function debug(reducer: ActionReducer<any>): ActionReducer<any> {
     MesssagingModule,
     ViewerModule,
     SpotLightModule,
-    ParcellationRegionModule,
     CookieModule,
     KgTosModule,
-    MouseoverModule,
     AtlasViewerRouterModule,
     QuickTourModule,
     
     EffectsModule.forRoot([
-      UseEffects,
-      UserConfigStateUseEffect,
-      ViewerStateControllerUseEffect,
-      ViewerStateUseEffect,
-      NgViewerUseEffect,
-      PluginServiceUseEffect,
-      UiStateUseEffect,
-      NewTemplateUseEffect,
-      ViewerStateHelperEffect,
-      GlueEffects,
-      UiEffects,
+      ...getStoreEffects(),
+      LayerCtrlEffects,
+      NehubaNavigationEffects,
     ]),
-    StoreModule.forRoot({
-      pluginState,
-      viewerConfigState,
-      ngViewerState,
-      viewerState,
-      viewerStateHelper: viewerStateHelperReducer,
-      uiState,
-      userConfigState,
-    },{
-      metaReducers: [ 
-        // debug,
-        ...viewerStateMetaReducers,
-        datasetPreviewMetaReducer,
-      ]
-    }),
+    RootStoreModule,
     HttpClientModule,
   ],
-  declarations : [
+  declarations: [
     AtlasViewer,
     NotSupportedCmp,
     TryMeComponent,
-
     /* directives */
-    FloatingContainerDirective,
-    FloatingMouseContextualContainerDirective,
-
-  ],
-  entryComponents : [
-    DialogComponent,
-    ConfirmDialogComponent,
   ],
-  providers : [
+  providers: [
     AtlasWorkerService,
     AuthService,
-    LocalFileService,
     DialogService,
     UIService,
-    TemplateCoordinatesTransformation,
+    InterSpaceCoordXformSvc,
     ClickInterceptorService,
-    {
-      provide: OVERRIDE_IAV_DATASET_PREVIEW_DATASET_FN,
-      useFactory: (glue: IDatasetPreviewGlue) => glue.displayDatasetPreview.bind(glue),
-      deps: [ DatasetPreviewGlue ]
-    },
     {
       provide: CANCELLABLE_DIALOG,
       useFactory: (uiService: UIService) => {
-        return (message, option) => {
+        return (message: string, option: CANCELLABLE_DIALOG_OPTS) => {
           const actionBtn = {
             type: 'mat-stroked-button',
             color: 'default',
@@ -190,26 +134,9 @@ export function debug(reducer: ActionReducer<any>): ActionReducer<any> {
       },
       deps: [ UIService ]
     },
-    {
-      provide: _PLI_VOLUME_INJ_TOKEN,
-      useFactory: (glue: DatasetPreviewGlue) => glue._volumePreview$,
-      deps: [ DatasetPreviewGlue ]
-    },
-    {
-      provide: IAV_DATASET_PREVIEW_ACTIVE,
-      useFactory: (glue: DatasetPreviewGlue) => glue.datasetPreviewDisplayed.bind(glue),
-      deps: [ DatasetPreviewGlue ]
-    },
-    {
-      provide: GET_KGDS_PREVIEW_INFO_FROM_ID_FILENAME,
-      useFactory: (glue: DatasetPreviewGlue) => glue.getDatasetPreviewFromId.bind(glue),
-      deps: [ DatasetPreviewGlue ]
-    },
-    DatasetPreviewGlue,
-    
     {
       provide: TOS_OBS_INJECTION_TOKEN,
-      useValue: of(kgTos)
+      useValue: of(CONST.KG_TOS)
     },
 
     {
@@ -232,11 +159,6 @@ export function debug(reducer: ActionReducer<any>): ActionReducer<any> {
         ClickInterceptorService
       ]
     },
-    {
-      provide: VIEWERMODULE_DARKTHEME,
-      useFactory: (pureConstantService: PureContantService) => pureConstantService.darktheme$,
-      deps: [ PureContantService ]
-    },
     {
       provide: WINDOW_MESSAGING_HANDLER_TOKEN,
       useClass: MessagingGlue
@@ -246,66 +168,26 @@ export function debug(reducer: ActionReducer<any>): ActionReducer<any> {
       useValue: (environment.BS_REST_URL || `https://siibra-api-stable.apps.hbp.eu/v1_0`).replace(/\/$/, '')
     },
     {
-      /**
-       * monkey patch 1um data as x-voi:d71d369a-c401-4d7e-b97a-3fb78eed06c5
-       */
+      provide: DARKTHEME,
+      useFactory: (store: Store) => store.pipe(
+        select(atlasSelection.selectors.selectedTemplate),
+        map(tmpl => !!(tmpl && tmpl["@id"] !== 'minds/core/referencespace/v1.0.0/a1655b99-82f1-420f-a3c2-fe80fd4c8588')),
+      ),
+      deps: [ Store ]
+    },
+    {
       provide: APP_INITIALIZER,
-      useFactory: (rSvc: RouterService, store: Store) => {
-        rSvc.customRoute$.pipe(
-          map(val => val[`x-voi`] === `d71d369a-c401-4d7e-b97a-3fb78eed06c5`),
-          debounceTime(160)
-        ).subscribe(flag => {
-          if (flag) {
-            store.dispatch(
-              ngViewerActionAddNgLayer({
-                layer: [{
-                  name: `VOI_1 (area V1)`,
-                  source: `precomputed://https://1um.brainatlas.eu/cyto_reconstructions/ebrains_release/BB_1um/VOI_1/precomputed`,
-                  shader: `void main(){ emitGrayscale(toNormalized(getDataValue()));}`,
-                  transform: [[1.002276062965393,0.1810370832681656,0.15283183753490448,-10704839],[-0.14879435300827026,-0.0360119566321373,1.018455982208252,-60994436],[0.18436983227729797,-1.0132216215133667,-0.008890812285244465,-2825862.75],[0,0,0,1]],
-                  visible: true,
-                  opacity: 1,
-                },{
-                  name: `VOI_2 (area V2)`,
-                  source: `precomputed://https://1um.brainatlas.eu/cyto_reconstructions/ebrains_release/BB_1um/VOI_2/precomputed`,
-                  shader: `void main(){ emitGrayscale(toNormalized(getDataValue()));}`,
-                  // transform: [[1.002276062965393,0.1810370832681656,0.15283183753490448,-10704839],[-0.14879435300827026,-0.0360119566321373,1.018455982208252,-60994436],[0.18436983227729797,-1.0132216215133667,-0.008890812285244465,-2825862.75],[0,0,0,1]],
-                  visible: true,
-                  opacity: 1,
-                }]
-              })
-            )
-          } else {
-            store.dispatch(
-              ngViewerActionRemoveNgLayer({
-                layer: [{
-                  name: `VOI_1 (area V1)`
-                }, {
-                  name: `VOI_2 (area V2)`
-                }]
-              })
-            )
-          }
-        })
+      useFactory: (authSvc: AuthService) => {
+        authSvc.authReloadState()
         return () => Promise.resolve()
       },
       multi: true,
-      deps: [ RouterService, Store ]
+      deps: [ AuthService ]
     }
   ],
-  bootstrap : [
+  bootstrap: [
     AtlasViewer,
-  ],
+  ]
 })
 
-export class MainModule {
-
-  constructor(
-    authServce: AuthService,
-    // bandaid fix: required to init glueService on startup
-    // TODO figure out why, then init service without this hack
-    glueService: DatasetPreviewGlue
-  ) {
-    authServce.authReloadState()
-  }
-}
+export class MainModule {}
diff --git a/src/messaging/nmvSwc/index.ts b/src/messaging/nmvSwc/index.ts
index 08b0ce6994d4ad595d3aa1689e872e8d45a702b8..2f4e7586970acd9450a6456fb9fe6e06ba7ddea9 100644
--- a/src/messaging/nmvSwc/index.ts
+++ b/src/messaging/nmvSwc/index.ts
@@ -1,5 +1,5 @@
 import { Observable, Subject } from "rxjs"
-import { getUuid } from "src/util/fn"
+import { getExportNehuba, getUuid } from "src/util/fn"
 import { IMessagingActions, IMessagingActionTmpl, TVec4, TMat4 } from "../types"
 import { INmvTransform } from "./type"
 
@@ -110,7 +110,7 @@ export const processJsonLd = (json: { [key: string]: any }): Observable<IMessagi
       }
     })
 
-    await waitFor(() => !!(window as any).export_nehuba)
+    await waitFor(() => !!getExportNehuba())
 
     const b64Encoded = encoding.indexOf('base64') >= 0
     const isGzipped = encoding.indexOf('gzip') >= 0
@@ -119,7 +119,7 @@ export const processJsonLd = (json: { [key: string]: any }): Observable<IMessagi
       data = atob(data)
     }
     if (isGzipped) {
-      data = (window as any).export_nehuba.pako.inflate(data)
+      data = getExportNehuba().pako.inflate(data)
     }
     let output = ``
     for (let i = 0; i < data.length; i++) {
@@ -146,7 +146,7 @@ export const processJsonLd = (json: { [key: string]: any }): Observable<IMessagi
     ]
     // NG translation works on nm scale
     const scaleUmToNm = 1e3
-    const { mat3, vec3 } = (window as any).export_nehuba
+    const { mat3, vec3 } = getExportNehuba()
     const modA = mat3.fromValues(
       scaleUmToVoxelFixed[0], 0, 0,
       0, scaleUmToVoxelFixed[1], 0,
diff --git a/src/messaging/service.spec.ts b/src/messaging/service.spec.ts
index 5ee3b0811946b615c8e0348cd1018144a8220e83..2ba18423e3617ed2c3016bc9467083fc49576ff9 100644
--- a/src/messaging/service.spec.ts
+++ b/src/messaging/service.spec.ts
@@ -1,7 +1,6 @@
 import { TestBed } from "@angular/core/testing"
 import { MockStore, provideMockStore } from "@ngrx/store/testing"
 import { AtlasWorkerService } from "src/atlasViewer/atlasViewer.workerService.service"
-import { viewerStateFetchedAtlasesSelector } from "src/services/state/viewerState/selectors"
 import { AngularMaterialModule } from "src/sharedModules"
 import { getUuid } from "src/util/fn"
 import { IAV_POSTMESSAGE_NAMESPACE, MessagingService } from "./service"
@@ -39,8 +38,6 @@ describe('> service.ts', () => {
         ]
       })
 
-      const mockStore = TestBed.inject(MockStore)
-      mockStore.overrideSelector(viewerStateFetchedAtlasesSelector, [])
     })
 
     it('> can be inst', () => {
diff --git a/src/messaging/service.ts b/src/messaging/service.ts
index 7739108fca2d3bab0cf4e3c6a6aed12e148a931a..d810a7fce7c330809be7154671389d090ef18bca 100644
--- a/src/messaging/service.ts
+++ b/src/messaging/service.ts
@@ -7,7 +7,7 @@ import { getUuid } from "src/util/fn";
 import { AtlasWorkerService } from "src/atlasViewer/atlasViewer.workerService.service";
 import { ConfirmDialogComponent } from "src/components/confirmDialog/confirmDialog.component";
 
-import { IMessagingActions, IMessagingActionTmpl, ILoadMesh, LOAD_MESH_TOKEN, WINDOW_MESSAGING_HANDLER_TOKEN, IWindowMessaging } from './types'
+import { IMessagingActions, IMessagingActionTmpl, WINDOW_MESSAGING_HANDLER_TOKEN, IWindowMessaging } from './types'
 import { TYPE as NMV_TYPE, processJsonLd as nmvProcess } from './nmvSwc/index'
 import { TYPE as NATIVE_TYPE, processJsonLd as nativeProcess } from './native'
 
@@ -35,7 +35,6 @@ export class MessagingService {
     private snackbar: MatSnackBar,
     private worker: AtlasWorkerService,
     @Optional() @Inject(WINDOW_MESSAGING_HANDLER_TOKEN) private messagingHandler: IWindowMessaging,
-    @Optional() @Inject(LOAD_MESH_TOKEN) private loadMesh: (loadMeshParam: ILoadMesh) => void,
   ){
     
     if (window.opener){
@@ -72,11 +71,13 @@ export class MessagingService {
           result
         }, origin)
       } catch (error) {
-        src.postMessage({
-          id,
-          jsonrpc: '2.0',
-          error
-        }, origin)
+        if (src) {
+          src.postMessage({
+            id,
+            jsonrpc: '2.0',
+            error
+          }, origin)
+        }
       }
     })
 
@@ -189,17 +190,21 @@ export class MessagingService {
       })
       isLoadingSnack?.dismiss()
       const meshId = 'bobby'
-      if (this.loadMesh) {
-        const { objectUrl, customFragmentColor } = resp.result || {}
-        this.loadMesh({
-          type: 'VTK',
-          id: meshId,
-          url: objectUrl,
-          customFragmentColor
-        })
-      } else {
-        this.snackbar.open(`Error: loadMesh method not injected.`)
-      }
+      /**
+       * TODO re-enable plotly VTK mesh
+       */
+      
+      // if (false) {
+      //   const { objectUrl, customFragmentColor } = resp.result || {}
+      //   this.loadMesh({
+      //     type: 'VTK',
+      //     id: meshId,
+      //     url: objectUrl,
+      //     customFragmentColor
+      //   })
+      // } else {
+      //   this.snackbar.open(`Error: loadMesh method not injected.`)
+      // }
       return 'OK'
     }
 
diff --git a/src/messaging/types.ts b/src/messaging/types.ts
index eda7bc06a35931dd508ead545b9bb7f6df38c174..6eb83f851f40a7da56d1e8aad529d573a32f31a9 100644
--- a/src/messaging/types.ts
+++ b/src/messaging/types.ts
@@ -8,10 +8,10 @@ interface IResourceType {
   swc: string
 }
 
-export type TVec4 = [number, number, number, number]
-export type TVec3 = [number, number, number]
-export type TMat3 = [TVec3, TVec3, TVec3]
-export type TMat4 = [TVec4, TVec4, TVec4, TVec4]
+export type TVec4 = number[]
+export type TVec3 = number[]
+export type TMat3 = TVec3[]
+export type TMat4 = TVec4[]
 
 interface ICommonResParam {
   transform: TMat4
@@ -45,14 +45,6 @@ export interface IMessagingActions<TAction extends keyof IMessagingActionTmpl> {
   payload: IMessagingActionTmpl[TAction]
 }
 
-export interface ILoadMesh {
-  type: 'VTK'
-  id: string
-  url: string
-  customFragmentColor?: string
-}
-export const LOAD_MESH_TOKEN = new InjectionToken<(loadMeshParam: ILoadMesh) => void>('LOAD_MESH_TOKEN')
-
 export interface IWindowMessaging {
   loadTempladById(payload: IMessagingActionTmpl['loadTemplate']): void
   loadResource(payload: IMessagingActionTmpl['loadResource']): void
diff --git a/src/messagingGlue.ts b/src/messagingGlue.ts
index 80e459157a717ec90b21f426596e531acbb48843..724841a7d13e9255e76ba5c4b0ad94e7393e91ad 100644
--- a/src/messagingGlue.ts
+++ b/src/messagingGlue.ts
@@ -1,10 +1,8 @@
 import { Injectable, OnDestroy } from "@angular/core";
-import { select, Store } from "@ngrx/store";
+import { Store } from "@ngrx/store";
 import { IMessagingActionTmpl, IWindowMessaging } from "./messaging/types";
-import { ngViewerActionAddNgLayer, ngViewerActionRemoveNgLayer } from "./services/state/ngViewerState/actions";
-import { viewerStateSelectAtlas } from "./services/state/viewerState/actions";
-import { viewerStateFetchedAtlasesSelector } from "./services/state/viewerState/selectors";
-import { generalActionError } from "./services/stateStore.helper";
+import { atlasAppearance, atlasSelection, generalActions } from "src/state"
+import { SAPI } from "./atlasComponents/sapi";
 
 @Injectable()
 export class MessagingGlue implements IWindowMessaging, OnDestroy {
@@ -17,14 +15,15 @@ export class MessagingGlue implements IWindowMessaging, OnDestroy {
     while(this.onDestroyCb.length > 0) this.onDestroyCb.pop()()
   }
 
-  constructor(private store: Store<any>){
+  constructor(
+    private store: Store<any>,
+    sapi: SAPI,
+  ){
 
-    const sub = this.store.pipe(
-      select(viewerStateFetchedAtlasesSelector)
-    ).subscribe((atlases: any[]) => {
+    const sub = sapi.atlases$.subscribe(atlases => {
       for (const atlas of atlases) {
-        const { ['@id']: atlasId, templateSpaces } = atlas
-        for (const tmpl of templateSpaces) {
+        const { ['@id']: atlasId, spaces } = atlas
+        for (const tmpl of spaces) {
           const { ['@id']: tmplId } = tmpl
           this.tmplSpIdToAtlasId.set(tmplId, atlasId)
         }
@@ -42,19 +41,15 @@ export class MessagingGlue implements IWindowMessaging, OnDestroy {
     const atlasId = this.tmplSpIdToAtlasId.get(payload['@id'])
     if (!atlasId) {
       return this.store.dispatch(
-        generalActionError({
+        generalActions.generalActionError({
           message: `atlas id with the corresponding templateId ${payload['@id']} not found.`
         })
       )
     }
     this.store.dispatch(
-      viewerStateSelectAtlas({
-        atlas: {
-          ['@id']: atlasId,
-          template: {
-            ['@id']: payload['@id']
-          }
-        }
+      atlasSelection.actions.selectATPById({
+        atlasId,
+        templateId: payload["@id"]
       })
     )
   }
@@ -71,29 +66,25 @@ export class MessagingGlue implements IWindowMessaging, OnDestroy {
     if (type === 'swc') {
       const { transform } = resourceParam
       const layer = {
-        name: swcLayerUuid,
         id: swcLayerUuid,
         source: `swc://${url}`,
-        mixability: 'mixable',
-        type: "segmentation",
-        "segments": [
+        segments: [
           "1"
         ],
-        transform,
+        transform: transform,
+        clType: 'customlayer/nglayer' as const
       }
 
       this.store.dispatch(
-        ngViewerActionAddNgLayer({
-          layer
+        atlasAppearance.actions.addCustomLayer({
+          customLayer: layer
         })
       )
 
       this.mapIdUnload.set(swcLayerUuid, () => {
         this.store.dispatch(
-          ngViewerActionRemoveNgLayer({
-            layer: {
-              name: swcLayerUuid
-            }
+          atlasAppearance.actions.removeCustomLayer({
+            id: swcLayerUuid
           })
         )
         unload()
diff --git a/src/mouseoverModule/mouseOverCvt.pipe.ts b/src/mouseoverModule/mouseOverCvt.pipe.ts
index b0a86234237ad87ab895e70575e8dd1964ba5d51..f325feacc32b30c67e39451bac74cff846782185 100644
--- a/src/mouseoverModule/mouseOverCvt.pipe.ts
+++ b/src/mouseoverModule/mouseOverCvt.pipe.ts
@@ -4,16 +4,14 @@ import { TOnHoverObj } from "./util";
 function render<T extends keyof TOnHoverObj>(key: T, value: TOnHoverObj[T]){
   if (!value) return []
   switch (key) {
-  case 'segments': {
-    return (value as TOnHoverObj['segments']).map(seg => {
+  case 'regions': {
+    return (value as TOnHoverObj['regions']).map(seg => {
       return {
         icon: {
           fontSet: 'fas',
           fontIcon: 'fa-brain'
         },
-        text: typeof seg.segment === 'string'
-          ? seg.segment
-          : seg.segment.name
+        text: seg.name
       }
     })
   }
diff --git a/src/mouseoverModule/mouseover.directive.ts b/src/mouseoverModule/mouseover.directive.ts
index cc4ae00872d32f4463aeb4d6a9ce7fbdee37ebe3..9e005530107e7653a559fe108c1b4891f9bc7847 100644
--- a/src/mouseoverModule/mouseover.directive.ts
+++ b/src/mouseoverModule/mouseover.directive.ts
@@ -1,11 +1,11 @@
 import { Directive } from "@angular/core"
 import { select, Store } from "@ngrx/store"
-import { merge, Observable } from "rxjs"
+import { merge, NEVER, Observable, of } from "rxjs"
 import { distinctUntilChanged, map, scan, shareReplay } from "rxjs/operators"
 import { LoggingService } from "src/logging"
-import { uiStateMouseOverLandmarkSelector, uiStateMouseOverSegmentsSelector, uiStateMouseoverUserLandmark } from "src/services/state/uiState/selectors"
 import { TOnHoverObj, temporalPositveScanFn } from "./util"
 import { ModularUserAnnotationToolService } from "src/atlasComponents/userAnnotations/tools/service";
+import { userInteraction } from "src/state"
 
 @Directive({
   selector: '[iav-mouse-hover]',
@@ -25,27 +25,29 @@ export class MouseHoverDirective {
     // TODO consider moving these into a single obs serviced by a DI service
     // can potentially net better performance
 
-    const onHoverUserLandmark$ = this.store$.pipe(
-      select(uiStateMouseoverUserLandmark)
-    )
-
-    const onHoverLandmark$ = this.store$.pipe(
-      select(uiStateMouseOverLandmarkSelector)
-    ).pipe(
-      map(landmark => {
-        if (landmark === null) { return null }
-        const idx = Number(landmark.replace('label=', ''))
-        if (isNaN(idx)) {
-          this.log.warn(`Landmark index could not be parsed as a number: ${landmark}`)
-          return {
-            landmarkName: idx,
-          }
-        } 
-      }),
-    )
+    const onHoverUserLandmark$ = NEVER
+    // this.store$.pipe(
+    //   select(uiStateMouseoverUserLandmark)
+    // )
+
+    const onHoverLandmark$ = NEVER
+    // this.store$.pipe(
+    //   select(uiStateMouseOverLandmarkSelector)
+    // ).pipe(
+    //   map(landmark => {
+    //     if (landmark === null) { return null }
+    //     const idx = Number(landmark.replace('label=', ''))
+    //     if (isNaN(idx)) {
+    //       this.log.warn(`Landmark index could not be parsed as a number: ${landmark}`)
+    //       return {
+    //         landmarkName: idx,
+    //       }
+    //     } 
+    //   }),
+    // )
 
     const onHoverSegments$ = this.store$.pipe(
-      select(uiStateMouseOverSegmentsSelector),
+      select(userInteraction.selectors.mousingOverRegions),
 
       // TODO fix aux mesh filtering
 
@@ -59,7 +61,7 @@ export class MouseHoverDirective {
       //   ? arr.filter(({ segment }) => {
       //     // if segment is not a string (i.e., not labelIndexId) return true
       //     if (typeof segment !== 'string') { return true }
-      //     const { labelIndex } = deserialiseParcRegionId(segment)
+      //     const { label: labelIndex } = deserializeSegment(segment)
       //     return parcellationSelected.auxillaryMeshIndices.indexOf(labelIndex) < 0
       //   })
       //   : arr),
@@ -70,8 +72,8 @@ export class MouseHoverDirective {
     const mergeObs = merge(
       onHoverSegments$.pipe(
         distinctUntilChanged(),
-        map(segments => {
-          return { segments }
+        map(regions => {
+          return { regions }
         }),
       ),
       onHoverAnnotation$.pipe(
@@ -101,7 +103,7 @@ export class MouseHoverDirective {
       map(arr => {
 
         let returnObj = {
-          segments: null,
+          regions: null,
           annotation: null,
           landmark: null,
           userLandmark: null,
diff --git a/src/mouseoverModule/type.ts b/src/mouseoverModule/type.ts
index 07c4312fa7896c53a7f4a8ca58b842e3cd88dc59..38033f903e9047b4164c90137564a19d0f75ae86 100644
--- a/src/mouseoverModule/type.ts
+++ b/src/mouseoverModule/type.ts
@@ -1,13 +1,5 @@
 import { TRegionSummary } from "src/util/siibraApiConstants/types";
 
-export type TMouseOverSegment = {
-  layer: {
-    name: string
-  }
-  segmentId: number
-  segment: TRegionSummary | string // if cannot decode, then segment will be {ngId}#{labelIndex}
-}
-
 export type TMouseOverVtkLandmark = {
   landmarkName: string
 }
\ No newline at end of file
diff --git a/src/mouseoverModule/util.ts b/src/mouseoverModule/util.ts
index ed4fbe50f42c9580f554e19b6e98678438c1b038..0811db26a592492f396f27f1d61cae1b4c7e2cb6 100644
--- a/src/mouseoverModule/util.ts
+++ b/src/mouseoverModule/util.ts
@@ -1,8 +1,8 @@
+import { SapiRegionModel } from "src/atlasComponents/sapi"
 import { IAnnotationGeometry } from "src/atlasComponents/userAnnotations/tools/type"
-import { TMouseOverSegment } from "./type"
 
 export type TOnHoverObj = {
-  segments: TMouseOverSegment[]
+  regions: SapiRegionModel[]
   annotation: IAnnotationGeometry
   landmark: {
     landmarkName: number
diff --git a/src/overwrite.scss b/src/overwrite.scss
index c8e7e12e46e899f162b3085f1cd523be12835d24..790e436d35acc4e970127548ec060fa796b6a650 100644
--- a/src/overwrite.scss
+++ b/src/overwrite.scss
@@ -1,41 +1,7 @@
 @use 'sass:math';
 
-iav-cmp-viewer-container
-{
-
-  .mat-chip-list-wrapper
-  {
-    flex-wrap: nowrap;
-  }
-
-  poly-update-cmp
-  {
-    .mat-chip-list-wrapper
-    {
-      flex-wrap: wrap;
-    }
-  }
-}
-
-quick-tour-unit
-{
-  svg
-  {
-    pointer-events: none;
-    stroke-width: 3px;
-    stroke: rgb(255, 255, 255);
-    stroke-linecap: round;
-    stroke-linejoin: round;
-  }
-}
+$nsp: "sxplr";
 
-kg-ds-prv-regional-feature-view
-{
-  div
-  {
-    min-height: 20em;
-  }
-}
 
 // no prefix
 @for $i from 1 through 12 {
@@ -71,3 +37,245 @@ $media-map: (
     }
   }
 }
+
+@for $i from 5 through 20 {
+  $fontsize: $i * 10;
+  .fs-#{$fontsize} {
+    font-size: $fontsize * 1%;
+  }
+}
+
+
+@for $i from 4 through 10 {
+  $tensvar: $i * 10;
+  .#{$nsp}-mxh-#{$tensvar}vh {
+    max-height: $tensvar * 1vh;
+  }
+  .#{$nsp}-mxw-#{$tensvar}vw {
+    max-width: $tensvar * 1vw;
+  }
+}
+
+$overflow-directive: hidden, scroll, auto, visible;
+@each $directive in $overflow-directive {
+  .#{$nsp}-of-x-#{$directive} {
+    overflow-x: $directive!important;
+  }
+  .#{$nsp}-of-y-#{$directive} {
+    overflow-y: $directive!important;
+  }
+  .#{$nsp}-of-#{$directive} {
+    overflow: $directive!important;
+  }
+}
+
+@for $scale from 5 through 10 {
+
+  $scale-var: $scale * 10;
+  .#{$nsp}-scale-#{$scale-var}
+  {
+    transform: scale($scale * 0.1);
+  }
+}
+
+$transform-origin-vars: "left-center", "center";
+$transform-origin-maps: (
+  "left-center": 0% 50%,
+  "center": 50% 50%
+);
+
+@each $var in $transform-origin-vars {
+  .transform-origin-#{$var} {
+    transform-origin: map-get($transform-origin-maps, $var);
+  }
+}
+
+@for $unit from 5 through 10 {
+  $width: $unit * 10;
+  .w-#{$width} {
+    width: $width * 1%;
+  }
+  .#{$nsp}-w-#{$width} {
+    width: $width * 1%;
+  }
+  .h-#{$width} {
+    width: $width * 1%;
+  }
+  .#{$nsp}-h-#{$width} {
+    height: $width * 1%;
+  }
+}
+
+@for $zlvl from 0 through 10 {
+  .#{$nsp}-z-#{$zlvl} {
+    z-index: $zlvl;
+  }
+}
+
+@for $unit from 0 through 10 {
+  .#{$nsp}-pt-#{$unit}{
+    padding-top: $unit * 0.5rem!important;
+  }
+  .#{$nsp}-pb-#{$unit}{
+    padding-bottom: $unit * 0.5rem!important;
+  }
+  .#{$nsp}-pl-#{$unit}{
+    padding-left: $unit * 0.5rem!important;
+  }
+  .#{$nsp}-pr-#{$unit}{
+    padding-right: $unit * 0.5rem!important;
+  }
+  .#{$nsp}-p-#{$unit}{
+    padding: $unit * 0.5rem!important;
+  }
+
+  .#{$nsp}-mt-#{$unit}{
+    margin-top: $unit * 0.5rem!important;
+  }
+  .#{$nsp}-mb-#{$unit}{
+    margin-bottom: $unit * 0.5rem!important;
+  }
+  .#{$nsp}-ml-#{$unit}{
+    margin-left: $unit * 0.5rem!important;
+  }
+  .#{$nsp}-mr-#{$unit}{
+    margin-right: $unit * 0.5rem!important;
+  }
+  .#{$nsp}-m-#{$unit}{
+    margin: $unit * 0.5rem!important;
+  }
+}
+
+.#{$nsp}-mt-a {
+  margin-top: auto!important;
+}
+.#{$nsp}-mb-a {
+  margin-bottom: auto!important;
+}
+
+$display-vars: none, block, inline-block, flex, inline-flex;
+@each $display-var in $display-vars {
+  .d-#{$display-var}
+  {
+    display: $display-var!important;
+  }
+  .#{$nsp}-d-#{$display-var}
+  {
+    display: $display-var!important;
+  }
+}
+
+$align-items-vars: center, stretch, start;
+@each $align-items-var in $align-items-vars {
+  .align-items-#{$align-items-var} {
+    align-items: $align-items-var;
+  }
+  
+  .#{$nsp}-align-items-#{$align-items-var} {
+    align-items: $align-items-var;
+  }
+}
+
+$justify-content-vars: end, center, space-between;
+@each $justify-content-var in $justify-content-vars {
+  .#{$nsp}-justify-content-#{$justify-content-var} {
+    justify-content: $justify-content-var;
+  }
+}
+
+.#{$nsp}-m-a {
+  margin: auto;
+}
+
+.#{$nsp}-muted {
+  opacity: 0.75;
+}
+
+.#{$nsp}-very-muted {
+  opacity: 0.5;
+}
+
+.#{$nsp}-extra-muted {
+  opacity: 0.25;
+}
+
+$position-vars: relative, absolute;
+@each $position-var in $position-vars {
+  .#{$nsp}-position-#{$position-var} {
+    position: $position-var;
+  }
+}
+
+.#{$nsp}-bg-none
+{
+  background: none!important;
+}
+
+.#{$nsp}-box-shadow-none
+{
+  box-shadow: none!important;
+}
+
+
+$white-space-vars: nowrap;
+@each $white-space-var in $white-space-vars {
+  .#{$nsp}-white-space-#{$white-space-var}
+  {
+    white-space: $white-space-var!important;
+  }
+}
+
+$pointer-events-vars: all, none;
+@each $pointer-events-var in $pointer-events-vars {
+  .#{$nsp}-pe-#{$pointer-events-var}
+  {
+    pointer-events: $pointer-events-var!important;
+  }
+}
+
+$width-pc-vars: 100;
+@each $width-pc-var in $width-pc-vars {
+  .#{$nsp}-w-#{$width-pc-var} {
+    width: $width-pc-var * 1%;
+  }
+}
+
+.#{$nsp}-border
+{
+  border-width: 1px;
+}
+
+// flex
+$flex-wrap-vars: nowrap, wrap, wrap-reverse;
+@each $flex-wrap-var in $flex-wrap-vars {
+  .#{$nsp}-flex-wrap-#{$flex-wrap-var} {
+    flex-wrap: $flex-wrap-var;
+  }
+}
+$flex-directions: row,column;
+@each $flex-direction in $flex-directions {
+  .#{$nsp}-flex-#{$flex-direction} {
+    flex-direction: $flex-direction;
+  }
+}
+.#{$nsp}-flex-var {
+  flex: 1 1 0px;
+}
+
+.#{$nsp}-flex-static {
+  flex: 0 0 auto;
+}
+
+.#{$nsp}-blink
+{
+  animation: blink 500ms ease-in-out infinite alternate;
+}
+
+@keyframes blink {
+  0% {
+    opacity: 0.8;
+  }
+  100% {
+    opacity: 0.5;
+  }
+}
diff --git a/src/plugin/MIGRATION.md b/src/plugin/MIGRATION.md
new file mode 100644
index 0000000000000000000000000000000000000000..cb162d8d0a680cd2dae7a855c4639e80c29e75e5
--- /dev/null
+++ b/src/plugin/MIGRATION.md
@@ -0,0 +1,31 @@
+# Migrate from siibra-explorer < 2.7.0
+
+Plugin within siibra-explorer existed since before `pre-0.2.0`. We changed the way plugin works on `siibra-explorer==2.7.0`.
+
+## Why
+
+In siibra-explorer < 2.7.0, the HTML, JS are rendered directly in the same frame as siibra-explorer. 
+
+Whilst this approach provided a lot of flexibility for the plugin, it also introduced a lot of points of failures and/or non-optimal practices.
+
+For example, the objects passed to the plugin was not always structureClone'd. This meant that plugins which mutate these objects could cause issues difficult to debug. 
+
+Another example is that plugin authors often have to write HTML and JS specificially to interact with siibra-explorer. These code snippets often cannot be reused (since they expect a globally defined `interactiveViewer` object to exist.)
+
+Additionally, the previous system necessitates the running of arbitary JS code, which can be a security vulnerability.
+
+## The new system
+
+The plugin now runs in an iframe, and the data are passed between `siibra-explorer` and the plugin via [postMessage API](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage). This address all/most of the three concerns above:
+
+- objects passed will always be a clone (per `postMessage` spec). This allows plugin authors to do as their heart content with the received data, and it will not affect the viewer instance
+
+- plugin authors will provide a valid HTML (rather than HTML fragment). It can be rendered independently without `siibra-explorer`.[1]
+
+- any arbitary code from the plugin is sandboxed in the iframe, and should not interfere with `siibra-explorer`. This does **not** completely eliminate potential security threats:
+
+  - Best practices still needs to be followed to harden the security (e.g. use `sandbox` attribute (WIP))
+
+  - Existing browser vulnerabilities, which the browser vendors have much greater resources and incentive to provide a fix.  
+
+[1] Most modern browser are quite forgiving when it comes to rendering HTML. They could often render partial/invalid HTML. We still believe having spec compliant HTML is a good practice.
diff --git a/src/plugin/README.md b/src/plugin/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..8656ab3155dcd446795a36f562a66e3d06761eb1
--- /dev/null
+++ b/src/plugin/README.md
@@ -0,0 +1,49 @@
+# Plugins
+
+:warning: the API in this document refer to `siibra-explorer>=2.7.0`. For migration guide/rationale, please see [MIGRATION.md](./MIGRATION.md)
+
+siibra-explorer provides a plugin system, which allow a third party application to interact with siibra-explorer.
+
+## Quickstart
+
+### manifest
+
+The plugin need to expose a manifest json file. The manifest file needs to have the following properties:
+
+```json
+{
+  "iframeUrl": "<iframeUrl>",
+  "name": "<name>",
+  "siibra-explorer": "<siibra-explorer>"
+}
+```
+
+| property | required | desc | 
+| --- | --- | --- |
+| `iframeUrl` | true | points to the html where the iframe is located. If does not start with `https?://`, siibra-explorer will try to resolve it relative to the absolute path of manifest. |
+| `name` | true | name of the plugin | 
+| `siibra-explorer` | true | the version siibra-explorer this plugin is targetting. Should be >= 2.7.0. n.b. currently this entry is partially implemented, and any truthy value is sufficient.
+ |
+
+
+<!-- TBD -->
+
+## Architecture
+
+The plugin needs to provide a HTML page, served over HTTP. This will be embedded into siibra-explorer as an iframe.
+
+All communications between siibra-explorer and plugin will occur via the [postMessage API](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage).
+
+## Lifecycle
+
+`handshake.init` (up to 10x attempts, 1sec debounce) -> `{broadcast|request}` -> `handshake.exit` (NYI)
+
+Please note that the `handshake.init` needs to be responded to, *before* any other messages are sent.
+
+## API References
+
+[handshake API](./handshake.md)
+
+[broadcast API](./broadcast.md)
+
+[request API](./request.md)
diff --git a/src/plugin/atlasViewer.pluginService.service.spec.ts b/src/plugin/atlasViewer.pluginService.service.spec.ts
deleted file mode 100644
index 41e342b8bf77d8941eb731554972b71217e75b73..0000000000000000000000000000000000000000
--- a/src/plugin/atlasViewer.pluginService.service.spec.ts
+++ /dev/null
@@ -1,309 +0,0 @@
-import { CommonModule } from "@angular/common"
-import { HttpClientTestingModule, HttpTestingController } from "@angular/common/http/testing"
-import { NgModule } from "@angular/core"
-import { async, fakeAsync, TestBed, tick } from "@angular/core/testing"
-import { MockStore, provideMockStore } from "@ngrx/store/testing"
-import { ComponentsModule } from "src/components"
-import { DialogService } from "src/services/dialogService.service"
-import { selectorPluginCspPermission } from "src/services/state/userConfigState.helper"
-import { AngularMaterialModule } from "src/sharedModules"
-import { PureContantService } from "src/util"
-import { APPEND_SCRIPT_TOKEN, REMOVE_SCRIPT_TOKEN } from "src/util/constants"
-import { WidgetModule, WidgetServices } from "src/widget"
-import { PluginServices } from "./atlasViewer.pluginService.service"
-import { PluginModule } from "./plugin.module"
-import { PluginUnit } from "./pluginUnit/pluginUnit.component"
-
-const MOCK_PLUGIN_MANIFEST = {
-  name: 'fzj.xg.MOCK_PLUGIN_MANIFEST',
-  templateURL: 'http://localhost:10001/template.html',
-  scriptURL: 'http://localhost:10001/script.js'
-}
-
-const spyfn = {
-  appendSrc: jasmine.createSpy('appendSrc')
-}
-
-
-
-describe('> atlasViewer.pluginService.service.ts', () => {
-  describe('> PluginServices', () => {
-    
-    let pluginService: PluginServices
-    let httpMock: HttpTestingController
-    let mockStore: MockStore
-    
-    beforeEach(async(() => {
-      TestBed.configureTestingModule({
-        imports: [
-          AngularMaterialModule,
-          CommonModule,
-          WidgetModule,
-          PluginModule,
-          HttpClientTestingModule,
-          ComponentsModule,
-        ],
-        providers: [
-          provideMockStore(),
-          PluginServices,
-          {
-            provide: APPEND_SCRIPT_TOKEN,
-            useValue: spyfn.appendSrc
-          },
-          {
-            provide: REMOVE_SCRIPT_TOKEN,
-            useValue: () => Promise.resolve()
-          },
-          {
-            provide: DialogService,
-            useValue: {
-              getUserConfirm: () => Promise.resolve()
-            }
-          },
-          {
-            provide: PureContantService,
-            useValue: {
-              backendUrl: `http://localhost:3000/`
-            }
-          }
-        ]
-      }).compileComponents().then(() => {
-        
-        httpMock = TestBed.inject(HttpTestingController)
-        pluginService = TestBed.inject(PluginServices)
-        mockStore = TestBed.inject(MockStore)
-        pluginService.pluginViewContainerRef = {
-          createComponent: () => {
-            return {
-              onDestroy: () => {},
-              instance: {
-                elementRef: {
-                  nativeElement: {
-                    append: () => {}
-                  }
-                }
-              }
-            }
-          }
-        } as any
-
-        httpMock.expectOne('http://localhost:3000/plugins/manifests').flush('[]')
-
-        const widgetService = TestBed.inject(WidgetServices)
-        /**
-         * widget service floatingcontainer not inst in this circumstance
-         * TODO fix widget service tests importing widget service are not as flaky
-         */
-        widgetService.addNewWidget = () => {
-          return {} as any
-        }
-      })
-    }))
-
-    afterEach(() => {
-      spyfn.appendSrc.calls.reset()
-      const ctrl = TestBed.inject(HttpTestingController)
-      ctrl.verify()
-    })
-
-    it('> service can be inst', () => {
-      expect(pluginService).toBeTruthy()
-    })
-
-    it('expectOne is working as expected', done => {
-      
-      pluginService.fetch('test')
-        .then(text => {
-          expect(text).toEqual('bla')
-          done()
-        })
-      httpMock.expectOne('test').flush('bla')
-        
-    })
-
-    /**
-     * need to consider user confirmation on csp etc
-     */
-    describe('#launchPlugin', () => {
-
-      beforeEach(() => {
-        mockStore.overrideSelector(selectorPluginCspPermission, { value: false })
-      })
-
-      describe('> basic fetching functionality', () => {
-        it('> fetches templateURL and scriptURL properly', fakeAsync(() => {
-          
-          pluginService.launchPlugin({...MOCK_PLUGIN_MANIFEST})
-
-          tick(100)
-          
-          const mockTemplate = httpMock.expectOne(MOCK_PLUGIN_MANIFEST.templateURL)
-          mockTemplate.flush('hello world')
-          
-          tick(100)
-          
-          expect(spyfn.appendSrc).toHaveBeenCalledTimes(1)
-          expect(spyfn.appendSrc).toHaveBeenCalledWith(MOCK_PLUGIN_MANIFEST.scriptURL)
-          
-        }))
-
-        it('> template overrides templateURL', fakeAsync(() => {
-          pluginService.launchPlugin({
-            ...MOCK_PLUGIN_MANIFEST,
-            template: ''
-          })
-
-          tick(20)
-          httpMock.expectNone(MOCK_PLUGIN_MANIFEST.templateURL)
-        }))
-
-        it('> script with scriptURL throws', done => {
-          pluginService.launchPlugin({
-            ...MOCK_PLUGIN_MANIFEST,
-            script: '',
-            scriptURL: null
-          })
-            .then(() => {
-              /**
-               * should not pass
-               */
-              expect(true).toEqual(false)
-            })
-            .catch(e => {
-              done()
-            })
-          
-          /**
-           * http call will not be made, as rejection happens by Promise.reject, while fetch call probably happens at the next event cycle
-           */
-          httpMock.expectNone(MOCK_PLUGIN_MANIFEST.templateURL)
-        })
-      
-        describe('> user permission', () => {
-          let userConfirmSpy: jasmine.Spy
-          let readyPluginSpy: jasmine.Spy
-          let cspManifest = {
-            ...MOCK_PLUGIN_MANIFEST,
-            csp: {
-              'connect-src': [`'unsafe-eval'`]
-            }
-          }
-          afterEach(() => {
-            userConfirmSpy.calls.reset()
-            readyPluginSpy.calls.reset()
-          })
-          beforeEach(() => {
-            readyPluginSpy = spyOn(pluginService, 'readyPlugin').and.callFake(() => Promise.reject())
-            const dialogService = TestBed.inject(DialogService)
-            userConfirmSpy = spyOn(dialogService, 'getUserConfirm')
-          })
-
-          describe('> if user permission has been given', () => {
-            beforeEach(fakeAsync(() => {
-              mockStore.overrideSelector(selectorPluginCspPermission, { value: true })
-              userConfirmSpy.and.callFake(() => Promise.reject())
-              pluginService.launchPlugin({
-                ...cspManifest
-              }).catch(() => {
-                /**
-                 * expecting to throw because call fake returning promise.reject in beforeEach
-                 */
-              })
-              tick(20)
-            }))
-            it('> will not ask for permission', () => {
-              expect(userConfirmSpy).not.toHaveBeenCalled()
-            })
-
-            it('> will call ready plugin', () => {
-              expect(readyPluginSpy).toHaveBeenCalled()
-            })
-          })
-
-          describe('> if user permission has not yet been given', () => {
-            beforeEach(() => {
-              mockStore.overrideSelector(selectorPluginCspPermission, { value: false })
-            })
-            describe('> user permission', () => {
-              beforeEach(fakeAsync(() => {
-                pluginService.launchPlugin({
-                  ...cspManifest
-                }).catch(() => {
-                  /**
-                   * expecting to throw because call fake returning promise.reject in beforeEach
-                   */
-                })
-                tick(40)
-              }))
-              it('> will be asked for', () => {
-                expect(userConfirmSpy).toHaveBeenCalled()
-              })
-            })
-
-            describe('> if user accepts', () => {
-              beforeEach(fakeAsync(() => {
-                userConfirmSpy.and.callFake(() => Promise.resolve())
-
-                pluginService.launchPlugin({
-                  ...cspManifest
-                }).catch(() => {
-                  /**
-                   * expecting to throw because call fake returning promise.reject in beforeEach
-                   */
-                })
-              }))
-              it('> calls /POST user/pluginPermissions', () => {
-                httpMock.expectOne({
-                  method: 'POST',
-                  url: 'http://localhost:3000/user/pluginPermissions'
-                })
-              })
-            })
-
-            describe('> if user declines', () => {
-
-              beforeEach(fakeAsync(() => {
-                userConfirmSpy.and.callFake(() => Promise.reject())
-
-                pluginService.launchPlugin({
-                  ...cspManifest
-                }).catch(() => {
-                  /**
-                   * expecting to throw because call fake returning promise.reject in beforeEach
-                   */
-                })
-              }))
-              it('> calls /POST user/pluginPermissions', () => {
-                httpMock.expectNone({
-                  method: 'POST',
-                  url: 'http://localhost:3000/user/pluginPermissions'
-                })
-              })
-            })
-          })
-        })
-      })
-
-      describe('> racing slow connection when launching plugin', () => {
-        it('> when template/script has yet been fetched, repeated launchPlugin should not result in repeated fetching', fakeAsync(() => {
-
-          expect(pluginService.pluginIsLaunching(MOCK_PLUGIN_MANIFEST.name)).toBeFalsy()
-          expect(pluginService.pluginHasLaunched(MOCK_PLUGIN_MANIFEST.name)).toBeFalsy()
-          pluginService.launchPlugin({...MOCK_PLUGIN_MANIFEST})
-          pluginService.launchPlugin({...MOCK_PLUGIN_MANIFEST})
-          tick(20)
-          const req = httpMock.expectOne(MOCK_PLUGIN_MANIFEST.templateURL)
-          req.flush('baba')
-          tick(20)
-          expect(spyfn.appendSrc).toHaveBeenCalledTimes(1)
-
-          expect(
-            pluginService.pluginIsLaunching(MOCK_PLUGIN_MANIFEST.name) ||
-            pluginService.pluginHasLaunched(MOCK_PLUGIN_MANIFEST.name)
-          ).toBeTruthy()
-        }))
-      })
-    
-    })
-  })
-})
diff --git a/src/plugin/atlasViewer.pluginService.service.ts b/src/plugin/atlasViewer.pluginService.service.ts
deleted file mode 100644
index 668027764cf639fe3be327119a12fb4140352f08..0000000000000000000000000000000000000000
--- a/src/plugin/atlasViewer.pluginService.service.ts
+++ /dev/null
@@ -1,414 +0,0 @@
-import { HttpClient } from '@angular/common/http'
-import { ComponentFactory, ComponentFactoryResolver, Injectable, ViewContainerRef, Inject, SecurityContext } from "@angular/core";
-import { PLUGINSTORE_ACTION_TYPES } from "src/services/state/pluginState.helper";
-import { PluginUnit } from "./pluginUnit/pluginUnit.component";
-import { select, Store } from "@ngrx/store";
-import { BehaviorSubject, from, merge, Observable, of } from "rxjs";
-import { catchError, filter, map, mapTo, shareReplay, switchMap, switchMapTo, take, tap } from "rxjs/operators";
-import { LoggingService } from 'src/logging';
-import { WidgetUnit, WidgetServices } from "src/widget";
-import { APPEND_SCRIPT_TOKEN, REMOVE_SCRIPT_TOKEN, getHttpHeader } from 'src/util/constants';
-import { PluginFactoryDirective } from './pluginFactory.directive';
-import { selectorPluginCspPermission } from 'src/services/state/userConfigState.helper';
-import { DialogService } from 'src/services/dialogService.service';
-import { DomSanitizer } from '@angular/platform-browser';
-import { MatSnackBar } from '@angular/material/snack-bar';
-import { PureContantService } from 'src/util';
-
-const requiresReloadMd = `\n\n***\n\n**warning**: interactive atlas viewer **will** be reloaded in order for the change to take effect.`
-
-class PluginHandler {
-  public onShutdown: (callback: () => void) => void
-  public blink: (sec?: number) => void
-  public shutdown: () => void
-
-  public initState?: any
-  public initStateUrl?: string
-
-  public setInitManifestUrl: (url: string|null) => void
-
-  public setProgressIndicator: (progress: number) => void
-}
-
-export const registerPluginFactoryDirectiveFactory = (pSer: PluginServices) => {
-  return (pFactoryDirective: PluginFactoryDirective) => {
-    pSer.loadExternalLibraries = pFactoryDirective.loadExternalLibraries.bind(pFactoryDirective)
-    pSer.unloadExternalLibraries = pFactoryDirective.unloadExternalLibraries.bind(pFactoryDirective)
-    pSer.pluginViewContainerRef = pFactoryDirective.viewContainerRef
-  }
-}
-
-@Injectable({
-  providedIn : 'root',
-})
-
-export class PluginServices {
-
-  public pluginHandlersMap: Map<string, PluginHandler> = new Map()
-
-  public loadExternalLibraries: (libraries: string[]) => Promise<any> = () => Promise.reject(`fail to overwritten`)
-  public unloadExternalLibraries: (libraries: string[]) => void = () => { throw new Error(`failed to be overwritten`) }
-
-  public fetchedPluginManifests: IPluginManifest[] = []
-  public pluginViewContainerRef: ViewContainerRef
-
-  private pluginUnitFactory: ComponentFactory<PluginUnit>
-  public minimisedPlugins$: Observable<Set<string>>
-
-  /**
-   * TODO remove polyfil and convert all calls to this.fetch to http client
-   */
-  public fetch: (url: string, httpOption?: any) => Promise<any> = (url, httpOption = {}) => this.http.get(url, httpOption).toPromise()
-
-  constructor(
-    private widgetService: WidgetServices,
-    private cfr: ComponentFactoryResolver,
-    private store: Store<any>,
-    private dialogService: DialogService,
-    private snackbar: MatSnackBar,
-    private http: HttpClient,
-    private log: LoggingService,
-    private sanitizer: DomSanitizer,
-    private constantSvc: PureContantService,
-    @Inject(APPEND_SCRIPT_TOKEN) private appendSrc: (src: string) => Promise<HTMLScriptElement>,
-    @Inject(REMOVE_SCRIPT_TOKEN) private removeSrc: (src: HTMLScriptElement) => void,
-  ) {
-
-    this.pluginUnitFactory = this.cfr.resolveComponentFactory( PluginUnit )
-
-    /**
-     * TODO convert to rxjs streams, instead of Promise.all
-     */
-    const pluginManifestsUrl = `${this.constantSvc.backendUrl}plugins/manifests`
-
-    this.http.get<IPluginManifest[]>(pluginManifestsUrl, {
-      responseType: 'json',
-      headers: getHttpHeader(),
-    }).subscribe(
-      arr => this.fetchedPluginManifests = arr,
-      this.log.error,
-    )
-
-    this.minimisedPlugins$ = merge(
-      of(new Set()),
-      this.widgetService.minimisedWindow$,
-    ).pipe(
-      map(set => {
-        const returnSet = new Set<string>()
-        for (const [pluginName, wu] of this.mapPluginNameToWidgetUnit) {
-          if (set.has(wu)) {
-            returnSet.add(pluginName)
-          }
-        }
-        return returnSet
-      }),
-      shareReplay(1),
-    )
-
-    this.launchedPlugins$ = new BehaviorSubject(new Set())
-  }
-
-  public launchNewWidget = (manifest) => this.launchPlugin(manifest)
-    .then(handler => {
-      this.orphanPlugins.add(manifest)
-      handler.onShutdown(() => {
-        this.orphanPlugins.delete(manifest)
-      })
-    })
-
-  public readyPlugin(plugin: IPluginManifest): Promise<any> {
-    const isDefined = input => typeof input !== 'undefined' && input !== null
-    if (!isDefined(plugin.scriptURL)) {
-      return Promise.reject(`inline script has been deprecated. use scriptURL instead`)
-    }
-    if (isDefined(plugin.template)) {
-      return Promise.resolve()
-    }
-    if (plugin.templateURL) {
-      return this.fetch(plugin.templateURL, {responseType: 'text'})
-        .then(template => {
-          plugin.template = template
-        })
-    }
-    return Promise.reject('both template and templateURL are not defined')
-  }
-
-  private launchedPlugins: Set<string> = new Set()
-  public launchedPlugins$: BehaviorSubject<Set<string>>
-  public pluginHasLaunched(pluginName: string) {
-    return this.launchedPlugins.has(pluginName)
-  }
-  public addPluginToLaunchedSet(pluginName: string) {
-    this.launchedPlugins.add(pluginName)
-    this.launchedPlugins$.next(this.launchedPlugins)
-  }
-  public removePluginFromLaunchedSet(pluginName: string) {
-    this.launchedPlugins.delete(pluginName)
-    this.launchedPlugins$.next(this.launchedPlugins)
-  }
-
-  public pluginIsLaunching(pluginName: string) {
-    return this.launchingPlugins.has(pluginName)
-  }
-  public addPluginToIsLaunchingSet(pluginName: string) {
-    this.launchingPlugins.add(pluginName)
-  }
-  public removePluginFromIsLaunchingSet(pluginName: string) {
-    this.launchingPlugins.delete(pluginName)
-  }
-
-  private mapPluginNameToWidgetUnit: Map<string, WidgetUnit> = new Map()
-
-  public pluginIsMinimised(pluginName: string) {
-    return this.widgetService.isMinimised( this.mapPluginNameToWidgetUnit.get(pluginName) )
-  }
-
-  private launchingPlugins: Set<string> = new Set()
-  public orphanPlugins: Set<IPluginManifest> = new Set()
-
-  public async revokePluginPermission(pluginKey: string) {
-    const createRevokeMd = (pluginKey: string) => `You are about to revoke the permission given to ${pluginKey}.${requiresReloadMd}`
-
-    try {
-      await this.dialogService.getUserConfirm({
-        markdown: createRevokeMd(pluginKey)
-      })
-
-      this.http.delete(
-        `${this.constantSvc.backendUrl}user/pluginPermissions/${encodeURIComponent(pluginKey)}`, 
-        {
-          headers: getHttpHeader()
-        }
-      ).subscribe(
-        () => {
-          window.location.reload()
-        },
-        err => {
-          this.snackbar.open(`Error revoking plugin permission ${err.toString()}`, 'Dismiss')
-        }
-      )
-    } catch (_e) {
-      /**
-       * user cancelled workflow
-       */
-    }
-  }
-
-  public async launchPlugin(plugin: IPluginManifest): Promise<PluginHandler> {
-    if (this.pluginIsLaunching(plugin.name)) {
-      // plugin launching please be patient
-      // TODO add visual feedback
-      return
-    }
-    if ( this.pluginHasLaunched(plugin.name)) {
-      // plugin launched
-      // TODO add visual feedback
-
-      // if widget window is minimized, maximize it
-
-      const wu = this.mapPluginNameToWidgetUnit.get(plugin.name)
-      if (this.widgetService.isMinimised(wu)) {
-        this.widgetService.unminimise(wu)
-      } else {
-        this.widgetService.minimise(wu)
-      }
-      return
-    }
-
-    this.addPluginToIsLaunchingSet(plugin.name)
-
-    const { csp, displayName, name = '', version = 'latest' } = plugin
-    const pluginKey = `${name}::${version}`
-    const createPermissionMd = ({ csp, name, version }) => {
-      const sanitize = val =>  this.sanitizer.sanitize(SecurityContext.HTML, val)
-      const getCspRow = ({ key }) => {
-        return `| ${sanitize(key)} | ${csp[key].map(v => '`' + sanitize(v) + '`').join(',')} |`
-      }
-      return `**${sanitize(displayName || name)}** version **${sanitize(version)}** requires additional permission from you to run:\n\n| permission | detail |\n| --- | --- |\n${Object.keys(csp).map(key => getCspRow({ key })).join('\n')}${requiresReloadMd}`
-    } 
-
-    await new Promise((rs, rj) => {
-      this.store.pipe(
-        select(selectorPluginCspPermission, { key: pluginKey }),
-        take(1),
-        switchMap(userAgreed => {
-          if (userAgreed.value) return of(true)
-
-          /**
-           * check if csp exists
-           */
-          if (!csp || Object.keys(csp).length === 0) {
-            return of(true)
-          }
-          /**
-           * TODO: check do not ask status
-           */
-          return from(
-            this.dialogService.getUserConfirm({
-              markdown: createPermissionMd({ csp, name, version })
-            })
-          ).pipe(
-            mapTo(true),
-            catchError(() => of(false)),
-            filter(v => !!v),
-            switchMapTo(
-              this.http.post(`${this.constantSvc.backendUrl}user/pluginPermissions`, 
-                { [pluginKey]: csp },
-                {
-                  responseType: 'json',
-                  headers: getHttpHeader()
-                })
-            ),
-            tap(() => {
-              window.location.reload()
-            }),
-            mapTo(false)
-          )
-        }),
-        take(1),
-      ).subscribe(
-        val => val ? rs(null) : rj(`val is falsy`),
-        err => rj(err)
-      )
-    })
-
-    await this.readyPlugin(plugin)
-
-    /**
-     * catch when pluginViewContainerRef as not been overwritten?
-     */
-    if (!this.pluginViewContainerRef) {
-      throw new Error(`pluginViewContainerRef not populated`)
-    }
-    const pluginUnit = this.pluginViewContainerRef.createComponent( this.pluginUnitFactory )
-    /* TODO in v0.2, I used:
-
-    const template = document.createElement('div')
-    template.insertAdjacentHTML('afterbegin',template)
-
-    // reason was:
-    // changed from innerHTML to insertadjacenthtml to accomodate angular elements ... not too sure about the actual ramification
-
-    */
-
-    const handler = new PluginHandler()
-    this.pluginHandlersMap.set(plugin.name, handler)
-
-    /**
-     * define the handler properties prior to appending plugin script
-     * so that plugin script can access properties w/o timeout
-     */
-    handler.initState = plugin.initState
-      ? plugin.initState
-      : null
-
-    handler.initStateUrl = plugin.initStateUrl
-      ? plugin.initStateUrl
-      : null
-
-    handler.setInitManifestUrl = (url) => this.store.dispatch({
-      type : PLUGINSTORE_ACTION_TYPES.SET_INIT_PLUGIN,
-      manifest : {
-        name : plugin.name,
-        initManifestUrl : url,
-      },
-    })
-
-    const shutdownCB = [
-      () => {
-        this.removePluginFromLaunchedSet(plugin.name)
-      },
-    ]
-
-    handler.onShutdown = (cb) => {
-      if (typeof cb !== 'function') {
-        this.log.warn('onShutdown requires the argument to be a function')
-        return
-      }
-      shutdownCB.push(cb)
-    }
-
-    const scriptEl = await this.appendSrc(plugin.scriptURL)
-
-    handler.onShutdown(() => this.removeSrc(scriptEl))
-
-    const template = document.createElement('div')
-    template.insertAdjacentHTML('afterbegin', plugin.template)
-    pluginUnit.instance.elementRef.nativeElement.append( template )
-
-    const widgetCompRef = this.widgetService.addNewWidget(pluginUnit, {
-      state : 'floating',
-      exitable : true,
-      persistency: plugin.persistency,
-      title : plugin.displayName || plugin.name,
-    })
-
-    this.addPluginToLaunchedSet(plugin.name)
-    this.removePluginFromIsLaunchingSet(plugin.name)
-
-    this.mapPluginNameToWidgetUnit.set(plugin.name, widgetCompRef.instance)
-
-    const unsubscribeOnPluginDestroy = []
-
-    // TODO deprecate sec
-    handler.blink = (_sec?: number) => {
-      widgetCompRef.instance.blinkOn = true
-    }
-
-    handler.setProgressIndicator = (val) => widgetCompRef.instance.progressIndicator = val
-
-    handler.shutdown = () => {
-      widgetCompRef.instance.exit()
-    }
-
-    handler.onShutdown(() => {
-      unsubscribeOnPluginDestroy.forEach(s => s.unsubscribe())
-      this.pluginHandlersMap.delete(plugin.name)
-      this.mapPluginNameToWidgetUnit.delete(plugin.name)
-    })
-
-    pluginUnit.onDestroy(() => {
-      while (shutdownCB.length > 0) {
-        shutdownCB.pop()()
-      }
-    })
-
-    return handler
-  }
-
-  public async addPluginViaManifestUrl(manifestUrl: string){
-    try {
-      const json = await this.fetch(manifestUrl)
-      this.fetchedPluginManifests = [
-        ...this.fetchedPluginManifests,
-        json
-      ]
-    } catch (e) {
-      throw new Error(e.statusText)
-    }
-  }
-}
-
-export interface IPluginManifest {
-  name?: string
-  version?: string
-  displayName?: string
-  templateURL?: string
-  template?: string
-  scriptURL?: string
-  script?: string
-  initState?: any
-  initStateUrl?: string
-  persistency?: boolean
-
-  description?: string
-  desc?: string
-
-  homepage?: string
-  authors?: string
-
-  csp?: {
-    'connect-src'?: string[]
-    'script-src'?: string[]
-  }
-}
diff --git a/src/plugin/broadcast.md b/src/plugin/broadcast.md
new file mode 100644
index 0000000000000000000000000000000000000000..bcc96cd4add3e291b62df5557c1afdc0f9a9501d
--- /dev/null
+++ b/src/plugin/broadcast.md
@@ -0,0 +1,64 @@
+# Broadcasting API
+
+Broadcasting messages are sent under two circumstances:
+
+- the state of the viewer changed, initiated by any source (user, plugin etc). Sent to all active plugin clients.
+
+- immediately after the plugin client acknowledged `handshake.init` to the specific client. This is so that the client can get the current state of the viewer.
+
+Broadcasting messages never expects a response (and thus will never contain and `id` attribute)
+
+<!-- the API reference below are auto generated by generateTypes.js  -->
+<!-- do not edit, as the edit will be overwritten by the auto generation -->
+
+## API
+
+### `sxplr.on.atlasSelected`
+
+- payload
+
+  ```ts
+  SapiAtlasModel
+  ```
+
+
+
+### `sxplr.on.templateSelected`
+
+- payload
+
+  ```ts
+  SapiSpaceModel
+  ```
+
+
+
+### `sxplr.on.parcellationSelected`
+
+- payload
+
+  ```ts
+  SapiParcellationModel
+  ```
+
+
+
+### `sxplr.on.allRegions`
+
+- payload
+
+  ```ts
+  SapiRegionModel[]
+  ```
+
+
+
+### `sxplr.on.regionsSelected`
+
+- payload
+
+  ```ts
+  SapiRegionModel[]
+  ```
+
+
diff --git a/src/plugin/const.ts b/src/plugin/const.ts
new file mode 100644
index 0000000000000000000000000000000000000000..474bc36bf2cc119f845074735cc3073368db6a13
--- /dev/null
+++ b/src/plugin/const.ts
@@ -0,0 +1,16 @@
+import { InjectionToken } from "@angular/core"
+
+const PLUGIN_SRC_KEY = "x-plugin-portal-src"
+
+export function setPluginSrc(src: string, record: Record<string, unknown> = {}){
+  return {
+    ...record,
+    [PLUGIN_SRC_KEY]: src
+  }
+}
+
+export function getPluginSrc(record: Record<string, string> = {}){
+  return record[PLUGIN_SRC_KEY]
+}
+
+export const SET_PLUGIN_NAME = new InjectionToken('SET_PLUGIN_NAME')
diff --git a/src/plugin/generateTypes.js b/src/plugin/generateTypes.js
new file mode 100644
index 0000000000000000000000000000000000000000..52f69d8f99921f02bbaf4f3949fdc212a2cd4203
--- /dev/null
+++ b/src/plugin/generateTypes.js
@@ -0,0 +1,96 @@
+const ts = require('typescript')
+const fs = require('fs')
+const path = require('path')
+const { promisify } = require('util')
+const asyncReadFile = promisify(fs.readFile)
+const asyncWriteFile = promisify(fs.writeFile)
+const { processTypeAliasDeclaration, processRequestTypeAlias } = require('./tsUtil')
+
+
+const typeAliasDeclarationMap = new Map()
+const pathToApiService = path.join(__dirname, '../api/service.ts')
+const NAMESPACE = `sxplr`
+const filenames = {
+  handshake: path.join(__dirname, './handshake.md'),
+  broadcast: path.join(__dirname, './broadcast.md'),
+  request: path.join(__dirname, './request.md'),
+}
+
+const puplateBroadCast = async broadcastNode => {
+  
+  if (!broadcastNode) throw new Error(`broadcastNode must be passed!`)
+
+  const src = await asyncReadFile(filenames.broadcast, 'utf-8')
+  const output = processTypeAliasDeclaration(broadcastNode)
+
+  let outputText = ``
+  for (const key in output) {
+    outputText += `
+
+### \`${NAMESPACE}.on.${key}\`
+
+- payload
+
+  \`\`\`ts
+  ${output[key]}
+  \`\`\`
+
+`
+  }
+  const newData = src.replace(/## API(.|\n)+/, s => `## API${outputText}\n`)
+
+  await asyncWriteFile(filenames.broadcast, newData, 'utf-8')
+}
+
+const populateConversations = async (filename, node) => {
+  const src = await asyncReadFile(filename, 'utf-8')
+  const output = processRequestTypeAlias(node, typeAliasDeclarationMap)
+      
+  let outputText = ``
+  for (const key in output) {
+    outputText += `
+### \`${NAMESPACE}.${key}\`
+
+- request
+
+  \`\`\`ts
+  ${output[key]['request']}
+  \`\`\`
+
+- response
+
+  \`\`\`ts
+  ${output[key]['response']}
+  \`\`\`
+
+`
+  }
+  const newData = src.replace(/## API(.|\n)+/, s => `## API${outputText}`)
+  await asyncWriteFile(filename, newData, 'utf-8')
+}
+
+const main = async () => {
+  const src = await asyncReadFile(pathToApiService, 'utf-8')
+  const node = ts.createSourceFile(
+    './x.ts',
+    src,
+    ts.ScriptTarget.Latest
+  )
+  node.forEachChild(n => {
+    if (ts.SyntaxKind[n.kind] === "TypeAliasDeclaration") {
+      typeAliasDeclarationMap.set(n.name?.text, n)
+    }
+    if (n.name?.text === "BroadCastingApiEvents") {
+      puplateBroadCast(n)
+    }
+    if (n.name?.text === "HeartbeatEvents") {
+      populateConversations(filenames.handshake, n)
+    }
+    if (n.name?.text === "ApiBoothEvents") {
+      populateConversations(filenames.request, n)
+    }
+  })
+  
+}
+
+main()
diff --git a/src/plugin/handshake.md b/src/plugin/handshake.md
new file mode 100644
index 0000000000000000000000000000000000000000..a55a97d23d23215df31d6f4ac33142d4066330d9
--- /dev/null
+++ b/src/plugin/handshake.md
@@ -0,0 +1,22 @@
+# Handshake API
+
+Handshake messages are meant for siibra-explorer to probe if the plugin is alive and well (and also a way for the plugin to check if siibra-explorer is responsive)
+
+<!-- the API reference below are auto generated by generateTypes.js  -->
+<!-- do not edit, as the edit will be overwritten by the auto generation -->
+
+## API
+### `sxplr.init`
+
+- request
+
+  ```ts
+  null
+  ```
+
+- response
+
+  ```ts
+  {"name": string}
+  ```
+
diff --git a/src/plugin/iframeSrc.pipe.ts b/src/plugin/iframeSrc.pipe.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0bc0916b5edb23234dbc089239fad37f547d28bc
--- /dev/null
+++ b/src/plugin/iframeSrc.pipe.ts
@@ -0,0 +1,23 @@
+import { Pipe, PipeTransform, SecurityContext } from "@angular/core";
+import { DomSanitizer, SafeResourceUrl } from "@angular/platform-browser";
+
+@Pipe({
+  name: 'iframeSrc',
+  pure: true
+})
+
+export class IFrameSrcPipe implements PipeTransform {
+  constructor(private domSanitizer: DomSanitizer){}
+
+  transform(src: string): SafeResourceUrl {
+    // https://angular.io/guide/security#sanitization-and-security-contexts
+    // Sanitizing resource url isn't possible
+    // hence bypassing
+    return this.domSanitizer.bypassSecurityTrustResourceUrl(
+      this.domSanitizer.sanitize(
+        SecurityContext.URL,
+        src
+      )
+    )
+  }
+}
diff --git a/src/plugin/index.ts b/src/plugin/index.ts
index bae5875b5d19c8b096188c6c8074e42a2ff14674..480c8fb59b4e00addf28512aede8cead1c33df70 100644
--- a/src/plugin/index.ts
+++ b/src/plugin/index.ts
@@ -1,9 +1,3 @@
-export {
-  IPluginManifest,
-  PluginServices,
-  registerPluginFactoryDirectiveFactory,
-} from './atlasViewer.pluginService.service'
-
 export {
   PluginModule
 } from './plugin.module'
\ No newline at end of file
diff --git a/src/plugin/plugin.module.ts b/src/plugin/plugin.module.ts
index ae859bab6ea49c0ab4be9f1d2e2adb4956249dbe..ea1ec730ae3867562e47a66399e7fd5ecc531f17 100644
--- a/src/plugin/plugin.module.ts
+++ b/src/plugin/plugin.module.ts
@@ -1,14 +1,14 @@
 import { CommonModule, DOCUMENT } from "@angular/common";
+import { HttpClientModule } from "@angular/common/http";
 import { NgModule } from "@angular/core";
 import { LoggingModule } from "src/logging";
 import { AngularMaterialModule } from "src/sharedModules";
 import { UtilModule } from "src/util";
 import { appendScriptFactory, APPEND_SCRIPT_TOKEN, removeScriptFactory, REMOVE_SCRIPT_TOKEN } from "src/util/constants";
-import { PluginServices, registerPluginFactoryDirectiveFactory } from "./atlasViewer.pluginService.service";
+import { IFrameSrcPipe } from "./iframeSrc.pipe";
 import { PluginBannerUI } from "./pluginBanner/pluginBanner.component";
-import { PluginCspCtrlCmp } from "./pluginCsp/pluginCsp.component";
-import { PluginFactoryDirective, REGISTER_PLUGIN_FACTORY_DIRECTIVE } from "./pluginFactory.directive";
-import { PluginUnit } from "./pluginUnit/pluginUnit.component";
+import { PluginPortal } from "./pluginPortal/pluginPortal.component";
+
 
 @NgModule({
   imports: [
@@ -16,27 +16,17 @@ import { PluginUnit } from "./pluginUnit/pluginUnit.component";
     LoggingModule,
     UtilModule,
     AngularMaterialModule,
+    HttpClientModule,
   ],
   declarations: [
-    PluginCspCtrlCmp,
-    PluginUnit,
-    PluginFactoryDirective,
     PluginBannerUI,
+    PluginPortal,
+    IFrameSrcPipe,
   ],
   exports: [
-    PluginCspCtrlCmp,
     PluginBannerUI,
-    PluginUnit,
-    PluginFactoryDirective,
   ],
   providers: [
-
-    PluginServices,
-    {
-      provide: REGISTER_PLUGIN_FACTORY_DIRECTIVE,
-      useFactory: registerPluginFactoryDirectiveFactory,
-      deps: [ PluginServices ]
-    },
     {
       provide: APPEND_SCRIPT_TOKEN,
       useFactory: appendScriptFactory,
diff --git a/src/plugin/pluginBanner/pluginBanner.component.ts b/src/plugin/pluginBanner/pluginBanner.component.ts
index 689f0aa32bfc4a7c16442bbc22e56c95d44a2b0d..df682e4cfd68a3cefac4a8b9952670cdcfb49bb0 100644
--- a/src/plugin/pluginBanner/pluginBanner.component.ts
+++ b/src/plugin/pluginBanner/pluginBanner.component.ts
@@ -1,8 +1,10 @@
-import { Component, ViewChild, TemplateRef } from "@angular/core";
-import { IPluginManifest, PluginServices } from "../atlasViewer.pluginService.service";
+import { Component, TemplateRef } from "@angular/core";
 import { MatDialog } from "@angular/material/dialog";
 import { environment } from 'src/environments/environment';
-import { MatSnackBar } from "@angular/material/snack-bar";
+import { PluginService } from "../service";
+import { PluginManifest } from "../types";
+import { combineLatest, Observable, Subject } from "rxjs";
+import { map, scan, startWith } from "rxjs/operators";
 
 @Component({
   selector : 'plugin-banner',
@@ -16,28 +18,14 @@ export class PluginBannerUI {
 
   EXPERIMENTAL_FEATURE_FLAG = environment.EXPERIMENTAL_FEATURE_FLAG
 
-  @ViewChild('pluginInfoTmpl', { read: TemplateRef })
-  private pluginInfoTmpl: TemplateRef<any>
-
   constructor(
-    public pluginServices: PluginServices,
+    private svc: PluginService,
     private matDialog: MatDialog,
-    private matSnackbar: MatSnackBar,
   ) {
   }
 
-  public clickPlugin(plugin: IPluginManifest) {
-    this.pluginServices.launchPlugin(plugin)
-  }
-
-  public showPluginInfo(manifest: IPluginManifest){
-    this.matDialog.open(
-      this.pluginInfoTmpl,
-      {
-        data: manifest,
-        ariaLabel: `Additional information about a plugin`
-      }
-    )
+  public launchPlugin(plugin: PluginManifest) {
+    this.svc.launchPlugin(plugin.iframeUrl)
   }
 
   public showTmpl(tmpl: TemplateRef<any>){
@@ -46,21 +34,25 @@ export class PluginBannerUI {
     })
   }
 
-  public loadingThirdpartyPlugin = false
-
-  public async addThirdPartyPlugin(manifestUrl: string) {
-    this.loadingThirdpartyPlugin = true
-    try {
-      await this.pluginServices.addPluginViaManifestUrl(manifestUrl)
-      this.loadingThirdpartyPlugin = false
-      this.matSnackbar.open(`Adding plugin successful`, 'Dismiss', {
-        duration: 5000
-      })
-    } catch (e) {
-      this.loadingThirdpartyPlugin = false
-      this.matSnackbar.open(`Error adding plugin: ${e.toString()}`, 'Dismiss', {
-        duration: 5000
-      })
-    }
+  private thirdpartyPlugin$: Subject<{name: 'Added Plugin', iframeUrl: string}> = new Subject()
+
+  availablePlugins$: Observable<{
+    name: string
+    iframeUrl: string
+  }[]> = combineLatest([
+    this.svc.pluginManifests$,
+    this.thirdpartyPlugin$.pipe(
+      scan((acc, curr) => acc.concat(curr), []),
+      startWith([])
+    ),
+  ]).pipe(
+    map(([builtIn, thirdParty]) => [...builtIn, ...thirdParty])
+  )
+
+  public addThirdPartyPlugin(iframeUrl: string) {
+    this.thirdpartyPlugin$.next({
+      name: 'Added Plugin',
+      iframeUrl
+    })
   }
 }
diff --git a/src/plugin/pluginBanner/pluginBanner.template.html b/src/plugin/pluginBanner/pluginBanner.template.html
index 3e452612ebef6347181b825c02c22c5ba2ce8c74..4f9ef3ae2332b37d845eddc838c5519ce787dd2d 100644
--- a/src/plugin/pluginBanner/pluginBanner.template.html
+++ b/src/plugin/pluginBanner/pluginBanner.template.html
@@ -1,18 +1,9 @@
 <mat-action-list>
   <button mat-menu-item
-    *ngFor="let plugin of pluginServices.fetchedPluginManifests"
-    [matTooltip]="plugin.displayName ? plugin.displayName : plugin.name"
-    (click)="clickPlugin(plugin)">
-    <span mat-icon-button
-      aria-label="About this plugin"
-      class="mat-icon d-inline-flex align-items-center justify-content-center fa-stack"
-      (click)="showPluginInfo(plugin)"
-      iav-stop="click mousedown mouseup">
-      <i class="fas fa-cube fa-stack-1x"></i>
-      <i class="fas fa-info-circle fa-stack-1x sub"></i>
-    </span>
+    *ngFor="let plugin of availablePlugins$ | async"
+    (click)="launchPlugin(plugin)">
     <span>
-      {{ plugin.displayName ? plugin.displayName : plugin.name }}
+      {{ plugin.name }}
     </span>
   </button>
 
@@ -33,9 +24,9 @@
     <form>
       <mat-form-field class="d-block">
         <mat-label>
-          manifest.json URL
+          iframe index.html URL
         </mat-label>
-        <input type="text" matInput placeholder="https://example.com/manifest.json" #urlInput>
+        <input type="text" matInput placeholder="https://example.com/index.html" #urlInput>
       </mat-form-field>
     </form>
   </mat-dialog-content>
@@ -43,7 +34,6 @@
   <mat-dialog-actions align="end">
     <button (click)="addThirdPartyPlugin(urlInput.value)"
       mat-raised-button
-      [disabled]="loadingThirdpartyPlugin"
       color="primary">
       Load
     </button>
@@ -54,57 +44,3 @@
   </mat-dialog-actions>
 
 </ng-template>
-
-<ng-template #pluginInfoTmpl let-manifest>
-  <h1 mat-dialog-title>
-    About {{ manifest.displayName || manifest.name }}
-  </h1>
-
-  <div mat-dialog-content>
-    <mat-list>
-      <mat-list-item>
-        <span mat-list-icon class="d-inline-flex justify-content-center align-items-center">
-          <i class="fas fa-info"></i>
-        </span>
-        <div mat-line>
-          Description
-        </div>
-        <div mat-line>
-          {{ manifest.description || manifest.desc || 'Not provided.' }}
-        </div>
-      </mat-list-item>
-
-      <mat-list-item>
-        <span mat-list-icon class="d-inline-flex justify-content-center align-items-center">
-          <i class="fas fa-users"></i>
-        </span>
-        <div mat-line>
-          Authors
-        </div>
-        <div mat-line>
-          {{ manifest.authors || 'Not provided' }}
-        </div>
-      </mat-list-item>
-
-      <mat-list-item>
-        <span mat-list-icon class="d-inline-flex justify-content-center align-items-center">
-          <i class="fas fa-globe-europe"></i>
-        </span>
-        <div mat-line>
-          Homepage
-        </div>
-        <div mat-line>
-          {{ manifest.homepage || 'Not provided' }}
-        </div>
-      </mat-list-item>
-    </mat-list>
-
-  </div>
-
-  <div mat-dialog-actions class="d-flex justify-content-center">
-    <button mat-button mat-dialog-close>
-      close
-    </button>
-  </div>
-  
-</ng-template>
diff --git a/src/plugin/pluginCsp/pluginCsp.component.ts b/src/plugin/pluginCsp/pluginCsp.component.ts
deleted file mode 100644
index 7ff3c8a1fcfcc7faa96237f9cbb9ad539104812c..0000000000000000000000000000000000000000
--- a/src/plugin/pluginCsp/pluginCsp.component.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-import { Component } from "@angular/core";
-import { select, Store } from "@ngrx/store";
-import { map } from "rxjs/operators";
-import { PluginServices } from "../atlasViewer.pluginService.service";
-import { selectorAllPluginsCspPermission } from "src/services/state/userConfigState.store";
-
-@Component({
-  selector: 'plugin-csp-controller',
-  templateUrl: './pluginCsp.template.html',
-  styleUrls: [
-    './pluginCsp.style.css'
-  ]
-})
-
-export class PluginCspCtrlCmp{
-
-  public pluginCsp$ = this.store$.pipe(
-    select(selectorAllPluginsCspPermission),
-    map(pluginCsp => Object.keys(pluginCsp).map(key => ({ pluginKey: key, pluginCsp: pluginCsp[key] }))),
-  )
-
-  constructor(
-    private store$: Store<any>,
-    private pluginService: PluginServices,
-  ){
-
-  }
-
-  revoke(pluginKey: string){
-    this.pluginService.revokePluginPermission(pluginKey)
-  }
-}
\ No newline at end of file
diff --git a/src/plugin/pluginCsp/pluginCsp.template.html b/src/plugin/pluginCsp/pluginCsp.template.html
deleted file mode 100644
index 16159fa0bf2b0525eed22819c9d06d4315fec6e4..0000000000000000000000000000000000000000
--- a/src/plugin/pluginCsp/pluginCsp.template.html
+++ /dev/null
@@ -1,52 +0,0 @@
-
-<ng-container *ngIf="pluginCsp$ | async as pluginsCsp; else fallbackTmpl">
-  
-  <ng-template #pluginsCspContainerTmpl>
-    <ng-container *ngTemplateOutlet="pluginCpTmpl; context: { pluginsCsp: pluginsCsp }">
-    </ng-container>  
-  </ng-template>
-  
-  <ng-container *ngIf="pluginsCsp.length === 0; else pluginsCspContainerTmpl">
-    <ng-container *ngTemplateOutlet="fallbackTmpl">
-    </ng-container>
-  </ng-container>
-</ng-container>
-
-<ng-template #fallbackTmpl>
-  You have not granted permission to any plugins.
-</ng-template>
-
-<ng-template #pluginCpTmpl let-pluginsCsp="pluginsCsp">
-  <p>
-    You have granted permission to the following plugins
-  </p>
-  
-  <mat-accordion>
-    <mat-expansion-panel *ngFor="let pluginCsp of pluginCsp$ | async">
-      <mat-expansion-panel-header>
-        <mat-panel-title>
-          {{ pluginCsp['pluginKey'] }}
-        </mat-panel-title>
-      </mat-expansion-panel-header>
-  
-      <button mat-raised-button
-        color="warn"
-        (click)="revoke(pluginCsp['pluginKey'])">
-        Revoke
-      </button>
-  
-      <mat-list>
-        <ng-container *ngFor="let csp of pluginCsp['pluginCsp'] | keyvalue">
-          <span mat-subheader>
-            {{ csp['key'] }}
-          </span>
-          <mat-list-item *ngFor="let item of csp['value']">
-            {{ item }}
-          </mat-list-item>
-        </ng-container>
-      </mat-list>
-  
-    </mat-expansion-panel>
-  </mat-accordion>
-  
-</ng-template>
diff --git a/src/plugin/pluginFactory.directive.spec.ts b/src/plugin/pluginFactory.directive.spec.ts
deleted file mode 100644
index 29d723d972543473d647354c82dd829dfaa4dda3..0000000000000000000000000000000000000000
--- a/src/plugin/pluginFactory.directive.spec.ts
+++ /dev/null
@@ -1,133 +0,0 @@
-import { async, TestBed } from "@angular/core/testing"
-import { PluginFactoryDirective, REGISTER_PLUGIN_FACTORY_DIRECTIVE } from "./pluginFactory.directive"
-import { Component, ViewChild } from "@angular/core"
-import { APPEND_SCRIPT_TOKEN, REMOVE_SCRIPT_TOKEN } from "src/util/constants"
-import { By } from "@angular/platform-browser"
-
-@Component({
-  template: '<div></div>'
-})
-class TestCmp{
-
-  @ViewChild(PluginFactoryDirective) pfd: PluginFactoryDirective
-}
-
-const dummyObj1 = {}
-const dummyObj2 = {}
-const appendSrcSpy = jasmine.createSpy('appendSrc').and.returnValues(
-  Promise.resolve(dummyObj1),
-  Promise.resolve(dummyObj2)
-)
-const removeSrcSpy = jasmine.createSpy('removeScript')
-const registerSpy = jasmine.createSpy('registerSpy')
-
-describe(`> pluginFactory.directive.ts`, () => {
-  describe(`> PluginFactoryDirective`, () => {
-
-    beforeEach(async(() => {
-      TestBed.configureTestingModule({
-        declarations: [
-          PluginFactoryDirective,
-          TestCmp
-        ],
-        providers: [
-          {
-            provide: APPEND_SCRIPT_TOKEN,
-            useValue: appendSrcSpy
-          },
-          {
-            provide: REMOVE_SCRIPT_TOKEN,
-            useValue: removeSrcSpy
-          },
-          {
-            provide: REGISTER_PLUGIN_FACTORY_DIRECTIVE,
-            useValue: registerSpy
-          }
-        ]
-      }).overrideComponent(TestCmp, {
-        set: {
-          template: `<div pluginFactoryDirective></div>`
-        }
-      }).compileComponents()
-    }))
-
-    afterEach(() => {
-      appendSrcSpy.calls.reset()
-      removeSrcSpy.calls.reset()
-      registerSpy.calls.reset()
-    })
-
-    it('> creates directive', () => {
-      const fixture = TestBed.createComponent(TestCmp)
-      fixture.detectChanges()
-
-      const queriedDirective = fixture.debugElement.query( By.directive(PluginFactoryDirective) )
-      expect(queriedDirective).toBeTruthy()
-    })
-
-    it('> register spy is called', () => {
-      
-      const fixture = TestBed.createComponent(TestCmp)
-      fixture.detectChanges()
-      expect(registerSpy).toHaveBeenCalledWith(fixture.componentInstance.pfd)
-    })
-
-    describe('> loading external libraries', () => {
-      it('> load once, call append script', async () => {
-        const fixture = TestBed.createComponent(TestCmp)
-        fixture.detectChanges()
-        const pfd = fixture.componentInstance.pfd
-        await pfd.loadExternalLibraries(['vue@2.5.16'])
-        expect(appendSrcSpy).toHaveBeenCalledWith('https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js')
-        expect(appendSrcSpy).toHaveBeenCalledTimes(1)
-      })
-
-      it('> load twice, called append script once', async () => {
-        const fixture = TestBed.createComponent(TestCmp)
-        fixture.detectChanges()
-        const pfd = fixture.componentInstance.pfd
-        await pfd.loadExternalLibraries(['vue@2.5.16'])
-        await pfd.loadExternalLibraries(['vue@2.5.16'])
-        expect(appendSrcSpy).toHaveBeenCalledWith('https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js')
-        expect(appendSrcSpy).toHaveBeenCalledTimes(1)
-      })
-
-      it('> load unload, call remove script once', async () => {
-        
-        const fixture = TestBed.createComponent(TestCmp)
-        fixture.detectChanges()
-        const pfd = fixture.componentInstance.pfd
-        await pfd.loadExternalLibraries(['vue@2.5.16'])
-        pfd.unloadExternalLibraries(['vue@2.5.16'])
-        expect(removeSrcSpy).toHaveBeenCalledTimes(1)
-      })
-
-      it('> load twice, unload, does not call remove', async () => {
-
-        const fixture = TestBed.createComponent(TestCmp)
-        fixture.detectChanges()
-        const pfd = fixture.componentInstance.pfd
-        await pfd.loadExternalLibraries(['vue@2.5.16'])
-        await pfd.loadExternalLibraries(['vue@2.5.16'])
-        pfd.unloadExternalLibraries(['vue@2.5.16'])
-        expect(removeSrcSpy).not.toHaveBeenCalled()
-      })
-
-      it('> load, unload, load, call append script twice', async () => {
-        
-        const fixture = TestBed.createComponent(TestCmp)
-        fixture.detectChanges()
-        const pfd = fixture.componentInstance.pfd
-        await pfd.loadExternalLibraries(['vue@2.5.16'])
-        pfd.unloadExternalLibraries(['vue@2.5.16'])
-
-        appendSrcSpy.calls.reset()
-        expect(appendSrcSpy).not.toHaveBeenCalled()
-
-        await pfd.loadExternalLibraries(['vue@2.5.16'])
-        pfd.unloadExternalLibraries(['vue@2.5.16'])
-        expect(appendSrcSpy).toHaveBeenCalledTimes(1)
-      })
-    })
-  })
-})
\ No newline at end of file
diff --git a/src/plugin/pluginFactory.directive.ts b/src/plugin/pluginFactory.directive.ts
deleted file mode 100644
index fcd47654120dcf3754471b1fc6e7eac2342cb57e..0000000000000000000000000000000000000000
--- a/src/plugin/pluginFactory.directive.ts
+++ /dev/null
@@ -1,102 +0,0 @@
-import { Directive, ViewContainerRef, Inject, Optional } from "@angular/core";
-import { APPEND_SCRIPT_TOKEN, REMOVE_SCRIPT_TOKEN } from "src/util/constants";
-
-export const SUPPORT_LIBRARY_MAP: Map<string, Map<string, string>> = new Map([
-  ['jquery', new Map<string, string>([
-    ['3', 'https://code.jquery.com/jquery-3.3.1.min.js'],
-    ['2', 'https://code.jquery.com/jquery-2.2.4.min.js']
-  ])],
-  ['webcomponentsLite', new Map([
-    ['1.1.0', 'https://cdnjs.cloudflare.com/ajax/libs/webcomponentsjs/1.1.0/webcomponents-lite.js']
-  ])],
-  ['react', new Map([
-    ['16', 'https://unpkg.com/react@16/umd/react.development.js']
-  ])],
-  ['reactdom', new Map([
-    ['16', 'https://unpkg.com/react-dom@16/umd/react-dom.development.js']
-  ])],
-  ['vue', new Map([
-    ['2.5.16', 'https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js']
-  ])],
-  ['preact', new Map([
-    ['8.4.2', 'https://cdn.jsdelivr.net/npm/preact@8.4.2/dist/preact.min.js']
-  ])],
-  ['d3', new Map([
-    ['5.7.0', 'https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js'],
-    ['6.2.0', 'https://cdnjs.cloudflare.com/ajax/libs/d3/6.2.0/d3.min.js']
-  ])],
-  ['mathjax', new Map([
-    ['3.1.2', 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/3.1.2/es5/tex-svg.js']
-  ])]
-])
-
-export const parseLibrary = (libVer: string) => {
-  const re = /^([a-zA-Z0-9]+)@([0-9.]+)$/.exec(libVer)
-  if (!re) throw new Error(`${libVer} cannot be parsed properly`)
-  const lib = re[1]
-  const ver = re[2]
-  const libMap = SUPPORT_LIBRARY_MAP.get(lib) 
-  if (!libMap) throw new Error(`${lib} not supported. Only supported libraries are ${Array.from(SUPPORT_LIBRARY_MAP.keys())}`)
-  const src = libMap.get(ver)
-  if (!src) throw new Error(`${lib} version ${ver} not supported. Only supports ${Array.from(libMap.keys())}`)
-  return src
-}
-
-export const REGISTER_PLUGIN_FACTORY_DIRECTIVE = `REGISTER_PLUGIN_FACTORY_DIRECTIVE`
-
-@Directive({
-  selector: '[pluginFactoryDirective]',
-})
-
-export class PluginFactoryDirective {
-  constructor(
-    public viewContainerRef: ViewContainerRef,
-    @Optional() @Inject(REGISTER_PLUGIN_FACTORY_DIRECTIVE) registerPluginFactoryDirective: (directive: PluginFactoryDirective) => void,
-    @Inject(APPEND_SCRIPT_TOKEN) private appendScript: (src: string) => Promise<HTMLScriptElement>,
-    @Inject(REMOVE_SCRIPT_TOKEN) private removeScript: (srcEl: HTMLScriptElement) => void,
-  ) {
-    if (registerPluginFactoryDirective) {
-      registerPluginFactoryDirective(this)
-    }
-  }
-
-  private loadedLibraries: Map<string, {counter: number, srcEl: HTMLScriptElement|null}> = new Map()
-  
-  async loadExternalLibraries(libraries: string[]) {
-    const libsToBeLoaded = libraries.map(libName => {
-      return {
-        libName,
-        libSrc: parseLibrary(libName),
-      }
-    })
-
-    for (const libToBeLoaded of libsToBeLoaded) {
-  
-      const { libSrc, libName } = libToBeLoaded
-
-      // if browser natively support custom element, do not append polyfill
-      if ('customElements' in window && /^webcomponentsLite@/.test(libName)) continue
-
-      let srcEl
-      const { counter, srcEl: srcElOld } = this.loadedLibraries.get(libName) || { counter: 0 }
-      if (counter === 0) {
-
-        // slight performance penalty not loading external libraries in parallel, but this should be an edge case any way
-        srcEl = await this.appendScript(libSrc)
-      }
-      this.loadedLibraries.set(libName, { counter: counter + 1, srcEl: srcEl || srcElOld })
-    }
-  }
-
-  unloadExternalLibraries(libraries: string[]) {
-    for (const lib of libraries) {
-      const { counter, srcEl } = this.loadedLibraries.get(lib) || { counter: 0 }
-      if (counter > 1) {
-        this.loadedLibraries.set(lib, { counter: counter - 1, srcEl })
-      } else {
-        this.loadedLibraries.set(lib, { counter: 0, srcEl: null })
-        this.removeScript(srcEl)
-      }
-    }
-  }
-}
diff --git a/src/plugin/pluginPortal/pluginPortal.component.ts b/src/plugin/pluginPortal/pluginPortal.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..58bd58993f3c2423ed278fee3c64b330b78b4af2
--- /dev/null
+++ b/src/plugin/pluginPortal/pluginPortal.component.ts
@@ -0,0 +1,149 @@
+import { AfterViewInit, ChangeDetectionStrategy, Component, ElementRef, Inject, OnDestroy, Optional, ViewChild, ViewContainerRef } from "@angular/core";
+import { combineLatest, fromEvent, interval, Subscription } from "rxjs";
+import { map, scan, share, startWith, take, filter } from "rxjs/operators";
+import { BoothVisitor, JRPCRequest, JRPCSuccessResp, ListenerChannel } from "src/api/jsonrpc";
+import { ApiBoothEvents, ApiService, BroadCastingApiEvents, HeartbeatEvents, namespace } from "src/api/service";
+import { getUuid } from "src/util/fn";
+import { WIDGET_PORTAL_TOKEN } from "src/widget/constants";
+import { getPluginSrc, SET_PLUGIN_NAME } from "../const";
+
+@Component({
+  selector: 'sxplr-plugin-portal',
+  template: `
+  <iframe [src]="src | iframeSrc" [sandbox]="sandbox" #iframe>
+  </iframe>
+  `,
+  styles: [
+    `:host { width: 100%; height: 100%; display: block; } iframe { width: 100%; height: 100%; border: none; }`
+  ],
+  changeDetection: ChangeDetectionStrategy.OnPush,
+})
+
+export class PluginPortal implements AfterViewInit, OnDestroy, ListenerChannel{
+
+  sandbox = [
+    "allow-downloads",
+    "allow-popups",
+    "allow-popups-to-escape-sandbox",
+    "allow-scripts",
+  ].join(" ")
+
+  @ViewChild('iframe', { read: ElementRef })
+  iframeElRef: ElementRef
+  src: string
+  srcName: string
+  private origin: string
+
+  private onDestroyCb: (() => void)[] = []
+  private sub: Subscription[] = []
+  private handshakeSub: Subscription[] = []
+
+  private boothVisitor: BoothVisitor<ApiBoothEvents>
+  private childWindow: Window
+
+  constructor(
+    private apiService: ApiService,
+    public vcr: ViewContainerRef,
+    @Optional() @Inject(SET_PLUGIN_NAME) private setPluginName: (inst: unknown, pluginName: string) => void,
+    @Optional() @Inject(WIDGET_PORTAL_TOKEN) portalData: Record<string, string>
+  ){
+    if (portalData){
+      this.src = getPluginSrc(portalData)
+      const url = new URL(this.src)
+      this.origin = url.origin
+    }
+  }
+
+  ngAfterViewInit(): void {
+    if (this.iframeElRef) {
+      const iframeWindow = (this.iframeElRef.nativeElement as HTMLIFrameElement).contentWindow
+      const handShake$ = interval(1000).pipe(
+        map(() => getUuid()),
+        take(10),
+        share()
+      )
+      this.handshakeSub.push(
+        /**
+         * handshake
+         */
+        handShake$.pipe(
+          // try for 10 seconds. If nothing loads within 10 minutes, assuming dead.
+        ).subscribe(id => {
+          const handshakeMsg: JRPCRequest<string, null> = {
+            jsonrpc: '2.0',
+            id,
+            method: `${namespace}.init`,
+          }
+          iframeWindow.postMessage(handshakeMsg, this.origin)
+        }),
+
+        combineLatest([
+          handShake$.pipe(
+            scan((acc, curr) => [...acc, curr], [])
+          ),
+          fromEvent<MessageEvent>(window, 'message').pipe(
+            startWith(null as MessageEvent),
+          )
+        ]).subscribe(([ids, event]) => {
+          const { id, jsonrpc } = event?.data || {}
+          if (jsonrpc === "2.0" && ids.includes(id)) {
+            const data = event.data as JRPCSuccessResp<HeartbeatEvents['init']['response']>
+
+            this.srcName = data.result.name || 'Untitled Pluging'
+            this.setPluginName(this, this.srcName)
+            
+            while (this.handshakeSub.length > 0) this.handshakeSub.pop().unsubscribe()
+
+            /**
+             * hook up to the listener for the plugin
+             */
+            this.childWindow = iframeWindow
+            this.apiService.broadcastCh.addListener(this)
+            this.boothVisitor = this.apiService.booth.handshake()
+          }
+        })
+      )
+
+      /**
+       * listening to plugin requests
+       * only start after boothVisitor is defined
+       */
+      const sub = fromEvent<MessageEvent>(window, 'message').pipe(
+        startWith(null as MessageEvent),
+        filter(msg => !!this.boothVisitor && msg.data.jsonrpc === "2.0" && !!msg.data.method && msg.origin === this.origin)
+      ).subscribe(async msg => {
+        try {
+          if (msg.data.method === `${namespace}.exit`) {
+            sub.unsubscribe()
+          }
+          const result = await this.boothVisitor.request(msg.data)
+          if (!!result) {
+            this.childWindow.postMessage(result, this.origin)
+          }
+        } catch (e) {
+          this.childWindow.postMessage({
+            id: msg.data.id,
+            error: {
+              code: -32603,
+              message: e.toString()
+            }
+          }, this.origin)
+        }
+      })
+    }
+  }
+  notify(payload: JRPCRequest<keyof BroadCastingApiEvents, BroadCastingApiEvents[keyof BroadCastingApiEvents]>) {
+    if (this.childWindow) {
+      this.childWindow.postMessage(payload, this.origin)
+    }
+  }
+  registerLeaveCb(cb: () => void) {
+    this.onDestroyCb.push(() => cb())
+  }
+
+  ngOnDestroy(): void {
+    while (this.handshakeSub.length > 0) this.handshakeSub.pop().unsubscribe()
+    while (this.sub.length > 0) this.sub.pop().unsubscribe()
+    while (this.onDestroyCb.length > 0) this.onDestroyCb.pop()()
+  }
+}
diff --git a/src/plugin/pluginUnit/pluginUnit.component.ts b/src/plugin/pluginUnit/pluginUnit.component.ts
deleted file mode 100644
index 902701b16c29791974171a16841beb85d7ee3fee..0000000000000000000000000000000000000000
--- a/src/plugin/pluginUnit/pluginUnit.component.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { Component, ElementRef, HostBinding } from "@angular/core";
-
-@Component({
-  templateUrl : `./pluginUnit.template.html`,
-})
-
-export class PluginUnit {
-
-  @HostBinding('attr.pluginContainer')
-  public pluginContainer = true
-
-  constructor(public elementRef: ElementRef) {
-
-  }
-}
diff --git a/src/plugin/request.md b/src/plugin/request.md
new file mode 100644
index 0000000000000000000000000000000000000000..487c1344f3d785978a99eb61f1ec94ca6b31904e
--- /dev/null
+++ b/src/plugin/request.md
@@ -0,0 +1,264 @@
+# Request API
+
+Request  messages are sent when the plugin requests siibra-explorer to do something on its behalf.
+
+Be it request the user to select a region, a point, navigate to a specific location etc. 
+
+> :warning: Please note that `beforeunload` window event does not fire on iframe windows. Plugins should do whatever cleanup it needs, then send the message `sxplr.exit`. 
+
+```javascript
+
+let parentWindow
+window.addEventListener('message', ev => {
+  const { source, data, origin } = msg
+  const { id, method, params, result, error } = data
+
+  if (method === "sxplr.init") {
+    parentWindow = source
+  }
+})
+
+window.addEventListener('pagehide', () => {
+
+  // do cleanup
+  // n.b. since iframe unload usually do not trigger DOM events
+  // one will need to manually trigger destroying any apps manually
+
+  parentWindow.postMessage({
+    jsonrpc: '2.0',
+    method: `sxplr.exit`,
+    params: {
+      requests: [] // any remaining requests to be carried out
+    }
+  })
+})
+```
+
+<!-- the API reference below are auto generated by generateTypes.js  -->
+<!-- do not edit, as the edit will be overwritten by the auto generation -->
+
+## API
+### `sxplr.getAllAtlases`
+
+- request
+
+  ```ts
+  null
+  ```
+
+- response
+
+  ```ts
+  SapiAtlasModel[]
+  ```
+
+
+### `sxplr.getSupportedTemplates`
+
+- request
+
+  ```ts
+  null
+  ```
+
+- response
+
+  ```ts
+  SapiSpaceModel[]
+  ```
+
+
+### `sxplr.getSupportedParcellations`
+
+- request
+
+  ```ts
+  null
+  ```
+
+- response
+
+  ```ts
+  SapiParcellationModel[]
+  ```
+
+
+### `sxplr.selectAtlas`
+
+- request
+
+  ```ts
+  {"@id": string}
+  ```
+
+- response
+
+  ```ts
+  'OK'
+  ```
+
+
+### `sxplr.selectParcellation`
+
+- request
+
+  ```ts
+  {"@id": string}
+  ```
+
+- response
+
+  ```ts
+  'OK'
+  ```
+
+
+### `sxplr.selectTemplate`
+
+- request
+
+  ```ts
+  {"@id": string}
+  ```
+
+- response
+
+  ```ts
+  'OK'
+  ```
+
+
+### `sxplr.navigateTo`
+
+- request
+
+  ```ts
+  MainState['[state.atlasSelection]']['navigation'] & {"animate": boolean}
+  ```
+
+- response
+
+  ```ts
+  'OK'
+  ```
+
+
+### `sxplr.getUserToSelectARoi`
+
+- request
+
+  ```ts
+  {"type": 'region' | 'point', "message": string}
+  ```
+
+- response
+
+  ```ts
+  SapiRegionModel | OpenMINDSCoordinatePoint
+  ```
+
+
+### `sxplr.addAnnotations`
+
+- request
+
+  ```ts
+  {"annotations": SxplrCoordinatePointExtension[]}
+  ```
+
+- response
+
+  ```ts
+  'OK'
+  ```
+
+
+### `sxplr.rmAnnotations`
+
+- request
+
+  ```ts
+  {"annotations": AtId[]}
+  ```
+
+- response
+
+  ```ts
+  'OK'
+  ```
+
+
+### `sxplr.loadLayers`
+
+- request
+
+  ```ts
+  {"layers": AddableLayer[]}
+  ```
+
+- response
+
+  ```ts
+  'OK'
+  ```
+
+
+### `sxplr.updateLayers`
+
+- request
+
+  ```ts
+  {"layers": AddableLayer[]}
+  ```
+
+- response
+
+  ```ts
+  'OK'
+  ```
+
+
+### `sxplr.removeLayers`
+
+- request
+
+  ```ts
+  {"layers": {"id": string}}
+  ```
+
+- response
+
+  ```ts
+  'OK'
+  ```
+
+
+### `sxplr.exit`
+
+- request
+
+  ```ts
+  {"requests": JRPCRequest[]}
+  ```
+
+- response
+
+  ```ts
+  'OK'
+  ```
+
+
+### `sxplr.cancelRequest`
+
+- request
+
+  ```ts
+  {"id": string}
+  ```
+
+- response
+
+  ```ts
+  'OK'
+  ```
+
diff --git a/src/plugin/service.ts b/src/plugin/service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..19f183189fac54e16b9b5a33be58de632276327a
--- /dev/null
+++ b/src/plugin/service.ts
@@ -0,0 +1,68 @@
+import { HttpClient } from "@angular/common/http";
+import { Injectable, Injector, NgZone } from "@angular/core";
+import { WIDGET_PORTAL_TOKEN } from "src/widget/constants";
+import { WidgetService } from "src/widget/service";
+import { WidgetPortal } from "src/widget/widgetPortal/widgetPortal.component";
+import { setPluginSrc, SET_PLUGIN_NAME } from "./const";
+import { PluginPortal } from "./pluginPortal/pluginPortal.component";
+import { environment } from "src/environments/environment"
+
+@Injectable({
+  providedIn: 'root'
+})
+export class PluginService {
+  loadedPlugins: string[] = []
+  srcWidgetMap = new Map<string, WidgetPortal<PluginPortal>>()
+  
+  constructor(
+    private wSvc: WidgetService,
+    private injector: Injector,
+    private zone: NgZone,
+    private http: HttpClient
+  ){}
+
+  pluginManifests$ = this.http.get<{
+    'siibra-explorer': true
+    name: string
+    iframeUrl: string
+  }[]>(`${environment.BACKEND_URL || ''}plugins/manifests`)
+
+  async launchPlugin(htmlSrc: string){
+    if (this.loadedPlugins.includes(htmlSrc)) return
+    const injector = Injector.create({
+      providers: [{
+        provide: WIDGET_PORTAL_TOKEN,
+        useValue: setPluginSrc(htmlSrc, {})
+      }, {
+        provide: SET_PLUGIN_NAME,
+        useValue: (inst: PluginPortal, pluginName: string) => this.setPluginName(inst, pluginName)
+      }],
+      parent: this.injector
+    })
+    const wdg = this.wSvc.addNewWidget(PluginPortal, injector)
+    this.srcWidgetMap.set(htmlSrc, wdg)
+  }
+
+  setPluginName(plg: PluginPortal, name: string) {
+    
+    if (!this.srcWidgetMap.has(plg.src)) {
+      console.warn(`cannot find plg.src ${plg.src}`)
+      return
+    }
+    const wdg = this.srcWidgetMap.get(plg.src)
+    this.zone.run(() => wdg.name = name)
+  }
+
+  rmPlugin(plg: PluginPortal){
+    this.loadedPlugins = this.loadedPlugins.filter(plgSrc => plgSrc !== plg.src)
+
+    if (!this.srcWidgetMap.has(plg.src)) {
+      console.warn(`cannot find plg.src ${plg.src}`)
+      return
+    }
+    const wdg = this.srcWidgetMap.get(plg.src)
+    this.srcWidgetMap.delete(plg.src)
+
+    this.wSvc.rmWidget(wdg)
+  }
+}
diff --git a/src/plugin/tsUtil.js b/src/plugin/tsUtil.js
new file mode 100644
index 0000000000000000000000000000000000000000..ea0040ad800c55dd5caa06c2ddf79a91b82b99fe
--- /dev/null
+++ b/src/plugin/tsUtil.js
@@ -0,0 +1,130 @@
+const ts = require('typescript')
+
+
+function processIndexAccessor(mem) {
+  if (ts.SyntaxKind[mem.kind] !== "IndexedAccessType") {
+    throw new Error(`Index accessor needs to have index accessor type as mem.kind`)
+  }
+  return `${getTypeText(mem.objectType)}[${getTypeText(mem.indexType)}]`
+}
+
+function getTypeText(node){
+  switch(ts.SyntaxKind[node.kind]) {
+    case "TypeReference": {
+      return node.typeName.text
+    }
+    case "ArrayType": {
+      if (!node.elementType.typeName) return getTypeText(node.elementType)
+      return `${node.elementType.typeName.text}[]`
+    }
+    case "TypeLiteral": {
+      let returnVal = '{'
+      returnVal += node.members.map(mem => `"${mem.name.text}": ${getTypeText(mem.type)}`).join(', ')
+      returnVal += "}"
+      return returnVal
+    }
+    case "LiteralType": {
+      if (ts.SyntaxKind[node.literal.kind] === "NullKeyword"){
+        return `null`
+      }
+      if (ts.SyntaxKind[node.literal.kind] === "StringLiteral"){
+        return `'${node.literal.text}'`
+      }
+      throw new Error(`LiteralType not caught`)
+    }
+    case "BooleanKeyword": {
+      return `boolean`
+    }
+    case "StringKeyword": {
+      return `string`
+    }
+    case "IntersectionType": {
+      return node.types.map(getTypeText).join(' & ')
+    }
+    case "UnionType": {
+      return node.types.map(getTypeText).join(' | ')
+    }
+    case 'PropertySignature': {
+      return processPropertySignature(node)
+    }
+    case 'IndexedAccessType': {
+      return processIndexAccessor(node)
+    }
+    default: {
+      debugger
+      throw new Error(`No parser for type ${ts.SyntaxKind[node.kind]}`)
+    }
+  }
+}
+
+function processPropertySignature(node) {
+  const output = {}
+  debugger
+}
+
+function processNodeMember(mem, typeAliasDeclarationMap = new Map()) {
+  if (ts.SyntaxKind[mem.kind] === "IndexedAccessType") {
+    return processIndexAccessor(mem)
+  }
+  if (ts.SyntaxKind[mem.kind] === "TypeReference") {
+    return mem.typeName.text
+  }
+  if (ts.SyntaxKind[mem.kind] !== "PropertySignature") {
+    throw new Error(`mem.kind should be of PropertySignature, but is instead ${ts.SyntaxKind[mem.kind]}`)
+  }
+  const typeText = getTypeText(mem.type)
+  if (typeAliasDeclarationMap.has(typeText)) {
+    return getTypeText(typeAliasDeclarationMap.get(typeText).type)
+  }
+  return typeText
+}
+
+function processTypeAliasDeclaration(node) {
+  const output = {}
+  const kind = ts.SyntaxKind[node.kind]
+  if (kind !== 'TypeAliasDeclaration') throw new Error(`processTypeAliasDeclaration should be of TypeAliasDeclaration`)
+  for (const mem of node.type.members) {
+    output[mem.name.text] = processNodeMember(mem)
+  }
+  return output
+}
+
+function processRequestTypeAlias(node, typeAliasDeclarationMap = new Map()) {
+  const kind = ts.SyntaxKind[node.kind]
+  if (kind !== 'TypeAliasDeclaration') throw new Error(`processTypeAliasDeclaration should be of TypeAliasDeclaration`)
+  if (!node.type.members.every(mem => ts.SyntaxKind[mem.type.kind] === "TypeLiteral")) {
+    throw new Error(`for request type alias, expected every type.members to be of type TypeLiteral`)
+  }
+
+  const output = {}
+  for (const mem of node.type.members) {
+    
+    const requestNode = mem.type.members.find(typeMem => typeMem.name.text === "request")
+    const responseNode = mem.type.members.find(typeMem => typeMem.name.text === "response")
+    if (!requestNode || !responseNode) {
+      let errorText = `for request type alias, every member must have both response and request defined, but ${node.name.text}.${mem.name.text} does not have `
+      if (!requestNode) {
+        errorText += " request "
+      }
+      if (!responseNode) {
+        errorText += " response "
+      }
+      errorText += "defined."
+      throw new Error(errorText)
+    }
+    if (!requestNode) {
+      throwFlag = true
+      errorText += ` request `
+    }
+    output[mem.name.text] = {
+      request: processNodeMember(requestNode, typeAliasDeclarationMap),
+      response: processNodeMember(responseNode, typeAliasDeclarationMap)
+    }
+  }
+  return output
+}
+
+module.exports = {
+  processTypeAliasDeclaration,
+  processRequestTypeAlias,
+}
\ No newline at end of file
diff --git a/src/plugin/types.ts b/src/plugin/types.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f51aa57d9deb5227fbc76075177607b3e38bcd70
--- /dev/null
+++ b/src/plugin/types.ts
@@ -0,0 +1,4 @@
+export type PluginManifest = {
+  name: string
+  iframeUrl: string
+}
diff --git a/src/plugin_examples/README.md b/src/plugin_examples/README.md
deleted file mode 100644
index 7f967539c0997992f1dae6e391a72e084d5a33a5..0000000000000000000000000000000000000000
--- a/src/plugin_examples/README.md
+++ /dev/null
@@ -1,118 +0,0 @@
-# Plugin README
-
-A plugin needs to contain three files. 
-- Manifest JSON
-- template HTML
-- script JS
-
-
-These files need to be served by GET requests over HTTP with appropriate CORS header. 
-
----
-
-## Manifest JSON
-
-The manifest JSON file describes the metadata associated with the plugin. 
-
-```json
-{
-  "name":"fzj.xg.helloWorld",
-  "displayName": "Hello World - my first plugin",
-  "templateURL":"http://LINK-TO-YOUR-PLUGIN-TEMPLATE/template.html",
-  "scriptURL":"http://LINK-TO-YOUR-PLUGIN-SCRIPT/script.js",
-  "initState":{
-    "key1": "value1",
-    "key2" : {
-      "nestedKey1" : "nestedValue1"
-    }
-  },
-  "initStateUrl": "http://LINK-TO-PLUGIN-STATE",
-  "persistency": false,
-
-  "description": "Human readable description of the plugin.",
-  "desc": "Same as description. If both present, description takes more priority.",
-  "homepage": "https://HOMEPAGE-URL-TO-YOUR-PLUGIN/doc.html",
-  "authors": "Author <author@example.com>, Author2 <author2@example.org>"
-}
-```
-*NB* 
-- Plugin name must be unique globally. To prevent plugin name clashing, please adhere to the convention of naming your package **AFFILIATION.AUTHORNAME.PACKAGENAME\[.VERSION\]**. 
-- the `initState` object and `initStateUrl` will be available prior to the evaluation of `script.js`, and will populate the objects `interactiveViewer.pluginControl[MANIFEST.name].initState` and `interactiveViewer.pluginControl[MANIFEST.name].initStateUrl` respectively. 
-
----
-
-## Template HTML
-
-The template HTML file describes the HTML view that will be rendered in the widget.
-
-
-```html
-<form>
-  <div class = "input-group">
-    <span class = "input-group-addon">Area 1</span>
-    <input type = "text" id = "fzj.xg.helloWorld.area1" name = "fzj.xg.helloWorld.area1" class = "form-control" placeholder="Select a region" value = "">
-  </div>
-
-  <div class = "input-group">
-    <span class = "input-group-addon">Area 2</span>
-    <input type = "text" id = "fzj.xg.helloWorld.area2" name = "fzj.xg.helloWorld.area2" class = "form-control" placeholder="Select a region" value = "">
-  </div>
-
-  <hr class = "col-md-10">
-
-  <div class = "col-md-12">
-    Select genes of interest:
-  </div>
-  <div class = "input-group">
-    <input type = "text" id = "fzj.xg.helloWorld.genes" name = "fzj.xg.helloWorld.genes" class = "form-control" placeholder = "Genes of interest ...">
-    <span class = "input-group-btn">
-      <button id = "fzj.xg.helloWorld.addgenes" name = "fzj.xg.helloWorld.addgenes" class = "btn btn-default" type = "button">Add</button>
-    </span>
-  </div>
-
-  <hr class = "col-md-10">
-
-  <button id = "fzj.xg.helloWorld.submit" name = "fzj.xg.helloWorld.submit" type = "button" class = "btn btn-default btn-block">Submit</button>
-
-  <hr class = "col-md-10">
-
-  <div class = "col-md-12" id = "fzj.xg.helloWorld.result">
-
-  </div>
-</form>
-```
-
-*NB*
-- *bootstrap 3.3.6* css is already included for templating.
-- keep in mind of the widget width restriction (400px) when crafting the template
-- whilst there are no vertical limits on the widget, contents can be rendered outside the viewport. Consider setting the *max-height* attribute.
-- your template and script will interact with each other likely via *element id*. As a result, it is highly recommended that unique id's are used. Please adhere to the convention: **AFFILIATION.AUTHOR.PACKAGENAME.ELEMENTID** 
-
----
-
-## Script JS
-
-The script will always be appended **after** the rendering of the template. 
-
-```javascript
-(()=>{
-  /* your code here */
-
-  if(interactiveViewer.pluginControl['fzj.xg.helloWorld'].initState){
-    /* init plugin with initState */
-  }
-  
-  const submitButton = document.getElemenById('fzj.xg.helloWorld.submit')
-  submitButton.addEventListener('click',(ev)=>{
-    console.log('submit button was clicked')
-  })
-})()
-```
-*NB*
-- JS is loaded and executed **before** the attachment of DOM (template). This is to allow webcomponents have a chance to be loaded. If your script needs the DOM to be attached, use a `setTimeout` callback to delay script execution.
-- ensure the script is scoped locally, instead of poisoning the global scope
-- for every observable subscription, call *unsubscribe()* in the *onShutdown* callback
-- some frameworks such as *jquery2*, *jquery3*, *react/reactdom* and *webcomponents* can be loaded via *interactiveViewer.pluinControl.loadExternalLibraries([LIBRARY_NAME_1, LIBRARY_NAME_2])*. if the libraries are loaded, remember to hook *interactiveViewer.pluginControl.unloadExternalLibraries([LIBRARY_NAME_1,LIBRARY_NAME_2])* in the *onShutdown* callback
-- when/if using webcomponents, please be aware that the `connectedCallback()` and `disconnectedCallback()` will be called everytime user toggle between *floating* and *docked* modes. 
-- when user navigate to a new template all existing widgets will be destroyed, unless the `persistency` is set to `true` in `manifest.json`.
-- for a list of APIs, see [plugin_api.md](plugin_api.md)
diff --git a/src/plugin_examples/migrationGuide.md b/src/plugin_examples/migrationGuide.md
deleted file mode 100644
index fcd5e040b9333b262dbbac60b6d3237859fe7f1c..0000000000000000000000000000000000000000
--- a/src/plugin_examples/migrationGuide.md
+++ /dev/null
@@ -1,51 +0,0 @@
-Plugin Migration Guide (v0.1.0 => v0.2.0)
-======
-Plugin APIs have changed drastically from v0.1.0 to v0.2.0. Here is a list of plugin API from v0.1.0, and how it has changed moving to v0.2.0.
-
-**n.b.** `webcomponents-lite.js` is no longer included by default. You will need to request it explicitly with `window.interactiveViewer.pluginControl.loadExternalLibraries()` and unload it once you are done.
-
----
-
-- ~~*window.nehubaUI*~~ removed
-  - ~~*metadata*~~ => **window.interactiveViewer.metadata**
-    - ~~*selectedTemplate* : nullable Object~~ removed. use **window.interactiveViewer.metadata.selectedTemplateBSubject** instead
-    - ~~*availableTemplates* : Array of TemplateDescriptors (empty array if no templates are available)~~ => **window.interactiveViewer.metadata.loadedTemplates**
-    - ~~*selectedParcellation* : nullable Object~~ removed. use **window.interactiveViewer.metadata.selectedParcellationBSubject** instead
-    - ~~*selectedRegions* : Array of Object (empty array if no regions are selected)~~ removed. use **window.interactiveViewer.metadata.selectedRegionsBSubject** instead
-
-- ~~window.pluginControl['YOURPLUGINNAME'] *nb: may be undefined if yourpluginname is incorrect*~~ => **window.interactiveViewer.pluginControl[YOURPLUGINNAME]**
-  - blink(sec?:number) : Function that causes the floating widget to blink, attempt to grab user attention
-  - ~~pushMessage(message:string) : Function that pushes a message that are displayed as a popover if the widget is minimised. No effect if the widget is not miniminised.~~ removed
-  - shutdown() : Function that causes the widget to shutdown dynamically. (triggers onShutdown callback)
-  - onShutdown(callback) : Attaches a callback function, which is called when the plugin is shutdown.
-  
-- ~~*window.viewerHandle*~~ => **window.interactiveViewer.viewerHandle**
-  - ~~*loadTemplate(TemplateDescriptor)* : Function that loads a new template~~ removed. use **window.interactiveViewer.metadata.selectedTemplateBSubject** instead
-  - ~~*onViewerInit(callback)* : Functional that allows a callback function to be called just before a nehuba viewer is initialised~~ removed
-  - ~~*afterViewerInit(callback)* : Function that allows a callback function to be called just after a nehuba viewer is initialised~~ removed
-  - ~~*onViewerDestroy(callback)* : Function that allows a callback function be called just before a nehuba viewer is destroyed~~ removed
-  - ~~*onParcellationLoading(callback)* : Function that allows a callback function to be called just before a parcellation is selected~~ removed
-  - ~~*afterParcellationLoading(callback)* : Function that allows a callback function to be called just after a parcellation is selected~~ removed
-  - *setNavigationLoc(loc,realSpace?)* : Function that teleports to loc : number[3]. Optional argument to determine if the loc is in realspace (default) or voxelspace.
-  - ~~*setNavigationOrientation(ori)* : Function that teleports to ori : number[4]. (Does not work currently)~~ => **setNavigationOri(ori)** (still non-functional)
-  - *moveToNavigationLoc(loc,realSpace?)* : same as *setNavigationLoc(loc,realSpace?)*, except moves to target location over 500ms.
-  - *showSegment(id)* : Function that selectes a segment in the viewer and UI. 
-  - *hideSegment(id)* : Function that deselects a segment in the viewer and UI.
-  - *showAllSegments()* : Function that selects all segments.
-  - *hideAllSegments()* : Function that deselects all segments.
-  - *loadLayer(layerObject)* : Function that loads a custom neuroglancer compatible layer into the viewer (e.g. precomputed, NIFTI, etc). Does not influence UI. 
-  - *mouseEvent* RxJs Observable. Read more at [rxjs doc](http://reactivex.io/rxjs/)
-    - *mouseEvent.filter(filterFn:({eventName : String, event: Event})=>boolean)* returns an Observable. Filters the event stream according to the filter function.
-    - *mouseEvent.map(mapFn:({eventName : String, event: Event})=>any)* returns an Observable. Map the event stream according to the map function.
-    - *mouseEvent.subscribe(callback:({eventName : String , event : Event})=>void)* returns an Subscriber instance. Call *Subscriber.unsubscribe()* when done to avoid memory leak. 
-  - *mouseOverNehuba* RxJs Observable. Read more at [rxjs doc](http://reactivex.io/rxjs)
-    - *mouseOverNehuba.filter* && *mouseOvernehuba.map* see above
-    - *mouseOverNehuba.subscribe(callback:({nehubaOutput : any, foundRegion : any})=>void)*
-
-- ~~*window.uiHandle*~~ => **window.interactiveViewer.uiHandle**
-  - ~~*onTemplateSelection(callback)* : Function that allows a callback function to be called just after user clicks to navigate to a new template, before *selectedTemplate* is updated~~ removed. use **window.interactiveViewer.metadata.selectedTemplateBSubject** instead
-  - ~~*afterTemplateSelection(callback)* : Function that allows a callback function to be called after the template selection process is complete, and *selectedTemplate* is updated~~ removed
-  - ~~*onParcellationSelection(callback)* : Function that attach a callback function to user selecting a different parcellation~~ removed. use **window.interactiveViewer.metadata.selectedParcellationBSubject** instead.
-  - ~~*afterParcellationSelection(callback)* : Function that attach a callback function to be called after the parcellation selection process is complete and *selectedParcellation* is updated.~~ removed
-  - *modalControl*
-    - ~~*getModalHandler()* : Function returning a handler to change/show/hide/listen to a Modal.~~ removed
\ No newline at end of file
diff --git a/src/plugin_examples/plugin1/manifest.json b/src/plugin_examples/plugin1/manifest.json
deleted file mode 100644
index 0813f787c22dc71f22c3d14bbd20de2716d27068..0000000000000000000000000000000000000000
--- a/src/plugin_examples/plugin1/manifest.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
-  "name":"fzj.xg.exmaple.0_0_1",
-  "displayName": "Example Plugin (v0.0.1)",
-  "templateURL": "http://HOSTNAME/test.html",
-  "scriptURL": "http://HOSTNAME/script.js",
-  "initState": {
-    "key1": "val1",
-    "key2": {
-      "key21": "val21"
-    }
-  },
-  "initStateUrl": "http://HOSTNAME/state?id=007",
-  "persistency": false,
-  "description": "description of example plugin",
-  "desc": "desc of example plugin",
-  "homepage": "http://HOSTNAME/home.html",
-  "authors": "Xiaoyun Gui <x.gui@fz-juelich.de>"
-}
\ No newline at end of file
diff --git a/src/plugin_examples/plugin_api.md b/src/plugin_examples/plugin_api.md
deleted file mode 100644
index 6954f15b52d7900e14a493376423c61697dacb82..0000000000000000000000000000000000000000
--- a/src/plugin_examples/plugin_api.md
+++ /dev/null
@@ -1,416 +0,0 @@
-# Plugin APIs
-
-## window.interactiveViewer
-
-### metadata
-
-#### selectedTemplateBSubject
-
-BehaviourSubject that emits a TemplateDescriptor object whenever a template is selected. Emits null onInit.
-
-#### selectedParcellationBSubject
-
-BehaviourSubject that emits a ParcellationDescriptor object whenever a parcellation is selected. n.b. selecting a new template automatically select the first available parcellation. Emits null onInit.
-
-#### selectedRegionsBSubject
-
-BehaviourSubject that emits an Array of RegionDescriptor objects whenever the list of selected regions changes. Emits empty array onInit.
-
-#### loadedTemplates
-
-Array of TemplateDescriptor objects. Loaded asynchronously onInit.
-
-#### layersRegionLabelIndexMap
-
-Map of layer name to Map of labelIndex (used by neuroglancer and nehuba) to the corresponding RegionDescriptor object.
-
-### viewerHandle
-
-> **nb** `viewerHandle` may be undefined at any time (user be yet to select an atlas, user could have unloaded an atlas. ...etc)
-
-#### setNavigationLoc(coordinates, realspace?:boolean)
-
-Function that teleports the navigation state to coordinates : [x:number,y:number,z:number]. Optional arg determine if the set of coordinates is in realspace (default) or voxelspace.
-
-#### moveToNavigationLoc(coordinates,realspace?:boolean)
-
-same as *setNavigationLoc(coordinates,realspace?)*, except the action is carried out over 500ms.
-
-#### setNavigationOri(ori)
-
-(NYI) Function that sets the orientation state of the viewer.
-
-#### moveToNavigationOri(ori)
-
-(NYI) same as *setNavigationOri*, except the action is carried out over 500ms.
-
-#### showSegment(labelIndex)
-
-Function that shows a specific segment. Will trigger *selectedRegionsBSubject*.
-
-#### hideSegment(labelIndex)
-
-Function that hides a specific segment. Will trigger *selectRegionsBSubject*
-
-#### showAllSegments()
-
-Function that shows all segments. Will trigger *selectRegionsBSubject*
-
-#### hideAllSegments()
-Function that hides all segments. Will trigger *selectRegionBSubject*
-
-#### getLayersSegmentColourMap()
-
-Call to get Map of layer name to Map of label index to colour map
-
-#### applyLayersColourMap
-
-Function that applies a custom colour map.
-
-#### loadLayer(layerObject)
-
-Function that loads *ManagedLayersWithSpecification* directly to neuroglancer. Returns the values of the object successfully added. **n.b.** advanced feature, will likely break other functionalities. **n.b.** if the layer name is already taken, the layer will not be added.
-  
-```javascript
-const obj = {
-  'advanced layer' : {
-    type : 'image',
-    source : 'nifti://http://example.com/data/nifti.nii',
-  },
-  'advanced layer 2' : {
-    type : 'mesh',
-    source : 'vtk://http://example.com/data/vtk.vtk'
-  }
-}
-const returnValue = window.interactiveViewer.viewerHandle.loadLayer(obj)
-/* loads two layers, an image nifti layer and a mesh vtk layer */
-
-console.log(returnValue)
-/* prints
-
-[{ 
-  type : 'image', 
-  source : 'nifti...' 
-},
-{
-  type : 'mesh',
-  source : 'vtk...'
-}] 
-*/
-```
-
-#### removeLayer(layerObject)
-
-Function that removes *ManagedLayersWithSpecification*, returns an array of the names of the layers removed. 
-
-**n.b.** advanced feature. may break other functionalities.
-
-```js
-const obj = {
-  'name' : /^PMap/
-}
-const returnValue = window.interactiveViewer.viewerHandle.removeLayer(obj)
-
-console.log(returnValue)
-/* prints
-['PMap 001','PMap 002']
-*/
-```
-
-#### add3DLandmarks(landmarks)
-
-adds landmarks to both the perspective view and slice view. 
-
-_input_
-
-| input | type | desc |
-| --- | --- | --- |
-| landmarks | array | an array of `landmarks` to be rendered by the viewer |
-
-A landmark object consist of the following keys:
-
-| key | type | desc | required |
-| --- | --- | --- | --- |
-| id | string | id of the landmark | yes |
-| name | string | name of the landmark | |
-| position | [number, number, number] | position (in mm) | yes |
-| color | [number, number, number] | rgb of the landmark | |
-
-
-```js
-const landmarks = [{
-  id : `fzj-xg-jugex-1`,
-  position : [0,0,0]
-},{
-  id : `fzj-xg-jugex-2`,
-  position : [22,27,-1],
-  color: [255, 0, 255]
-}]
-window.interactiveViewer.viewerHandle.add3DLandmarks(landmarks)
-
-/* adds landmarks in perspective view and slice view */
-```
-
-#### remove3DLandmarks(IDs)
-
-removes the landmarks by their IDs
-
-```js
-window.interactiveViewer.viewerHandle
-  .remove3DLandmarks(['fzj-xg-jugex-1', 'fzj-xg-jugex-2'])
-/* removes the landmarks added above */
-```
-
-#### setLayerVisibility(layerObject, visible)
-
-Function that sets the visibility of a layer. Returns the names of all the layers that are affected as an Array of string.
-
-```js
-const obj = {
-  'type' : 'segmentation'
-}
-
-window.interactiveViewer.viewerHandle.setLayerVisibility(obj,false)
-
-/* turns off all the segmentation layers */
-```
-
-#### mouseEvent
-
-Subject that emits an object shaped `{ eventName : string, event: event }` when a user triggers a mouse event on the viewer. 
-
-
-#### mouseOverNehuba
-
-**deprecating** use mouseOverNehubaUI instead
-
-BehaviourSubject that emits an object shaped `{ nehubaOutput : number | null, foundRegion : RegionDescriptor | null }`
-
-#### mouseOverNehubaUI
-
-`Observable<{ landmark, segments, customLandmark }>`.
-
-**nb** it is a known issue that if customLandmarks are destroyed/created while user mouse over the custom landmark this observable will emit `{ customLandmark: null }`
-
-### uiHandle
-
-#### getModalHandler()
-
-returns a modalHandler object, which has the following methods/properties:
-
-##### hide()
-
-Dynamically hides the modal
-
-##### show()
-
-Shows the modal
-
-##### title
-
-title of the modal (String)
-
-##### body
-
-body of the modal shown (String)
-
-##### footer
-
-footer of the modal (String)
-
-##### dismissable
-
-whether the modal is dismissable on click backdrop/esc key (Boolean)
-
-*n.b. if true, users will not be able to interact with the viewer unless you specifically call `handler.hide()`*
-
-#### launchNewWidget(manifest)
-
-returns a Promise. expects a JSON object, with the same key value as a plugin manifest. the *name* key must be unique, or the promise will be rejected. 
-
-#### getUserInput(config)
-
-returns a Promise, resolves when user confirms, rejects when user cancels. expects config object object with the following structure:
-
-```javascript
-const config = {
-  "title": "Title of the modal", // default: "Message"
-  "message":"Message to be seen by the user.", // default: ""
-  "placeholder": "Start typing here", // default: "Type your response here"
-  "defaultValue": "42" // default: ""
-  "iconClass":"fas fa-save" // default fas fa-save, set to falsy value to disable
-}
-```
-
-#### getUserConfirmation(config)
-
-returns a Promise, resolves when user confirms, rejects when user cancels. expects config object object with the following structure:
-
-```javascript
-const config = {
-  "title": "Title of the modal", // default: "Message"
-  "message":"Message to be seen by the user." // default: ""
-}
-```
-
-#### getUserToSelectARegion(message)
-
-**To be deprecated**
-
-_input_
-
-| input | type | desc |
-| --- | --- | --- |
-| message | `string` | human readable message displayed to the user | 
-| spec.type | `'POINT'` `'PARCELLATION_REGION'` **default** | type of region to be returned. |
-
-_returns_
-
-`Promise`, resolves to return array of region clicked, rejects with error object `{ userInitiated: boolean }`
-
-Requests user to select a region of interest. Resolving to the region selected by the user. Rejects if either user cancels by pressing `Esc` or `Cancel`, or by developer calling `cancelPromise`
-
-#### getUserToSelectRoi(message, spec)
-
-_input_
-
-| input | type | desc |
-| --- | --- | --- |
-| message | `string` | human readable message displayed to the user | 
-| spec.type | `POINT` `PARCELLATION_REGION` | type of ROI to be returned. |
-
-_returns_
-
-`Promise`
-
-**resolves**: return `{ type, payload }`. `type` is the same as `spec.type`, and `payload` differs depend on the type requested:
-
-| type | payload | example |
-| --- | --- | --- |
-| `POINT` | array of number in mm | `[12.2, 10.1, -0.3]` |
-| `PARCELLATION_REGOIN` | non empty array of region selected | `[{ "layer": { "name" : " viewer specific layer name " }, "segment": {} }]` |
-
-**rejects**: with error object `{ userInitiated: boolean }`
-
-Requests user to select a region of interest. If the `spec.type` input is missing, it is assumed to be `'PARCELLATION_REGION'`. Resolving to the region selected by the user. Rejects if either user cancels by pressing `Esc` or `Cancel`, or by developer calling `cancelPromise`
-
-#### cancelPromise(promise)
-
-returns `void`
-  
-_input_ 
-
-| input | type | desc |
-| --- | --- | --- |
-| promise | `Promise` | Reference to the __exact__ promise returned by `uiHnandle` methods |
-
-Cancel the request to select a parcellation region.
-
-_usage example_
-
-```javascript
-
-(() => {
-  const pr = interactive.uiHandle.getUserToSelectARegion(`webJuGEx would like you to select a region`)
-
-  pr.then(region => {  })
-    .catch(console.warn)
-
-  /*
-    * do NOT do 
-    * 
-    * const pr = interactive.uiHandle.getUserToSelectARegion(`webJuGEx would like you to select a region`)
-    *   .then(region => {  })
-    *   -catch(console.warn)
-    * 
-    * the promise passed to `cancelPromise` must be the exact promise returned.
-    * by chaining then/catch, a new reference is returned
-    */
-
-  setTimeout(() => {
-    try {
-      interactive.uiHandle.cancelPromise(pr)
-    } catch (e) {
-      // if the promise has been fulfilled (by resolving or user cancel), cancelPromise will throw
-    }
-  }, 5000)
-})()
-```
-
-### pluginControl
-
-#### loadExternalLibraries([LIBRARY_NAME_1,LIBRARY_NAME_2])
-
-Function that loads external libraries. Pass the name of the libraries as an Array of string, and returns a Promise. When promise resolves, the libraries are loaded.
-
-**n.b.** while unlikely, there is a possibility that multiple requests to load external libraries in quick succession can cause the promise to resolve before the library is actually loaded. 
-
-```js
-const currentlySupportedLibraries = ['jquery@2','jquery@3','webcomponentsLite@1.1.0','react@16','reactdom@16','vue@2.5.16']
-
-window.interactivewViewer.loadExternalLibraries(currentlySupportedLibraries)
-  .then(() => {
-    /* loaded */
-  })
-  .catch(e=>console.warn(e))
-
-```
-
-####  unloadExternalLibraries([LIBRARY_NAME_1,LIBRARY_NAME_2])
-
-unloading the libraries (should be called on shutdown).
-
-#### *[PLUGINNAME]*
-
-returns a plugin handler. This would be how to interface with the plugins.
-
-##### blink()
-
-Function that causes the floating widget to blink, attempt to grab user attention (silently fails if called on startup).
-
-##### setProgressIndicator(val:number|null)
-
-Set the progress of the plugin. Useful for giving user feedbacks on the progress of a long running process. Call the function with null to unset the progress.
-
-##### shutdown()
-
-Function that causes the widget to shutdown dynamically. (triggers onShutdown callback, silently fails if called on startup)
-
-##### onShutdown(callback)
-
-Attaches a callback function, which is called when the plugin is shutdown.
-
-##### initState
-
-passed from `manifest.json`. Useful for setting initial state of the plugin. Can be any JSON valid value (array, object, string).
-
-##### initStateUrl
-
-passed from `manifest.json`. Useful for setting initial state of the plugin.  Can be any JSON valid value (array, object, string).
-
-##### setInitManifestUrl(url|null)
-
-set/unset the url for a manifest json that will be fetched on atlas viewer startup. the argument should be a valid URL, has necessary CORS header, and returns a valid manifest json file. null will unset the search param. Useful for passing/preserving state. If called multiple times, the last one will take effect.
-
-```js
-const pluginHandler = window.interactiveViewer.pluginControl[PLUGINNAME]
-
-const subscription = window.interactiveViewer.metadata.selectedTemplateBSubject.subscribe(template=>console.log(template))
-
-fetch(`http://YOUR_BACKEND.com/API_ENDPOINT`)
-  .then(data=>pluginHandler.blink(20))
-
-pluginHandler.onShutdown(()=>{
-  subscription.unsubscribe()
-})
-```
-
-------
-
-## window.nehubaViewer
-
-nehuba object, exposed if developer would like to use it
-
-## window.viewer
-
-neuroglancer object, exposed if developer would like to use it
\ No newline at end of file
diff --git a/src/routerModule/module.ts b/src/routerModule/module.ts
index 1053deeb997244e486942db154f13e3d1c057f51..c5e4845005c2fcdc4db1f5d1f68151bae4754a50 100644
--- a/src/routerModule/module.ts
+++ b/src/routerModule/module.ts
@@ -2,6 +2,7 @@ import { APP_BASE_HREF } from "@angular/common";
 import { NgModule } from "@angular/core";
 import { RouterModule } from '@angular/router'
 import { RouterService } from "./router.service";
+import { RouteStateTransformSvc } from "./routeStateTransform.service";
 import { routes } from "./util";
 
 
@@ -16,7 +17,8 @@ import { routes } from "./util";
       provide: APP_BASE_HREF,
       useValue: '/'
     },
-    RouterService
+    RouterService,
+    RouteStateTransformSvc,
   ],
   exports:[
     RouterModule
diff --git a/src/routerModule/parseRouteToTmplParcReg.spec.ts b/src/routerModule/parseRouteToTmplParcReg.spec.ts
deleted file mode 100644
index d81c9491c540006a068f13ad7b8e573f645d0345..0000000000000000000000000000000000000000
--- a/src/routerModule/parseRouteToTmplParcReg.spec.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-import { parseSearchParamForTemplateParcellationRegion } from './parseRouteToTmplParcReg'
-
-const url = `/a:juelich:iav:atlas:v1.0.0:1/t:minds:core:referencespace:v1.0.0:dafcffc5-4826-4bf1-8ff6-46b8a31ff8e2/p:minds:core:parcellationatlas:v1.0.0:94c1125b-b87e-45e4-901c-00daee7f2579-26/@:0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..0.1w4W0~.0..1jtG`
-
-const fakeState = {
-}
-const fakeTmpl = {
-  '@id': 'foobar'
-}
-const fakeParc = {
-  '@id': 'buzbaz'
-}
-const fakeRegions = [{
-  ngId: 'foo',
-  labelIndex: 152
-}]
-
-describe('parseRouteToTmplParcReg.ts', () => {
-  describe('> parseSearchParamForTemplateParcellationRegion', () => {
-    it('> parses selected region properly', () => {
-      
-    })
-  })
-})
\ No newline at end of file
diff --git a/src/routerModule/parseRouteToTmplParcReg.ts b/src/routerModule/parseRouteToTmplParcReg.ts
deleted file mode 100644
index a1ed2a7c47003fbaae169cc49bdd0b7e8be0e3bb..0000000000000000000000000000000000000000
--- a/src/routerModule/parseRouteToTmplParcReg.ts
+++ /dev/null
@@ -1,110 +0,0 @@
-import { getGetRegionFromLabelIndexId } from 'src/util/fn'
-import { serialiseParcellationRegion } from "common/util"
-import { decodeToNumber, separator } from './cipher'
-
-import {
-  TUrlAtlas,
-  TUrlPathObj,
-} from './type'
-import { UrlTree } from '@angular/router'
-
-
-export const PARSE_ERROR = {
-  TEMPALTE_NOT_SET: 'TEMPALTE_NOT_SET',
-  TEMPLATE_NOT_FOUND: 'TEMPLATE_NOT_FOUND',
-  PARCELLATION_NOT_UPDATED: 'PARCELLATION_NOT_UPDATED',
-}
-
-export const encodeId = (id: string) => id && id.replace(/\//g, ':')
-export const decodeId = (codedId: string) => codedId && codedId.replace(/:/g, '/')
-
-export function parseSearchParamForTemplateParcellationRegion(obj: TUrlPathObj<string[], TUrlAtlas<string[]>>, fullPath: UrlTree, state: any, warnCb: (arg: string) => void) {
-
-  /**
-   * TODO if search param of either template or parcellation is incorrect, wrong things are searched
-   */
-  
-
-  const templateSelected = (() => {
-    const { fetchedTemplates } = state.viewerState
-
-    const searchedId = obj.t && decodeId(obj.t[0])
-
-    if (!searchedId) return null
-    const templateToLoad = fetchedTemplates.find(template => (template['@id'] || template['fullId']) === searchedId)
-    if (!templateToLoad) { throw new Error(PARSE_ERROR.TEMPLATE_NOT_FOUND) }
-    return templateToLoad
-  })()
-
-  const parcellationSelected = (() => {
-    if (!templateSelected) return null
-    const searchedId = obj.p && decodeId(obj.p[0])
-
-    const parcellationToLoad = templateSelected.parcellations.find(parcellation => (parcellation['@id'] || parcellation['fullId']) === searchedId)
-    if (!parcellationToLoad) { 
-      warnCb(`parcellation with id ${searchedId} not found... load the first parc instead`)
-    }
-    return parcellationToLoad || templateSelected.parcellations[0]
-  })()
-
-  /* selected regions */
-
-  const regionsSelected = (() => {
-    if (!parcellationSelected) return []
-
-    // TODO deprecate. Fallback (defaultNgId) (should) already exist
-    // if (!viewerState.parcellationSelected.updated) throw new Error(PARCELLATION_NOT_UPDATED)
-
-    const getRegionFromlabelIndexId = getGetRegionFromLabelIndexId({ parcellation: parcellationSelected })
-    /**
-     * either or both parcellationToLoad and .regions maybe empty
-     */
-
-    try {
-      const json = obj.r
-        ? { [obj.r[0]]: obj.r[1] }
-        : JSON.parse(fullPath.queryParams['cRegionsSelected'] || '{}')
-
-      const selectRegionIds = []
-
-      for (const ngId in json) {
-        const val = json[ngId]
-        const labelIndicies = val.split(separator).map(n => {
-          try {
-            return decodeToNumber(n)
-          } catch (e) {
-            /**
-             * TODO poisonsed encoded char, send error message
-             */
-            return null
-          }
-        }).filter(v => !!v)
-        for (const labelIndex of labelIndicies) {
-          selectRegionIds.push( serialiseParcellationRegion({ ngId, labelIndex }) )
-        }
-      }
-      return selectRegionIds
-        .map(labelIndexId => {
-          const region = getRegionFromlabelIndexId({ labelIndexId })
-          if (!region) {
-            // cb && cb({ type: ID_ERROR, message: `region with id ${labelIndexId} not found, and will be ignored.` })
-          }
-          return region
-        })
-        .filter(r => !!r)
-
-    } catch (e) {
-      /**
-       * parsing cRegionSelected error
-       */
-      // cb && cb({ type: DECODE_CIPHER_ERROR, message: `parsing cRegionSelected error ${e.toString()}` })
-    }
-    return []
-  })()
-
-  return {
-    templateSelected,
-    parcellationSelected,
-    regionsSelected,
-  }
-}
diff --git a/src/routerModule/routeStateTransform.service.spec.ts b/src/routerModule/routeStateTransform.service.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..590619e9e2f428f4ebd07591e14d6e911862701c
--- /dev/null
+++ b/src/routerModule/routeStateTransform.service.spec.ts
@@ -0,0 +1,199 @@
+import { TestBed } from "@angular/core/testing"
+import { of } from "rxjs"
+import { SAPI } from "src/atlasComponents/sapi"
+import { RouteStateTransformSvc } from "./routeStateTransform.service"
+import { DefaultUrlSerializer } from "@angular/router"
+import * as nehubaConfigService from "src/viewerModule/nehuba/config.service"
+import { atlasSelection, userInteraction } from "src/state"
+import { encodeNumber } from "./cipher"
+
+const serializer = new DefaultUrlSerializer()
+
+describe("> routeStateTransform.service.ts", () => {
+  describe("> RouteStateTransformSvc", () => {
+    let svc: RouteStateTransformSvc
+    beforeEach(() => {
+      TestBed.configureTestingModule({
+        providers: [
+          RouteStateTransformSvc,
+          {
+            provide: SAPI,
+            useValue: {
+              atlases$: of([]),
+              getSpaceDetail: jasmine.createSpy('getSpaceDetail'),
+              getParcDetail: jasmine.createSpy('getParcDetail'),
+              getParcRegions: jasmine.createSpy('getParcRegions'),
+            }
+          }
+        ]
+      })
+      svc = TestBed.inject(RouteStateTransformSvc)
+    })
+
+    describe("> cvtRouteToState", () => {
+      describe("> decode region", () => {
+
+      })
+
+      describe("> decode sv", () => {
+        let sv: string[]
+        beforeEach(async () => {
+          const searchParam = new URLSearchParams()
+          searchParam.set('standaloneVolumes', '["precomputed://https://object.cscs.ch/v1/AUTH_08c08f9f119744cbbf77e216988da3eb/imgsvc-46d9d64f-bdac-418e-a41b-b7f805068c64"]')
+          const url = `foo/bar?${searchParam.toString()}`
+          const urlTree = serializer.parse(url)
+          const state = await svc.cvtRouteToState(urlTree)
+          sv = state["[state.atlasSelection]"].standAloneVolumes
+        })
+  
+        it('> sv should be truthy', () => {
+          expect(sv).toBeTruthy()
+        })
+  
+        it('> sv should be array', () => {
+          expect(
+            Array.isArray(sv)
+          ).toBeTrue()
+        })
+  
+        it('> sv should have length 1', () => {
+          expect(sv.length).toEqual(1)
+        })
+  
+        it('> sv[0] should be expected value', () => {
+          expect(sv[0]).toEqual(
+            'precomputed://https://object.cscs.ch/v1/AUTH_08c08f9f119744cbbf77e216988da3eb/imgsvc-46d9d64f-bdac-418e-a41b-b7f805068c64'
+          )
+        })
+      })
+
+      describe("> decode navigation", () => {
+        beforeEach(() => {
+        })
+        // it('> if not present, should show something palatable', async () => {
+
+        //   const scale = 0.25
+  
+        //   const url = `/a:juelich:iav:atlas:v1.0.0:1/t:minds:core:referencespace:v1.0.0:dafcffc5-4826-4bf1-8ff6-46b8a31ff8e2/p:minds:core:parcellationatlas:v1.0.0:94c1125b-b87e-45e4-901c-00daee7f2579-290`
+
+        //   const urlTree = serializer.parse(url)
+        //   const state = await svc.cvtRouteToState(urlTree)
+          
+        //   const { navigation } = state["[state.atlasSelection]"]
+        //   const {
+        //     orientation,
+        //     perspectiveOrientation,
+        //     position,
+        //     zoom,
+        //     perspectiveZoom,
+        //   } = navigation
+  
+        //   expect(orientation).toEqual([0,0,0,1])
+        //   expect(perspectiveOrientation).toEqual([
+        //     0.3140767216682434,
+        //     -0.7418519854545593,
+        //     0.4988985061645508,
+        //     -0.3195493221282959
+        //   ])
+        //   expect(position).toEqual([0,0,0])
+        //   expect(zoom).toEqual(350000 * scale)
+        //   expect(perspectiveZoom).toEqual(1922235.5293810747 * scale)
+        // })
+      })
+    })
+    
+    describe("> cvtStateToRoute", () => {
+
+      describe('> should be able encode region properly', () => {
+        let getRegionLabelIndexSpy: jasmine.Spy = jasmine.createSpy('getRegionLabelIndex')
+        let getParcNgId: jasmine.Spy = jasmine.createSpy('getParcNgId')
+        let atlasSelectionSpy: Record<string, jasmine.Spy> = {
+          selectedAtlas: jasmine.createSpy('selectedAtlas'),
+          selectedParcellation: jasmine.createSpy('selectedParcellation'),
+          selectedTemplate: jasmine.createSpy('selectedTemplate'),
+          selectedRegions: jasmine.createSpy('selectedRegions'),
+          standaloneVolumes: jasmine.createSpy('standaloneVolumes'),
+          navigation: jasmine.createSpy('navigation'),
+        }
+
+        let userInteractionSpy: Record<string, jasmine.Spy> = {
+          selectedFeature: jasmine.createSpy('selectedFeature')
+        }
+
+        const altasObj = {"@id": 'foo-bar-a'}
+        const templObj = {"@id": 'foo-bar-t'}
+        const parcObj = {"@id": 'foo-bar-p'}
+        const regions = [{}]
+        const standAloneVolumes = []
+        const navigation = null
+
+        beforeEach(() => {
+          spyOnProperty(nehubaConfigService, 'getRegionLabelIndex').and.returnValue(getRegionLabelIndexSpy)
+          spyOnProperty(nehubaConfigService, 'getParcNgId').and.returnValue(getParcNgId)
+          spyOnProperty(atlasSelection, 'selectors').and.returnValue(atlasSelectionSpy)
+          spyOnProperty(userInteraction, 'selectors').and.returnValue(userInteractionSpy)
+
+          atlasSelectionSpy.selectedAtlas.and.returnValue(altasObj)
+          atlasSelectionSpy.selectedParcellation.and.returnValue(templObj)
+          atlasSelectionSpy.selectedTemplate.and.returnValue(parcObj)
+          atlasSelectionSpy.selectedRegions.and.returnValue(regions)
+          atlasSelectionSpy.standaloneVolumes.and.returnValue(standAloneVolumes)
+          atlasSelectionSpy.navigation.and.returnValue(navigation)
+
+          userInteractionSpy.selectedFeature.and.returnValue(null)
+        })
+
+        afterEach(() => {
+          getRegionLabelIndexSpy.calls.reset()
+          getParcNgId.calls.reset()
+          for (const spyRecord of [atlasSelectionSpy, userInteractionSpy]) {
+            for (const key in spyRecord) {
+              spyRecord[key].calls.reset()
+            }
+          }
+        })
+
+        it('> calls correct functions', () => {
+
+          getRegionLabelIndexSpy.and.returnValue(11)
+          getParcNgId.and.returnValue('foo-bar')
+
+          const state = {}
+          const svc = TestBed.inject(RouteStateTransformSvc)
+          const s = svc.cvtStateToRoute(state as any)
+
+          for (const key in atlasSelectionSpy) {
+            expect(atlasSelectionSpy[key]).toHaveBeenCalledTimes(1)
+          }
+        })
+
+        it('> regular ngId', () => {
+          const ngId = 'foobar'
+          const labelIndex = 124
+          
+          getRegionLabelIndexSpy.and.returnValue(labelIndex)
+          getParcNgId.and.returnValue(ngId)
+
+          const state = {}
+          const svc = TestBed.inject(RouteStateTransformSvc)
+          const s = svc.cvtStateToRoute(state as any)
+
+          expect(s).toContain(`r:${ngId}::${encodeNumber(labelIndex, { float: false })}`)
+        })
+  
+        it('> ngId containing ()', () => {
+          const ngId = 'foobar(1)'
+          const labelIndex = 124
+
+          getRegionLabelIndexSpy.and.returnValue(labelIndex)
+          getParcNgId.and.returnValue(ngId)
+          
+          const state = {}
+          const svc = TestBed.inject(RouteStateTransformSvc)
+          const s = svc.cvtStateToRoute(state as any)
+          expect(s).toContain(`r:foobar%25281%2529::${encodeNumber(labelIndex, { float: false })}`)
+        })
+      })
+    })
+  })
+})
diff --git a/src/routerModule/routeStateTransform.service.ts b/src/routerModule/routeStateTransform.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..dc7da45d8a9eed4d3b8c4edbe31c409864d09741
--- /dev/null
+++ b/src/routerModule/routeStateTransform.service.ts
@@ -0,0 +1,286 @@
+import { Injectable } from "@angular/core";
+import { UrlSegment, UrlTree } from "@angular/router";
+import { map } from "rxjs/operators";
+import { SAPI, SapiRegionModel } from "src/atlasComponents/sapi";
+import { atlasSelection, defaultState, MainState, plugins, userInteraction } from "src/state";
+import { getParcNgId, getRegionLabelIndex } from "src/viewerModule/nehuba/config.service";
+import { decodeToNumber, encodeNumber, encodeURIFull, separator } from "./cipher";
+import { TUrlAtlas, TUrlPathObj, TUrlStandaloneVolume } from "./type";
+import { decodePath, encodeId, decodeId, endcodePath } from "./util";
+
+@Injectable()
+export class RouteStateTransformSvc {
+
+  static GetOneAndOnlyOne<T>(arr: T[]): T{
+    if (!arr || arr.length === 0) return null
+    if (arr.length > 1) throw new Error(`expecting exactly 1 item, got ${arr.length}`)
+    return arr[0]
+  }
+
+  constructor(private sapi: SAPI){}
+  
+  private async getATPR(obj: TUrlPathObj<string[], TUrlAtlas<string[]>>){
+    const selectedAtlasId = decodeId( RouteStateTransformSvc.GetOneAndOnlyOne(obj.a) )
+    const selectedTemplateId = decodeId( RouteStateTransformSvc.GetOneAndOnlyOne(obj.t) )
+    const selectedParcellationId = decodeId( RouteStateTransformSvc.GetOneAndOnlyOne(obj.p) )
+    const selectedRegionIds = obj.r
+    
+    if (!selectedAtlasId || !selectedTemplateId || !selectedParcellationId) {
+      return {}
+    }
+
+    const [
+      selectedAtlas,
+      selectedTemplate,
+      selectedParcellation,
+      allParcellationRegions = []
+    ] = await Promise.all([
+      this.sapi.atlases$.pipe(
+        map(atlases => atlases.find(atlas => atlas["@id"] === selectedAtlasId))
+      ).toPromise(),
+      this.sapi.getSpaceDetail(selectedAtlasId, selectedTemplateId, { priority: 10 }).toPromise(),
+      this.sapi.getParcDetail(selectedAtlasId, selectedParcellationId, { priority: 10 }).toPromise(),
+      this.sapi.getParcRegions(selectedAtlasId, selectedParcellationId, selectedTemplateId, { priority: 10 }).toPromise(),
+    ])
+
+    const ngIdToRegionMap: Map<string, Map<number, SapiRegionModel[]>> = new Map()
+
+    for (const region of allParcellationRegions) {
+      const ngId = getParcNgId(selectedAtlas, selectedTemplate, selectedParcellation, region)
+      if (!ngIdToRegionMap.has(ngId)) {
+        ngIdToRegionMap.set(ngId, new Map())
+      }
+      const map = ngIdToRegionMap.get(ngId)
+
+      const idx = getRegionLabelIndex(selectedAtlas, selectedTemplate, selectedParcellation, region)
+      if (!map.has(idx)) {
+        map.set(idx, [])
+      }
+      map.get(idx).push(region)
+    }
+    
+    const selectedRegions = (() => {
+      if (!selectedRegionIds) return []
+      /**
+       * assuming only 1 selected region
+       * if this assumption changes, iterate over array of selectedRegionIds
+       */
+      const json = { [selectedRegionIds[0]]: selectedRegionIds[1] }
+
+      for (const ngId in json) {
+        if (!ngIdToRegionMap.has(ngId)) {
+          console.error(`could not find matching map for ${ngId}`)
+          continue
+        }
+
+        const map = ngIdToRegionMap.get(ngId)
+        
+        const val = json[ngId]
+        const labelIndicies = val.split(separator).map(n => {
+          try {
+            return decodeToNumber(n)
+          } catch (e) {
+            /**
+             * TODO poisonsed encoded char, send error message
+             */
+            return null
+          }
+        }).filter(v => !!v)
+
+        return labelIndicies.map(idx => map.get(idx) || []).flatMap(v => v)
+      }
+      return []
+    })()
+
+    return {
+      selectedAtlas,
+      selectedTemplate,
+      selectedParcellation,
+      selectedRegions,
+      allParcellationRegions
+    }
+  }
+
+  async cvtRouteToState(fullPath: UrlTree) {
+
+    const returnState: MainState = defaultState
+    const pathFragments: UrlSegment[] = fullPath.root.hasChildren()
+      ? fullPath.root.children['primary'].segments
+      : []
+
+    const returnObj: Partial<TUrlPathObj<string[], unknown>> = {}
+    for (const f of pathFragments) {
+      const { key, val } = decodePath(f.path) || {}
+      if (!key || !val) continue
+      returnObj[key] = val
+    }
+
+    // nav obj is almost always defined, regardless if standaloneVolume or not
+    const cViewerState = returnObj['@'] && returnObj['@'][0]
+    let parsedNavObj: MainState['[state.atlasSelection]']['navigation']
+    if (cViewerState) {
+      try {
+        const [ cO, cPO, cPZ, cP, cZ ] = cViewerState.split(`${separator}${separator}`)
+        const o = cO.split(separator).map(s => decodeToNumber(s, {float: true}))
+        const po = cPO.split(separator).map(s => decodeToNumber(s, {float: true}))
+        const pz = decodeToNumber(cPZ)
+        const p = cP.split(separator).map(s => decodeToNumber(s))
+        const z = decodeToNumber(cZ)
+        parsedNavObj = {
+          orientation: o,
+          perspectiveOrientation: po,
+          perspectiveZoom: pz,
+          position: p,
+          zoom: z,
+        }
+      } catch (e) {
+        /**
+         * TODO Poisoned encoded char
+         * send error message
+         */
+        console.error(`cannot parse navigation state`, e)
+      }
+    }
+
+    // pluginState should always be defined, regardless if standalone volume or not
+    const pluginStates = fullPath.queryParams['pl']
+    if (pluginStates) {
+      try {
+        const arrPluginStates: string[] = JSON.parse(pluginStates)
+        if (arrPluginStates.length > 1) throw new Error(`can only initialise one plugin at a time`)
+        returnState["[state.plugins]"].initManifests = {
+          [plugins.INIT_MANIFEST_SRC]: arrPluginStates
+        }
+      } catch (e) {
+        /**
+         * parsing plugin error
+         */
+        console.error(`parse plugin states error`, e, pluginStates)
+      }
+    }
+
+    // If sv (standaloneVolume is defined)
+    // only load sv in state
+    // ignore all other params
+    // /#/sv:%5B%22precomputed%3A%2F%2Fhttps%3A%2F%2Fobject.cscs.ch%2Fv1%2FAUTH_08c08f9f119744cbbf77e216988da3eb%2Fimgsvc-46d9d64f-bdac-418e-a41b-b7f805068c64%22%5D
+    const standaloneVolumes = fullPath.queryParams['standaloneVolumes']
+    try {
+      const parsedArr = JSON.parse(standaloneVolumes)
+      if (!Array.isArray(parsedArr)) throw new Error(`Parsed standalone volumes not of type array`)
+
+      returnState["[state.atlasSelection]"].standAloneVolumes = parsedArr
+      returnState["[state.atlasSelection]"].navigation = parsedNavObj
+      return returnState
+    } catch (e) {
+      // if any error occurs, parse rest per normal
+    }
+
+    // try to get feature
+    try {
+      if (returnObj.f && returnObj.f.length === 1) {
+        const decodedFeatId = decodeId(returnObj.f[0])
+        const feature = await this.sapi.getFeature(decodedFeatId).detail$.toPromise()
+        returnState["[state.userInteraction]"].selectedFeature = feature
+      }
+    } catch (e) {
+      console.error(`fetching selected feature error`)
+    }
+
+    try {
+      const { selectedAtlas, selectedParcellation, selectedRegions = [], selectedTemplate, allParcellationRegions } = await this.getATPR(returnObj as TUrlPathObj<string[], TUrlAtlas<string[]>>)
+      returnState["[state.atlasSelection]"].selectedAtlas = selectedAtlas
+      returnState["[state.atlasSelection]"].selectedParcellation = selectedParcellation
+      returnState["[state.atlasSelection]"].selectedTemplate = selectedTemplate
+      returnState["[state.atlasSelection]"].selectedRegions = selectedRegions || []
+      returnState["[state.atlasSelection]"].selectedParcellationAllRegions = allParcellationRegions || []
+      returnState["[state.atlasSelection]"].navigation = parsedNavObj
+    } catch (e) {
+      // if error, show error on UI?
+      console.error(`parse template, parc, region error`, e)
+    }
+    return returnState
+  }
+
+  cvtStateToRoute(_state: MainState) {
+    
+    /**
+     * need to create new references here
+     * or else, the memoized selector will spit out undefined
+     */
+    const state:MainState = JSON.parse(JSON.stringify(_state))
+
+    const selectedAtlas = atlasSelection.selectors.selectedAtlas(state)
+    const selectedParcellation = atlasSelection.selectors.selectedParcellation(state)
+    const selectedTemplate = atlasSelection.selectors.selectedTemplate(state)
+    
+    const selectedRegions = atlasSelection.selectors.selectedRegions(state)
+    const standaloneVolumes = atlasSelection.selectors.standaloneVolumes(state)
+    const navigation = atlasSelection.selectors.navigation(state)
+    const selectedFeature = userInteraction.selectors.selectedFeature(state)
+  
+    const searchParam = new URLSearchParams()
+  
+    let cNavString: string
+    if (navigation) {
+      const { orientation, perspectiveOrientation, perspectiveZoom, position, zoom } = navigation
+      if (orientation && perspectiveOrientation && perspectiveZoom && position && zoom) {
+        cNavString = [
+          orientation.map((n: number) => encodeNumber(n, {float: true})).join(separator),
+          perspectiveOrientation.map(n => encodeNumber(n, {float: true})).join(separator),
+          encodeNumber(Math.floor(perspectiveZoom)),
+          Array.from(position).map((v: number) => Math.floor(v)).map(n => encodeNumber(n)).join(separator),
+          encodeNumber(Math.floor(zoom)),
+        ].join(`${separator}${separator}`)
+      }
+    }
+  
+    // encoding selected regions
+    let selectedRegionsString: string
+    if (selectedRegions.length === 1) {
+      const region = selectedRegions[0]
+      const labelIndex = getRegionLabelIndex(selectedAtlas, selectedTemplate, selectedParcellation, region)
+      
+      const ngId = getParcNgId(selectedAtlas, selectedTemplate, selectedParcellation, region)
+      selectedRegionsString = `${ngId}::${encodeNumber(labelIndex, { float: false })}`
+    }
+    let routes: TUrlPathObj<string, TUrlAtlas<string>> | TUrlPathObj<string, TUrlStandaloneVolume<string>>
+    
+    routes = {
+      // for atlas
+      a: selectedAtlas && encodeId(selectedAtlas['@id']),
+      // for template
+      t: selectedTemplate && encodeId(selectedTemplate['@id']),
+      // for parcellation
+      p: selectedParcellation && encodeId(selectedParcellation['@id']),
+      // for regions
+      r: selectedRegionsString && encodeURIFull(selectedRegionsString),
+      // nav
+      ['@']: cNavString,
+      // showing dataset
+      f: selectedFeature && encodeId(selectedFeature["@id"])
+    }
+  
+    /**
+     * if any params needs to overwrite previosu routes, put them here
+     */
+    if (standaloneVolumes && Array.isArray(standaloneVolumes) && standaloneVolumes.length > 0) {
+      searchParam.set('standaloneVolumes', JSON.stringify(standaloneVolumes))
+      routes = {
+        // nav
+        ['@']: cNavString,
+      } as TUrlPathObj<string, TUrlStandaloneVolume<string>>
+    }
+  
+    const routesArr: string[] = []
+    for (const key in routes) {
+      if (!!routes[key]) {
+        const segStr = endcodePath(key, routes[key])
+        routesArr.push(segStr)
+      }
+    }
+  
+    return searchParam.toString() === '' 
+      ? routesArr.join('/')
+      : `${routesArr.join('/')}?${searchParam.toString()}`
+  }
+}
diff --git a/src/routerModule/router.service.spec.ts b/src/routerModule/router.service.spec.ts
index a06a700ff5c0d726381c0ecb9e3de24d08170bc7..f85b0beeab988b802bbff64dae27e0ffc34fc042 100644
--- a/src/routerModule/router.service.spec.ts
+++ b/src/routerModule/router.service.spec.ts
@@ -1,199 +1,193 @@
 import { APP_BASE_HREF, Location } from "@angular/common"
 import { discardPeriodicTasks, fakeAsync, TestBed, tick } from "@angular/core/testing"
-import { Router } from "@angular/router"
-import { RouterTestingModule } from '@angular/router/testing'
+import { DefaultUrlSerializer, NavigationEnd, Router } from "@angular/router"
 import { MockStore, provideMockStore } from "@ngrx/store/testing"
 import { cold } from "jasmine-marbles"
-import { of } from "rxjs"
-import { PureContantService } from "src/util"
+import { of, Subject } from "rxjs"
+import { SAPI } from "src/atlasComponents/sapi"
 import { RouterService } from "./router.service"
+import { RouteStateTransformSvc } from "./routeStateTransform.service"
 import * as util from './util'
 
-const { routes, DummyCmp } = util
-const dummyPureConstantService = {
-  allFetchingReady$: of(true)
+const { DummyCmp } = util
+
+let cvtStateToRouteSpy: jasmine.Spy 
+let cvtRouteToStateSpy: jasmine.Spy 
+const mockRouter = {
+  events: new Subject(),
+  parseUrl: (url: string) => {
+    return new DefaultUrlSerializer().parse(url)
+  },
+  url: '/',
+  navigate: jasmine.createSpy('navigate'),
+  navigateByUrl: jasmine.createSpy('navigateByUrl')
 }
 
-let cvtStateToHashedRoutesSpy: jasmine.Spy 
-let cvtFullRouteToStateSpy: jasmine.Spy 
-let location: Location
-let router: Router
-
 describe('> router.service.ts', () => {
   describe('> RouterService', () => {
     beforeEach(() => {
-      cvtStateToHashedRoutesSpy = jasmine.createSpy('cvtStateToHashedRoutesSpy')
-      cvtFullRouteToStateSpy = jasmine.createSpy('cvtFullRouteToState')
-
-      spyOnProperty(util, 'cvtStateToHashedRoutes').and.returnValue(cvtStateToHashedRoutesSpy)
-      spyOnProperty(util, 'cvtFullRouteToState').and.returnValue(cvtFullRouteToStateSpy)
+      cvtStateToRouteSpy = jasmine.createSpy('cvtStateToRouteSpy')
+      cvtRouteToStateSpy = jasmine.createSpy('cvtFullRouteToState')
 
       TestBed.configureTestingModule({
-        imports: [
-          RouterTestingModule.withRoutes(routes, {
-            useHash: true
-          }),
-        ],
+        imports: [],
         declarations: [
           DummyCmp,
         ],
         providers: [
           provideMockStore(),
-          {
-            provide: PureContantService,
-            useValue: dummyPureConstantService
-          },
           {
             provide: APP_BASE_HREF,
             useValue: '/'
+          },
+          {
+            provide: SAPI,
+            useValue: {
+              atlases$: of([])
+            }
+          },
+          {
+            provide: RouteStateTransformSvc,
+            useValue: {
+              cvtRouteToState: cvtRouteToStateSpy,
+              cvtStateToRoute: cvtStateToRouteSpy
+            }
+          },
+          {
+            provide: Router,
+            useValue: mockRouter
           }
         ]
       })
     })
 
     afterEach(() => {
-      cvtStateToHashedRoutesSpy.calls.reset()
-      cvtFullRouteToStateSpy.calls.reset()
+      cvtStateToRouteSpy.calls.reset()
+      cvtRouteToStateSpy.calls.reset()
+      mockRouter.navigateByUrl.calls.reset()
+      mockRouter.navigate.calls.reset()
     })
     describe('> on state set', () => {
+      const fakeState = {
+        foo: 'bar'
+      }
+      beforeEach(() => {
 
-      it('> should call cvtStateToHashedRoutes', fakeAsync(() => {
-        cvtStateToHashedRoutesSpy.and.callFake(() => ``)
-        const service = TestBed.inject(RouterService)
+        cvtRouteToStateSpy.and.resolveTo({
+          url: '/',
+          stateFromRoute: fakeState
+        })
         const store = TestBed.inject(MockStore)
-        const fakeState = {
-          foo: 'bar'
-        }
         store.setState(fakeState)
-        tick(320)
-        expect(cvtStateToHashedRoutesSpy).toHaveBeenCalledWith(fakeState)
+        const service = TestBed.inject(RouterService)
+      })
+
+      it('> should call cvtStateToHashedRoutes', fakeAsync(() => {
+        mockRouter.events.next(
+          new NavigationEnd(1, '/', '/')
+        )
+        tick(400)
+        expect(cvtStateToRouteSpy).toHaveBeenCalledWith(fakeState)
       }))
-      it('> if cvtStateToHashedRoutes throws, should navigate to home', fakeAsync(() => {
-        cvtStateToHashedRoutesSpy.and.callFake(() => {
+      it('> if cvtStateToRoute throws, should navigate to home', fakeAsync(() => {
+        cvtStateToRouteSpy.and.callFake(() => {
           throw new Error(`foo bar`)
         })
-        const service = TestBed.inject(RouterService)
-        const store = TestBed.inject(MockStore)
-        const fakeState = {
-          foo: 'bar'
-        }
-        store.setState(fakeState)
-        tick(320)
-        location = TestBed.inject(Location)
-        expect(
-          location.path()
-        ).toBe('/')
+        const baseHref = TestBed.inject(APP_BASE_HREF)
+
+        mockRouter.events.next(
+          new NavigationEnd(1, '/', '/')
+        )
+        tick(400)
+        expect(mockRouter.navigate).toHaveBeenCalledWith([baseHref])
 
       }))
       it('> if cvtStateToHashedRoutes returns, should navigate to expected location', fakeAsync(() => {
-        cvtStateToHashedRoutesSpy.and.callFake(() => {
-          return `foo/bar`
-        })
-        const service = TestBed.inject(RouterService)
-        const store = TestBed.inject(MockStore)
-        const fakeState = {
-          foo: 'bar'
-        }
-        store.setState(fakeState)
-        tick(320)
-        location = TestBed.inject(Location)
-        expect(
-          location.path()
-        ).toBe('/foo/bar')
+        cvtStateToRouteSpy.and.returnValue(`foo/bar`)
+        mockRouter.events.next(
+          new NavigationEnd(1, '/', '/')
+        )
+        tick(400)
+        expect(mockRouter.navigateByUrl).toHaveBeenCalledWith('/foo/bar')
       }))
     
       describe('> does not excessively call navigateByUrl', () => {
-        let navigateSpy: jasmine.Spy
-        let navigateByUrlSpy: jasmine.Spy
-        beforeEach(() => {
-          const router = TestBed.inject(Router)
-          navigateSpy = spyOn(router, 'navigate').and.callThrough()
-          navigateByUrlSpy = spyOn(router, 'navigateByUrl').and.callThrough()
-        })
-        afterEach(() => {
-          navigateSpy.calls.reset()
-          navigateByUrlSpy.calls.reset()
-        })
 
         it('> navigate calls navigateByUrl', fakeAsync(() => {
-          cvtStateToHashedRoutesSpy.and.callFake(() => {
+          cvtStateToRouteSpy.and.callFake(() => {
             return `foo/bar`
           })
-          TestBed.inject(RouterService)
-          const store = TestBed.inject(MockStore)
-          store.setState({
-            'hello': 'world'
-          })
-          tick(320)
-          expect(cvtStateToHashedRoutesSpy).toHaveBeenCalledTimes(1 + 1)
-          expect(navigateByUrlSpy).toHaveBeenCalledTimes(1)
+          mockRouter.events.next(
+            new NavigationEnd(1, '/', '/')
+          )
+          tick(400)
+          expect(cvtStateToRouteSpy).toHaveBeenCalledTimes(1 + 1)
+          expect(mockRouter.navigateByUrl).toHaveBeenCalledTimes(1)
         }))
 
         it('> same state should not navigate', fakeAsync(() => {
-          cvtStateToHashedRoutesSpy.and.callFake(() => {
+          cvtStateToRouteSpy.and.callFake(() => {
             return `foo/bar`
           })
-          
-          TestBed.inject(RouterService)
-          const router = TestBed.inject(Router)
-          router.navigate(['foo', 'bar'])
-          const store = TestBed.inject(MockStore)
-          store.setState({
-            'hello': 'world'
-          })
-          tick(320)
-          expect(cvtStateToHashedRoutesSpy).toHaveBeenCalledTimes(1 + 1)
-          expect(navigateByUrlSpy).toHaveBeenCalledTimes(1)
+          mockRouter.events.next(
+            new NavigationEnd(1, '/', '/')
+          )
+          tick(400)
+          expect(cvtStateToRouteSpy).toHaveBeenCalledTimes(1 + 1)
+          expect(mockRouter.navigateByUrl).toHaveBeenCalledTimes(1)
         }))
 
         it('> should handle queryParam gracefully', fakeAsync(() => {
           const searchParam = new URLSearchParams()
           const sv = '["precomputed://https://object.cscs.ch/v1/AUTH_08c08f9f119744cbbf77e216988da3eb/imgsvc-46d9d64f-bdac-418e-a41b-b7f805068c64"]'
           searchParam.set('standaloneVolumes', sv)
-          cvtStateToHashedRoutesSpy.and.callFake(() => {
+          cvtStateToRouteSpy.and.callFake(() => {
             return `foo/bar?${searchParam.toString()}`
           })
-          TestBed.inject(RouterService)
-          const store = TestBed.inject(MockStore)
-
-          TestBed.inject(RouterService)
-          const router = TestBed.inject(Router)
-          router.navigate(['foo', `bar`], { queryParams: { standaloneVolumes: sv }})
-          store.setState({
-            'hello': 'world'
-          })
-          tick(320)
-          expect(cvtStateToHashedRoutesSpy).toHaveBeenCalledTimes(1 + 1)
-          expect(navigateByUrlSpy).toHaveBeenCalledTimes(1)
+          mockRouter.events.next(
+            new NavigationEnd(1, '/', '/')
+          )
+          tick(400)
+          expect(cvtStateToRouteSpy).toHaveBeenCalledTimes(1 + 1)
+          expect(mockRouter.navigateByUrl).toHaveBeenCalledTimes(1)
         }))
       })
     })
   
     describe('> on route change', () => {
 
-      describe('> compares new state and previous state', () => {
+      const fakeState = {
+        foo: 'bar'
+      }
+      beforeEach(fakeAsync(() => {
+        cvtRouteToStateSpy.and.resolveTo(fakeState)
+        TestBed.inject(RouterService)
+        const store = TestBed.inject(MockStore)
+        store.setState(fakeState)
+        mockRouter.events.next(
+          new NavigationEnd(1, '/', '/')
+        )
+        tick(400)
+      }))
 
-        it('> calls cvtFullRouteToState', fakeAsync(() => {
+      describe('> compares new state and previous state', () => {
+        it('> calls cvtRouteToState', fakeAsync(() => {
           const fakeParsedState = {
             bizz: 'buzz'
           }
-          cvtFullRouteToStateSpy.and.callFake(() => fakeParsedState)
-          cvtStateToHashedRoutesSpy.and.callFake(() => {
+          cvtRouteToStateSpy.and.resolveTo(fakeParsedState)
+          cvtStateToRouteSpy.and.callFake(() => {
             return ['bizz', 'buzz']
           })
-          router = TestBed.inject(Router)
-          router.navigate(['foo', 'bar'])
-  
-          const service = TestBed.inject(RouterService)
-  
-          tick()
-  
-          expect(cvtFullRouteToStateSpy).toHaveBeenCalledWith(
-            router.parseUrl('/foo/bar'), {}, service['logError']
+          mockRouter.events.next(
+            new NavigationEnd(1, '/foo/bar', '/foo/bar')
           )
   
-          discardPeriodicTasks()
+          tick(160)
   
+          expect(cvtRouteToStateSpy).toHaveBeenCalledWith(
+            new DefaultUrlSerializer().parse('/foo/bar')
+          )
         }))
 
         it('> calls cvtStateToHashedRoutes with current state', fakeAsync(() => {
@@ -203,23 +197,20 @@ describe('> router.service.ts', () => {
           const fakeState = {
             foo: 'bar'
           }
-          cvtFullRouteToStateSpy.and.callFake(() => fakeParsedState)
+          cvtRouteToStateSpy.and.resolveTo(fakeParsedState)
           const store = TestBed.inject(MockStore)
           store.setState(fakeState)
 
-          cvtStateToHashedRoutesSpy.and.callFake(() => {
+          cvtStateToRouteSpy.and.callFake(() => {
             return ['bizz', 'buzz']
           })
-          router = TestBed.inject(Router)
-          router.navigate(['foo', 'bar'])
-  
-          TestBed.inject(RouterService)
+          mockRouter.events.next(
+            new NavigationEnd(1, '/foo/bar', '/foo/bar')
+          )
   
-          tick()
+          tick(160)
   
-          expect(cvtStateToHashedRoutesSpy).toHaveBeenCalledWith(fakeState)
-
-          discardPeriodicTasks()
+          expect(cvtStateToRouteSpy).toHaveBeenCalledWith(fakeState)
         }))
 
         describe('> when cvtStateToHashedRoutes ...', () => {
@@ -227,44 +218,42 @@ describe('> router.service.ts', () => {
             const fakeParsedState = {
               bizz: 'buzz'
             }
-            cvtFullRouteToStateSpy.and.callFake(() => fakeParsedState)
-            cvtStateToHashedRoutesSpy.and.callFake(() => {
+            cvtRouteToStateSpy.and.resolveTo(fakeParsedState)
+            cvtStateToRouteSpy.and.callFake(() => {
               throw new Error(`fizz buzz`)
             })
-            router = TestBed.inject(Router)
-            router.navigate(['foo', 'bar'])
+            
+            mockRouter.events.next(
+              new NavigationEnd(1, '/foo/bar', '/foo/bar')
+            )
     
-            TestBed.inject(RouterService)
             const store = TestBed.inject(MockStore)
             const dispatchSpy = spyOn(store, 'dispatch')
             
-            tick()
+            tick(160)
 
             expect(dispatchSpy).toHaveBeenCalled()
-    
-            discardPeriodicTasks()
           }))
 
           it('> ... returns different value, dispatches', fakeAsync(() => {
             const fakeParsedState = {
               bizz: 'buzz'
             }
-            cvtFullRouteToStateSpy.and.callFake(() => fakeParsedState)
-            cvtStateToHashedRoutesSpy.and.callFake(() => {
+            cvtRouteToStateSpy.and.resolveTo(fakeParsedState)
+            cvtStateToRouteSpy.and.callFake(() => {
               return `fizz/buzz`
             })
-            router = TestBed.inject(Router)
-            router.navigate(['foo', 'bar'])
+            mockRouter.events.next(
+              new NavigationEnd(1, '/foo/bar', '/foo/bar')
+            )
     
             TestBed.inject(RouterService)
             const store = TestBed.inject(MockStore)
             const dispatchSpy = spyOn(store, 'dispatch')
             
-            tick(320)
+            tick(160)
 
             expect(dispatchSpy).toHaveBeenCalled()
-    
-            discardPeriodicTasks()
           }))
 
           describe('> returns the same value', () => {
@@ -272,30 +261,31 @@ describe('> router.service.ts', () => {
               const fakeParsedState = {
                 bizz: 'buzz'
               }
-              cvtFullRouteToStateSpy.and.callFake(() => fakeParsedState)
-              cvtStateToHashedRoutesSpy.and.callFake(() => {
+              cvtRouteToStateSpy.and.resolveTo(fakeParsedState)
+              cvtStateToRouteSpy.and.callFake(() => {
                 return `foo/bar`
               })
-              router = TestBed.inject(Router)
-              router.navigate(['foo', 'bar'])
+              
+              mockRouter.events.next(
+                new NavigationEnd(1, '/foo/bar', '/foo/bar')
+              )
       
               const service = TestBed.inject(RouterService)
               const store = TestBed.inject(MockStore)
               const dispatchSpy = spyOn(store, 'dispatch')
               
-              tick(320)
+              tick(160)
 
               expect(dispatchSpy).not.toHaveBeenCalled()
       
-              discardPeriodicTasks()
             }))
             
             it('> takes into account of customRoute', fakeAsync(() => {
               const fakeParsedState = {
                 bizz: 'buzz'
               }
-              cvtFullRouteToStateSpy.and.callFake(() => fakeParsedState)
-              cvtStateToHashedRoutesSpy.and.callFake(() => {
+              cvtRouteToStateSpy.and.resolveTo(fakeParsedState)
+              cvtStateToRouteSpy.and.callFake(() => {
                 return `foo/bar`
               })
       
@@ -304,8 +294,10 @@ describe('> router.service.ts', () => {
                 'x-foo': 'hello'
               })
 
-              router = TestBed.inject(Router)
-              router.navigate(['foo', 'bar', 'x-foo:hello'])
+              
+              mockRouter.events.next(
+                new NavigationEnd(1, '/foo/bar/x-foo:hello', '/foo/bar/x-foo:hello')
+              )
 
               const store = TestBed.inject(MockStore)
               const dispatchSpy = spyOn(store, 'dispatch')
@@ -313,8 +305,6 @@ describe('> router.service.ts', () => {
               tick(320)
 
               expect(dispatchSpy).not.toHaveBeenCalled()
-      
-              discardPeriodicTasks()
             }))
           })
         })
@@ -323,11 +313,24 @@ describe('> router.service.ts', () => {
 
     describe('> customRoute$', () => {
       let decodeCustomStateSpy: jasmine.Spy
+
+      const fakeState = {
+        foo: 'bar'
+      }
+      let rService: RouterService
       beforeEach(() => {
+        cvtRouteToStateSpy.and.resolveTo({
+          url: '/',
+          stateFromRoute: fakeState
+        })
+        const store = TestBed.inject(MockStore)
+        store.setState(fakeState)
+        rService = TestBed.inject(RouterService)
         decodeCustomStateSpy = jasmine.createSpy('decodeCustomState')
         spyOnProperty(util, 'decodeCustomState').and.returnValue(decodeCustomStateSpy)
-
-        router = TestBed.inject(Router)
+        mockRouter.events.next(
+          new NavigationEnd(0, '/', '/')
+        )
       })
 
       afterEach(() => {
@@ -339,10 +342,11 @@ describe('> router.service.ts', () => {
           'x-foo': 'bar'
         }
         decodeCustomStateSpy.and.returnValue(value)
-        const rService = TestBed.inject(RouterService)
-        router.navigate(['foo'])
-        tick(320)
         
+        mockRouter.events.next(
+          new NavigationEnd(1, '/foo', '/foo')
+        )
+        tick(400)
         expect(rService.customRoute$).toBeObservable(
           cold('a', {
             a: {
@@ -350,7 +354,6 @@ describe('> router.service.ts', () => {
             }
           })
         )
-        discardPeriodicTasks()
       }))
       it('> merges observable from _customRoutes$', fakeAsync(() => {
         decodeCustomStateSpy.and.returnValue({})
@@ -374,9 +377,8 @@ describe('> router.service.ts', () => {
           'x-foo': 'bar'
         }
         decodeCustomStateSpy.and.returnValue(value)
-        const rService = TestBed.inject(RouterService)
         rService.setCustomRoute('x-fizz', 'buzz')
-        tick(320)
+        tick(400)
         
         expect(rService.customRoute$).toBeObservable(
           cold('(ba)', {
@@ -389,15 +391,10 @@ describe('> router.service.ts', () => {
             }
           })
         )
-        discardPeriodicTasks()
       }))
 
       it('> subsequent emits overwrites', fakeAsync(() => {
         decodeCustomStateSpy.and.returnValue({})
-        const rService = TestBed.inject(RouterService)
-        spyOn(router, 'navigateByUrl').and.callFake((() => {
-          console.log('navigate by url')
-        }) as any)
 
         const customRouteSpy = jasmine.createSpy('customRouteSpy')
         rService.customRoute$.subscribe(customRouteSpy)
@@ -426,7 +423,6 @@ describe('> router.service.ts', () => {
         for (const c in expectedCalls) {
           expect(customRouteSpy).toHaveBeenCalledWith(expectedCalls[c])
         }
-        discardPeriodicTasks()
       }))
     })
   })
diff --git a/src/routerModule/router.service.ts b/src/routerModule/router.service.ts
index a17b554adcc8d430c279d7c241f8f96438ca018d..3359055f7b70b395772c5d27842dffee46b1b374 100644
--- a/src/routerModule/router.service.ts
+++ b/src/routerModule/router.service.ts
@@ -3,12 +3,14 @@ import { APP_BASE_HREF } from "@angular/common";
 import { Inject } from "@angular/core";
 import { NavigationEnd, Router } from '@angular/router'
 import { Store } from "@ngrx/store";
-import { debounceTime, distinctUntilChanged, filter, map, shareReplay, startWith, switchMapTo, take, tap, withLatestFrom } from "rxjs/operators";
-import { generalApplyState } from "src/services/stateStore.helper";
-import { PureContantService } from "src/util";
-import { cvtStateToHashedRoutes, cvtFullRouteToState, encodeCustomState, decodeCustomState, verifyCustomState } from "./util";
-import { BehaviorSubject, combineLatest, merge, Observable, of } from 'rxjs'
+import { debounceTime, distinctUntilChanged, filter, map, mapTo, shareReplay, startWith, switchMap, switchMapTo, take, withLatestFrom } from "rxjs/operators";
+import { encodeCustomState, decodeCustomState, verifyCustomState } from "./util";
+import { BehaviorSubject, combineLatest, concat, merge, Observable, timer } from 'rxjs'
 import { scan } from 'rxjs/operators'
+import { RouteStateTransformSvc } from "./routeStateTransform.service";
+import { SAPI } from "src/atlasComponents/sapi";
+import { generalActions } from "src/state";
+
 
 @Injectable({
   providedIn: 'root'
@@ -20,11 +22,9 @@ export class RouterService {
     console.log(...e)
   }
 
-  private _customRoute$ = new BehaviorSubject<{
-    [key: string]: string
-  }>({})
+  private _customRoute$ = new BehaviorSubject<Record<string, string>>({})
 
-  public customRoute$: Observable<Record<string, any>>
+  public customRoute$: Observable<Record<string, string>>
 
   setCustomRoute(key: string, state: string){
     if (!verifyCustomState(key)) {
@@ -37,7 +37,8 @@ export class RouterService {
 
   constructor(
     router: Router,
-    pureConstantService: PureContantService,
+    routeToStateTransformSvc: RouteStateTransformSvc,
+    sapi: SAPI,
     store$: Store<any>,
     @Inject(APP_BASE_HREF) baseHref: string
   ){
@@ -51,7 +52,31 @@ export class RouterService {
 
     navEnd$.subscribe()
 
-    const ready$ = pureConstantService.allFetchingReady$.pipe(
+    /**
+     * onload
+     */
+    const onload$ = navEnd$.pipe(
+      take(1),
+      filter(ev => ev.urlAfterRedirects !== '/'),
+      switchMap(ev => 
+        routeToStateTransformSvc.cvtRouteToState(
+          router.parseUrl(
+            ev.urlAfterRedirects
+          )
+        )
+      )
+    )
+    onload$.subscribe(
+      state => {
+        store$.dispatch(
+          generalActions.generalApplyState({
+            state
+          })
+        )
+      }
+    )
+
+    const ready$ = sapi.atlases$.pipe(
       filter(flag => !!flag),
       take(1),
       shareReplay(1),
@@ -91,25 +116,47 @@ export class RouterService {
       ),
     )
 
-    ready$.pipe(
-      switchMapTo(
-        navEnd$.pipe(
-          withLatestFrom(
-            store$,
-            this.customRoute$.pipe(
-              startWith({})
-            )
+    /**
+     * does work too well =( 
+     */
+    concat(
+      onload$.pipe(
+        mapTo(false)
+      ),
+      timer(160).pipe(
+        mapTo(false)
+      ),
+      ready$.pipe(
+        map(val => !!val)
+      )
+    ).pipe(
+      switchMap(() => navEnd$),
+      map(navEv => navEv.urlAfterRedirects),
+      switchMap(url =>
+        routeToStateTransformSvc.cvtRouteToState(
+          router.parseUrl(
+            url
           )
+        ).then(stateFromRoute => {
+          return {
+            url,
+            stateFromRoute
+          }
+        })
+      ),
+      withLatestFrom(
+        store$,
+        this.customRoute$.pipe(
+          startWith({})
         )
       )
     ).subscribe(arg => {
-      const [ev, state, customRoutes] = arg
+      const [{ stateFromRoute, url }, currentState, customRoutes] = arg
+      const fullPath = url
       
-      const fullPath = ev.urlAfterRedirects
-      const stateFromRoute = cvtFullRouteToState(router.parseUrl(fullPath), state, this.logError)
       let routeFromState: string
       try {
-        routeFromState = cvtStateToHashedRoutes(state)
+        routeFromState = routeToStateTransformSvc.cvtStateToRoute(currentState)
       } catch (_e) {
         routeFromState = ``
       }
@@ -119,27 +166,41 @@ export class RouterService {
         if (!customStatePath) continue
         routeFromState += `/${customStatePath}`
       }
-
       if ( fullPath !== `/${routeFromState}`) {
         store$.dispatch(
-          generalApplyState({
+          generalActions.generalApplyState({
             state: stateFromRoute
           })
         )
       }
     })
     
-    // TODO this may still be a bit finiky. 
-    // we rely on that update of store happens within 160ms
-    // which may or many not be 
-    ready$.pipe(
+    /**
+     * wait until onload completes
+     * wait for 160ms
+     * then start listening to store changes, and update route accordingly
+     * 
+     * this is so that initial state can be loaded
+     */
+    concat(
+      onload$.pipe(
+        mapTo(false)
+      ),
+      timer(160).pipe(
+        mapTo(false)
+      ),
+      ready$.pipe(
+        map(val => !!val)
+      )
+    ).pipe(
+      filter(flag => flag),
       switchMapTo(
         combineLatest([
           store$.pipe(
             debounceTime(160),
             map(state => {
               try {
-                return cvtStateToHashedRoutes(state)
+                return routeToStateTransformSvc.cvtStateToRoute(state)
               } catch (e) {
                 this.logError(e)
                 return ``
@@ -160,7 +221,12 @@ export class RouterService {
         )
       )
     ).subscribe(routePath => {
-      if (routePath === '') {
+      /**
+       * routePath may be falsy
+       * or empty string
+       * both can be caught by !routePath
+       */
+      if (!routePath) {
         router.navigate([ baseHref ])
       } else {
 
diff --git a/src/routerModule/type.ts b/src/routerModule/type.ts
index fa8855ad8202348ddbec659db149c84296e49a7c..13205ad3e09e0969a8c419a5d9803b2883bc2de3 100644
--- a/src/routerModule/type.ts
+++ b/src/routerModule/type.ts
@@ -9,10 +9,6 @@ export type TUrlAtlas<T> = {
   r?: T  // region selected
 }
 
-export type TUrlPreviewDs<T> = {
-  dsp: T // dataset preview
-}
-
 export type TUrlPlugin<T> = {
   pl: T  // pluginState
 }
@@ -21,10 +17,14 @@ export type TUrlNav<T> = {
   ['@']: T // navstring
 }
 
+export type TUrlViewFeat<T> = {
+  f: T
+}
+
 export type TConditional<T> = Partial<
-  TUrlPreviewDs<T> &
   TUrlPlugin<T> &
-  TUrlNav<T>
+  TUrlNav<T> & 
+  TUrlViewFeat<T>
 >
 
 export type TUrlPathObj<T, V> = 
diff --git a/src/routerModule/util.spec.ts b/src/routerModule/util.spec.ts
index 35e0baefc23aa62bd6531b3ea32a2659744c63e7..95971bb00bf7f5d69249f9392d35def82d80d484 100644
--- a/src/routerModule/util.spec.ts
+++ b/src/routerModule/util.spec.ts
@@ -1,163 +1,6 @@
-import { TestBed } from '@angular/core/testing'
-import { MockStore, provideMockStore } from '@ngrx/store/testing'
-import { uiStatePreviewingDatasetFilesSelector } from 'src/services/state/uiState/selectors'
-import { viewerStateGetSelectedAtlas, viewerStateSelectedParcellationSelector, viewerStateSelectedRegionsSelector, viewerStateSelectedTemplateSelector, viewerStateSelectorNavigation, viewerStateSelectorStandaloneVolumes } from 'src/services/state/viewerState/selectors'
-import { cvtFullRouteToState, cvtStateToHashedRoutes, DummyCmp, encodeCustomState, routes, verifyCustomState } from './util'
-import { encodeNumber } from './cipher'
-import { Router } from '@angular/router'
-import { RouterTestingModule } from '@angular/router/testing'
-import * as parsedRoute from './parseRouteToTmplParcReg'
-import { spaceMiscInfoMap } from 'src/util/pureConstant.service'
+import { encodeCustomState, verifyCustomState } from './util'
 
 describe('> util.ts', () => {
-  describe('> cvtFullRouteToState', () => {
-
-    beforeEach(() => {
-      TestBed.configureTestingModule({
-        imports: [
-          RouterTestingModule.withRoutes(routes, {
-            useHash: true
-          })
-        ],
-        declarations: [
-          DummyCmp
-        ]
-      })
-    })
-    beforeEach(() => {
-    })
-    it('> should be able to decode region properly', () => {
-
-    })
-
-    describe('> decode sv', () => {
-      let sv: any
-      beforeEach(() => {
-        const searchParam = new URLSearchParams()
-        searchParam.set('standaloneVolumes', '["precomputed://https://object.cscs.ch/v1/AUTH_08c08f9f119744cbbf77e216988da3eb/imgsvc-46d9d64f-bdac-418e-a41b-b7f805068c64"]')
-        const svRoute = `/?${searchParam.toString()}`
-        const router = TestBed.inject(Router)
-        const parsedUrl = router.parseUrl(svRoute)
-        const returnState = cvtFullRouteToState(parsedUrl, {})
-        sv = returnState?.viewerState?.standaloneVolumes
-      })
-
-      it('> sv should be truthy', () => {
-        expect(sv).toBeTruthy()
-      })
-
-      it('> sv should be array', () => {
-        expect(
-          Array.isArray(sv)
-        ).toBeTrue()
-      })
-
-      it('> sv should have length 1', () => {
-        expect(sv.length).toEqual(1)
-      })
-
-      it('> sv[0] should be expected value', () => {
-        expect(sv[0]).toEqual(
-          'precomputed://https://object.cscs.ch/v1/AUTH_08c08f9f119744cbbf77e216988da3eb/imgsvc-46d9d64f-bdac-418e-a41b-b7f805068c64'
-        )
-      })
-    })
-
-    describe('> navigation', () => {
-      let parseSpy: jasmine.Spy
-      let mapGetSpy: jasmine.Spy
-      beforeEach(() => {
-        parseSpy = spyOnProperty(parsedRoute, 'parseSearchParamForTemplateParcellationRegion')
-        mapGetSpy = spyOn(spaceMiscInfoMap, 'get')
-      })
-      it('> if not present, should show something palatable', () => {
-        parseSpy.and.returnValue(() => ({
-          parcellationSelected: {
-            id: 'dummpy-id-parc'
-          },
-          regionsSelected: [],
-          templateSelected: {
-            id: 'dummpy-id-tmpl-sel'
-          },
-        }))
-
-        const scale = 0.25
-
-        mapGetSpy.and.returnValue({ scale })
-
-        const router = TestBed.inject(Router)
-        const route = `/a:juelich:iav:atlas:v1.0.0:1/t:minds:core:referencespace:v1.0.0:dafcffc5-4826-4bf1-8ff6-46b8a31ff8e2/p:minds:core:parcellationatlas:v1.0.0:94c1125b-b87e-45e4-901c-00daee7f2579-290`
-        const parsedUrl = router.parseUrl(route)
-        const { viewerState = {} } = cvtFullRouteToState(parsedUrl, {}) || {}
-        const { navigation } = viewerState
-        const {
-          orientation,
-          perspectiveOrientation,
-          position,
-          zoom,
-          perspectiveZoom,
-        } = navigation
-
-        expect(orientation).toEqual([0,0,0,1])
-        expect(perspectiveOrientation).toEqual([
-          0.3140767216682434,
-          -0.7418519854545593,
-          0.4988985061645508,
-          -0.3195493221282959
-        ])
-        expect(position).toEqual([0,0,0])
-        expect(zoom).toEqual(350000 * scale)
-        expect(perspectiveZoom).toEqual(1922235.5293810747 * scale)
-      })
-    })
-  })
-
-  describe('> cvtStateToHashedRoutes', () => {
-    let mockStore: MockStore
-    beforeEach(() => {
-      TestBed.configureTestingModule({
-        providers: [
-          provideMockStore()
-        ]
-      })
-
-      mockStore = TestBed.inject(MockStore)
-      
-      mockStore.overrideSelector(viewerStateGetSelectedAtlas, null)
-      mockStore.overrideSelector(viewerStateSelectedTemplateSelector, null)
-      mockStore.overrideSelector(viewerStateSelectedParcellationSelector, null)
-      mockStore.overrideSelector(viewerStateSelectedRegionsSelector, [])
-      mockStore.overrideSelector(viewerStateSelectorStandaloneVolumes, [])
-      mockStore.overrideSelector(viewerStateSelectorNavigation, null)
-
-      mockStore.overrideSelector(uiStatePreviewingDatasetFilesSelector, [])
-    })
-    describe('> should be able encode region properly', () => {
-
-      it('> regular ngId', () => {
-        const ngId = 'foobar'
-        const labelIndex = 124
-        mockStore.overrideSelector(viewerStateSelectedRegionsSelector, [{
-          labelIndex,
-          ngId
-        }])
-        const s = cvtStateToHashedRoutes({})
-        expect(s).toContain(`r:${ngId}::${encodeNumber(labelIndex, { float: false })}`)
-      })
-
-      it('> ngId containing ()', () => {
-
-        const ngId = 'foobar(1)'
-        const labelIndex = 124
-        mockStore.overrideSelector(viewerStateSelectedRegionsSelector, [{
-          labelIndex,
-          ngId
-        }])
-        const s = cvtStateToHashedRoutes({})
-        expect(s).toContain(`r:foobar%25281%2529::${encodeNumber(labelIndex, { float: false })}`)
-      })
-    })
-  })
 
   describe('> verifyCustomState', () => {
     it('> should return false on bad custom state', () => {
diff --git a/src/routerModule/util.ts b/src/routerModule/util.ts
index 39ec477aebc60ba1bd201971923bc5c3a6f539c5..ee8f181f0431509a7b6512a762df034ec56982ab 100644
--- a/src/routerModule/util.ts
+++ b/src/routerModule/util.ts
@@ -1,32 +1,17 @@
-import { viewerStateGetSelectedAtlas, viewerStateSelectedParcellationSelector, viewerStateSelectedRegionsSelector, viewerStateSelectedTemplateSelector, viewerStateSelectorNavigation, viewerStateSelectorStandaloneVolumes } from "src/services/state/viewerState/selectors"
-import { encodeNumber, decodeToNumber, separator, encodeURIFull } from './cipher'
 import { UrlSegment, UrlTree } from "@angular/router"
-import { getShader, PMAP_DEFAULT_CONFIG } from "src/util/constants"
-import { mixNgLayers } from "src/services/state/ngViewerState.store"
-import { PLUGINSTORE_CONSTANTS } from 'src/services/state/pluginState.helper'
-import { viewerStateHelperStoreName } from "src/services/state/viewerState.store.helper"
-import { uiStatePreviewingDatasetFilesSelector } from "src/services/state/uiState/selectors"
 import { Component } from "@angular/core"
 
-import {
-  TUrlStandaloneVolume,
-  TUrlAtlas,
-  TUrlPathObj,
-} from './type'
+export const encodeId = (id: string) => id && id.replace(/\//g, ':')
+export const decodeId = (codedId: string) => codedId && codedId.replace(/:/g, '/')
 
-import {
-  parseSearchParamForTemplateParcellationRegion,
-  encodeId,
-} from './parseRouteToTmplParcReg'
-import { spaceMiscInfoMap } from "src/util/pureConstant.service"
-
-const endcodePath = (key: string, val: string|string[]) =>
+export const endcodePath = (key: string, val: string|string[]) =>
   key[0] === '?'
     ? `?${key}=${val}`
     : `${key}:${Array.isArray(val)
       ? val.map(v => encodeURI(v)).join('::')
       : encodeURI(val)}`
-const decodePath = (path: string) => {
+
+export const decodePath = (path: string) => {
   const re = /^(.*?):(.*?)$/.exec(path)
   if (!re) return null
   return {
@@ -46,266 +31,6 @@ export const DEFAULT_NAV = {
   position: [0, 0, 0],
 }
 
-export const cvtFullRouteToState = (fullPath: UrlTree, state: any, _warnCb?: (arg: string) => void) => {
-
-  const warnCb = _warnCb || ((...e: any[]) => console.warn(...e))
-  const pathFragments: UrlSegment[] = fullPath.root.hasChildren()
-    ? fullPath.root.children['primary'].segments
-    : []
-
-  const returnState = JSON.parse(JSON.stringify(state))
-
-  const returnObj: Partial<TUrlPathObj<string[], unknown>> = {}
-  for (const f of pathFragments) {
-    const { key, val } = decodePath(f.path) || {}
-    if (!key || !val) continue
-    returnObj[key] = val
-  }
-
-  // TODO deprecate
-  // but ensure bkwd compat?
-  const niftiLayers = fullPath.queryParams['niftiLayers']
-  if (niftiLayers) {
-    const layers = niftiLayers
-      .split('__')
-      .map(layer => {
-        return {
-          name : layer,
-          source : `nifti://${layer}`,
-          mixability : 'nonmixable',
-          shader : getShader(PMAP_DEFAULT_CONFIG),
-        } as any
-      })
-    const { ngViewerState } = returnState
-    ngViewerState.layers = mixNgLayers(ngViewerState.layers, layers)
-  }
-  // -- end deprecate
-
-  // logical assignment. Use instead of above after typescript > v4.0.0
-  // returnState['viewerState'] ||= {}
-  if (!returnState['viewerState']) {
-    returnState['viewerState'] = {}
-  }
-  // -- end fix logical assignment
-
-  // nav obj is almost always defined, regardless if standaloneVolume or not
-  const cViewerState = returnObj['@'] && returnObj['@'][0]
-  let parsedNavObj: any
-  if (cViewerState) {
-    try {
-      const [ cO, cPO, cPZ, cP, cZ ] = cViewerState.split(`${separator}${separator}`)
-      const o = cO.split(separator).map(s => decodeToNumber(s, {float: true}))
-      const po = cPO.split(separator).map(s => decodeToNumber(s, {float: true}))
-      const pz = decodeToNumber(cPZ)
-      const p = cP.split(separator).map(s => decodeToNumber(s))
-      const z = decodeToNumber(cZ)
-      parsedNavObj = {
-        orientation: o,
-        perspectiveOrientation: po,
-        perspectiveZoom: pz,
-        position: p,
-        zoom: z,
-
-        // flag to allow for animation when enabled
-        animation: {},
-      }
-    } catch (e) {
-      /**
-       * TODO Poisoned encoded char
-       * send error message
-       */
-      warnCb(`parse nav error`, e)
-    }
-  }
-
-  // pluginState should always be defined, regardless if standalone volume or not
-  const pluginStates = fullPath.queryParams['pl']
-  const { pluginState } = returnState
-  if (pluginStates) {
-    try {
-      const arrPluginStates = JSON.parse(pluginStates)
-      pluginState.initManifests = arrPluginStates.map(url => [PLUGINSTORE_CONSTANTS.INIT_MANIFEST_SRC, url] as [string, string])
-    } catch (e) {
-      /**
-       * parsing plugin error
-       */
-      warnCb(`parse plugin states error`, e, pluginStates)
-    }
-  }
-
-  // preview dataset can and should be displayed regardless of standalone volume or not
-
-  try {
-    const { uiState } = returnState
-    const arr = returnObj.dsp
-      ? [{
-        datasetId: returnObj.dsp[0],
-        filename: returnObj.dsp[1]
-      }]
-      : fullPath.queryParams['previewingDatasetFiles'] && JSON.parse(fullPath.queryParams['previewingDatasetFiles'])
-    if (arr) {
-      uiState.previewingDatasetFiles = arr.map(({ datasetId, filename }) => {
-        return {
-          datasetId,
-          filename
-        }
-      })
-    }
-  } catch (e) {
-    // parsing previewingDatasetFiles
-    warnCb(`parse dsp error`, e)
-  }
-
-  // If sv (standaloneVolume is defined)
-  // only load sv in state
-  // ignore all other params
-  // /#/sv:%5B%22precomputed%3A%2F%2Fhttps%3A%2F%2Fobject.cscs.ch%2Fv1%2FAUTH_08c08f9f119744cbbf77e216988da3eb%2Fimgsvc-46d9d64f-bdac-418e-a41b-b7f805068c64%22%5D
-  const standaloneVolumes = fullPath.queryParams['standaloneVolumes']
-  if (!!standaloneVolumes) {
-    try {
-      const parsedArr = JSON.parse(standaloneVolumes)
-      if (!Array.isArray(parsedArr)) throw new Error(`Parsed standalone volumes not of type array`)
-
-      returnState['viewerState']['standaloneVolumes'] = parsedArr
-      returnState['viewerState']['navigation'] = parsedNavObj
-      return returnState
-    } catch (e) {
-      // if any error occurs, parse rest per normal
-      warnCb(`parse standalone volume error`, e)
-    }
-  } else {
-    returnState['viewerState']['standaloneVolumes'] = []
-  }
-
-  try {
-    const { parcellationSelected, regionsSelected, templateSelected } = parseSearchParamForTemplateParcellationRegion(returnObj as TUrlPathObj<string[], TUrlAtlas<string[]>>, fullPath, state, warnCb)
-    returnState['viewerState']['parcellationSelected'] = parcellationSelected
-    returnState['viewerState']['regionsSelected'] = regionsSelected
-    returnState['viewerState']['templateSelected'] = templateSelected
-
-    if (templateSelected) {
-      const { scale } = spaceMiscInfoMap.get(templateSelected.id) || { scale: 1 }
-      returnState['viewerState']['navigation'] = parsedNavObj || ({
-        ...DEFAULT_NAV,
-        zoom: 350000 * scale,
-        perspectiveZoom: 1922235.5293810747 * scale
-      })
-    }
-  } catch (e) {
-    // if error, show error on UI?
-    warnCb(`parse template, parc, region error`, e)
-  }
-
-  /**
-   * parsing template to get atlasId
-   */
-  (() => {
-
-    const viewreHelperState = returnState[viewerStateHelperStoreName] || {}
-    const { templateSelected, parcellationSelected } = returnState['viewerState']
-    const { fetchedAtlases, ...rest } = viewreHelperState
-    
-    const selectedAtlas = (fetchedAtlases || []).find(a => a['templateSpaces'].find(t => t['@id'] === (templateSelected && templateSelected['@id'])))
-    
-    const overlayLayer = selectedAtlas && selectedAtlas['parcellations'].find(p => p['@id'] === (parcellationSelected && parcellationSelected['@id']))
-
-    viewreHelperState['selectedAtlasId'] = selectedAtlas && selectedAtlas['@id']
-    viewreHelperState['overlayingAdditionalParcellations'] = (overlayLayer && !overlayLayer['baseLayer'])
-      ? [ overlayLayer ]
-      : []
-  })()
-
-  return returnState
-}
-
-export const cvtStateToHashedRoutes = (state): string => {
-  // TODO check if this causes memleak
-  const selectedAtlas = viewerStateGetSelectedAtlas(state)
-  const selectedTemplate = viewerStateSelectedTemplateSelector(state)
-  const selectedParcellation = viewerStateSelectedParcellationSelector(state)
-  const selectedRegions = viewerStateSelectedRegionsSelector(state)
-  const standaloneVolumes = viewerStateSelectorStandaloneVolumes(state)
-  const navigation = viewerStateSelectorNavigation(state)
-
-  const previewingDatasetFiles = uiStatePreviewingDatasetFilesSelector(state)
-  let dsPrvString: string
-  const searchParam = new URLSearchParams()
-
-  if (previewingDatasetFiles && Array.isArray(previewingDatasetFiles)) {
-    const dsPrvArr = []
-    const datasetPreviews = (previewingDatasetFiles as {datasetId: string, filename: string}[])
-    for (const preview of datasetPreviews) {
-      dsPrvArr.push(preview)
-    }
-
-    if (dsPrvArr.length === 1) {
-      dsPrvString = `${dsPrvArr[0].datasetId}::${dsPrvArr[0].filename}`
-    }
-  }
-
-  let cNavString: string
-  if (navigation) {
-    const { orientation, perspectiveOrientation, perspectiveZoom, position, zoom } = navigation
-    if (orientation && perspectiveOrientation && perspectiveZoom && position && zoom) {
-      cNavString = [
-        orientation.map((n: number) => encodeNumber(n, {float: true})).join(separator),
-        perspectiveOrientation.map(n => encodeNumber(n, {float: true})).join(separator),
-        encodeNumber(Math.floor(perspectiveZoom)),
-        Array.from(position).map((v: number) => Math.floor(v)).map(n => encodeNumber(n)).join(separator),
-        encodeNumber(Math.floor(zoom)),
-      ].join(`${separator}${separator}`)
-    }
-  }
-
-  // encoding selected regions
-  let selectedRegionsString
-  if (selectedRegions.length === 1) {
-    const region = selectedRegions[0]
-    const { ngId, labelIndex } = region
-    selectedRegionsString = `${ngId}::${encodeNumber(labelIndex, { float: false })}`
-  }
-  let routes: any
-  
-  routes= {
-    // for atlas
-    a: selectedAtlas && encodeId(selectedAtlas['@id']),
-    // for template
-    t: selectedTemplate && encodeId(selectedTemplate['@id'] || selectedTemplate['fullId']),
-    // for parcellation
-    p: selectedParcellation && encodeId(selectedParcellation['@id'] || selectedParcellation['fullId']),
-    // for regions
-    r: selectedRegionsString && encodeURIFull(selectedRegionsString),
-    // nav
-    ['@']: cNavString,
-    // dataset file preview
-    dsp: dsPrvString && encodeURI(dsPrvString),
-  } as TUrlPathObj<string, TUrlAtlas<string>>
-
-  /**
-   * if any params needs to overwrite previosu routes, put them here
-   */
-  if (standaloneVolumes && Array.isArray(standaloneVolumes) && standaloneVolumes.length > 0) {
-    searchParam.set('standaloneVolumes', JSON.stringify(standaloneVolumes))
-    routes = {
-      // nav
-      ['@']: cNavString,
-      dsp: dsPrvString && encodeURI(dsPrvString)
-    } as TUrlPathObj<string|string[], TUrlStandaloneVolume<string[]>>
-  }
-
-  const routesArr: string[] = []
-  for (const key in routes) {
-    if (!!routes[key]) {
-      const segStr = endcodePath(key, routes[key])
-      routesArr.push(segStr)
-    }
-  }
-
-  return searchParam.toString() === '' 
-    ? routesArr.join('/')
-    : `${routesArr.join('/')}?${searchParam.toString()}`
-}
-
 export const verifyCustomState = (key: string) => {
   return /^x-/.test(key)
 }
diff --git a/src/screenshot/screenshotCmp/screenshot.template.html b/src/screenshot/screenshotCmp/screenshot.template.html
index ad7143b67c6c49b75f8341450b70633e997149eb..ee570082170d036ee4429f425ce4f0d112255d79 100644
--- a/src/screenshot/screenshotCmp/screenshot.template.html
+++ b/src/screenshot/screenshotCmp/screenshot.template.html
@@ -2,7 +2,7 @@
 <ng-template #placeholderTmpl>
 
   <div class="d-flex align-items-center justify-content-center w-100 h-100 cover">
-    <span class="iv-custom-comp text">
+    <span class="sxplr-custom-cmp text">
       <h2 class="mat-h2 text-center">
         <span>
           Drag a box to take a screenshot or
diff --git a/src/services/effect/effect.spec.ts b/src/services/effect/effect.spec.ts
deleted file mode 100644
index 906e23daf3f5d76b601306fa10fff91e6c912611..0000000000000000000000000000000000000000
--- a/src/services/effect/effect.spec.ts
+++ /dev/null
@@ -1,48 +0,0 @@
-import {} from 'jasmine'
-import { UseEffects } from './effect'
-import { TestBed } from '@angular/core/testing'
-import { Observable } from 'rxjs'
-import { SELECT_PARCELLATION, SELECT_REGIONS } from '../state/viewerState.store'
-import { provideMockActions } from '@ngrx/effects/testing'
-import { hot } from 'jasmine-marbles'
-import { provideMockStore } from '@ngrx/store/testing'
-import { defaultRootState } from '../stateStore.service'
-import { viewerStateNewViewer } from '../state/viewerState/actions'
-
-describe('effect.ts', () => {
-
-  describe('UseEffects', () => {
-    let actions$:Observable<any>
-
-    beforeEach(() => {  
-      TestBed.configureTestingModule({
-        providers: [
-          UseEffects,
-          provideMockActions(() => actions$),
-          provideMockStore({ initialState: defaultRootState })
-        ]
-      })
-    })
-
-    it('both SELECT_PARCELLATION and viewerStateNewViewer.type actions should trigger onParcellationSelected$', () => {
-      const useEffectsInstance: UseEffects = TestBed.inject(UseEffects)
-      actions$ = hot(
-        'ab',
-        {
-          a: { type: SELECT_PARCELLATION },
-          b: { type: viewerStateNewViewer.type }
-        }
-      )
-      expect(
-        useEffectsInstance.onParcChange$
-      ).toBeObservable(
-        hot(
-          'aa',
-          {
-            a: { type: SELECT_REGIONS, selectRegions: [] }
-          }  
-        )
-      )
-    })
-  })
-})
diff --git a/src/services/effect/effect.ts b/src/services/effect/effect.ts
deleted file mode 100644
index 10e5506143f0963ddaea23b81bd3e09509c8623b..0000000000000000000000000000000000000000
--- a/src/services/effect/effect.ts
+++ /dev/null
@@ -1,243 +0,0 @@
-import { Injectable, OnDestroy } from "@angular/core";
-import { Actions, Effect, ofType } from "@ngrx/effects";
-import { select, Store } from "@ngrx/store";
-import { merge, Observable, Subscription, combineLatest } from "rxjs";
-import { filter, map, shareReplay, switchMap, take, withLatestFrom, mapTo, distinctUntilChanged } from "rxjs/operators";
-import { LoggingService } from "src/logging";
-import { IavRootStoreInterface, recursiveFindRegionWithLabelIndexId } from '../stateStore.service';
-import { viewerStateNewViewer, viewerStateSelectAtlas, viewerStateSetSelectedRegionsWithIds, viewerStateToggleLayer } from "../state/viewerState.store.helper";
-import { deserialiseParcRegionId, serialiseParcellationRegion } from "common/util"
-import { getGetRegionFromLabelIndexId } from 'src/util/fn'
-import { actionAddToRegionsSelectionWithIds, actionSelectLandmarks, viewerStateSelectParcellation, viewerStateSelectRegionWithIdDeprecated, viewerStateSetSelectedRegions } from "../state/viewerState/actions";
-
-@Injectable({
-  providedIn: 'root',
-})
-export class UseEffects implements OnDestroy {
-
-  @Effect()
-  setRegionsSelected$: Observable<any> 
-
-  constructor(
-    private actions$: Actions,
-    private store$: Store<IavRootStoreInterface>,
-    private log: LoggingService,
-  ) {
-
-    this.regionsSelected$ = this.store$.pipe(
-      select('viewerState'),
-      select('regionsSelected'),
-      shareReplay(1),
-    )
-
-    this.setRegionsSelected$ = combineLatest(
-      this.actions$.pipe(
-        ofType(viewerStateSetSelectedRegionsWithIds),
-        map(action => {
-          const { selectRegionIds } = action
-          return selectRegionIds
-        })
-      ),
-      this.store$.pipe(
-        select('viewerState'),
-        select('parcellationSelected'),
-        filter(v => !!v),
-        distinctUntilChanged()
-      ),
-    ).pipe(
-      map(([ids, parcellation]) => {
-        const getRegionFromlabelIndexId = getGetRegionFromLabelIndexId({ parcellation })
-        const selectRegions = !!ids && Array.isArray(ids)
-          ? ids.map(id => getRegionFromlabelIndexId({ labelIndexId: id })).filter(v => !!v)
-          : []
-          /**
-           * only allow 1 selection at a time
-           */
-        return viewerStateSetSelectedRegions({
-          selectRegions: selectRegions.slice(0,1)
-        })
-      })
-    )
-
-    this.onDeselectRegionsWithId$ = this.actions$.pipe(
-      ofType(ACTION_TYPES.DESELECT_REGIONS_WITH_ID),
-      map(action => {
-        const { deselecRegionIds } = action as any
-        return deselecRegionIds
-      }),
-      withLatestFrom(this.regionsSelected$),
-      map(([ deselecRegionIds, alreadySelectedRegions ]) => {
-        const deselectSet = new Set(deselecRegionIds)
-        return viewerStateSetSelectedRegions({
-          selectRegions: alreadySelectedRegions
-            .filter(({ ngId, labelIndex }) => !deselectSet.has(serialiseParcellationRegion({ ngId, labelIndex }))),
-        })
-      }),
-    )
-
-    this.addToSelectedRegions$ = this.actions$.pipe(
-      ofType(actionAddToRegionsSelectionWithIds.type),
-      map(action => {
-        const { selectRegionIds } = action
-        return selectRegionIds
-      }),
-      switchMap(selectRegionIds => this.updatedParcellation$.pipe(
-        filter(p => !!p),
-        take(1),
-        map(p => [selectRegionIds, p]),
-      )),
-      map(this.convertRegionIdsToRegion),
-      withLatestFrom(this.regionsSelected$),
-      map(([ selectedRegions, alreadySelectedRegions ]) => {
-        return viewerStateSetSelectedRegions({
-          selectRegions: this.removeDuplicatedRegions(selectedRegions, alreadySelectedRegions),
-        })
-      }),
-    )
-  }
-
-  private regionsSelected$: Observable<any[]>
-
-  public ngOnDestroy() {
-    while (this.subscriptions.length > 0) {
-      this.subscriptions.pop().unsubscribe()
-    }
-  }
-
-  private subscriptions: Subscription[] = []
-
-  private parcellationSelected$ = this.actions$.pipe(
-    ofType(viewerStateSelectParcellation.type),
-  )
-
-
-  private updatedParcellation$ = this.store$.pipe(
-    select('viewerState'),
-    select('parcellationSelected'),
-    map(p => p.updated ? p : null),
-    shareReplay(1),
-  )
-
-  @Effect()
-  public onDeselectRegionsWithId$: Observable<any>
-
-  private convertRegionIdsToRegion = ([selectRegionIds, parcellation]) => {
-    const { ngId: defaultNgId } = parcellation
-    return (selectRegionIds as any[])
-      .map(labelIndexId => deserialiseParcRegionId(labelIndexId))
-      .map(({ ngId, labelIndex }) => {
-        return {
-          labelIndexId: serialiseParcellationRegion({
-            ngId: ngId || defaultNgId,
-            labelIndex,
-          }),
-        }
-      })
-      .map(({ labelIndexId }) => {
-        return recursiveFindRegionWithLabelIndexId({
-          regions: parcellation.regions,
-          labelIndexId,
-          inheritedNgId: defaultNgId,
-        })
-      })
-      .filter(v => {
-        if (!v) {
-          this.log.log(`SELECT_REGIONS_WITH_ID, some ids cannot be parsed intto label index`)
-        }
-        return !!v
-      })
-  }
-
-  private removeDuplicatedRegions = (...args) => {
-    const set = new Set()
-    const returnArr = []
-    for (const regions of args) {
-      for (const region of regions) {
-        if (!set.has(region.name)) {
-          returnArr.push(region)
-          set.add(region.name)
-        }
-      }
-    }
-    return returnArr
-  }
-
-  @Effect()
-  public addToSelectedRegions$: Observable<any>
-
-  /**
-   * for backwards compatibility.
-   * older versions of atlas viewer may only have labelIndex as region identifier
-   */
-  @Effect()
-  public onSelectRegionWithId = this.actions$.pipe(
-    ofType(viewerStateSelectRegionWithIdDeprecated.type),
-    map(action => {
-      const { selectRegionIds } = action
-      return selectRegionIds
-    }),
-    switchMap(selectRegionIds => this.updatedParcellation$.pipe(
-      filter(p => !!p),
-      take(1),
-      map(parcellation => [selectRegionIds, parcellation]),
-    )),
-    map(this.convertRegionIdsToRegion),
-    map(selectRegions => {
-      return viewerStateSetSelectedRegions({
-        selectRegions
-      })
-    }),
-  )
-
-  /**
-   * side effect of selecting a parcellation means deselecting all regions
-   */
-  @Effect()
-  public onParcChange$ = merge(
-    this.actions$.pipe(
-      ofType(viewerStateToggleLayer.type)
-    ),
-    this.parcellationSelected$,
-    this.actions$.pipe(
-      ofType(viewerStateNewViewer.type)
-    ),
-    this.actions$.pipe(
-      ofType(viewerStateSelectAtlas.type)
-    )
-  ).pipe(
-    mapTo(
-      viewerStateSetSelectedRegions({
-        selectRegions: []
-      })
-    )
-  )
-
-  /**
-   * side effects of loading a new template space
-   * Landmarks will no longer be accurate (differente template space)
-   */
-
-  @Effect()
-  public onNewViewerResetLandmarkSelected$ = this.actions$.pipe(
-    ofType(viewerStateNewViewer.type),
-    mapTo(
-      actionSelectLandmarks({
-        landmarks: []
-      })
-    )
-  )
-}
-
-export const compareRegions: (r1: any, r2: any) => boolean = (r1, r2) => {
-  if (!r1) { return !r2 }
-  if (!r2) { return !r1 }
-  return r1.ngId === r2.ngId
-    && r1.labelIndex === r2.labelIndex
-    && r1.name === r2.name
-}
-
-const ACTION_TYPES = {
-  DESELECT_REGIONS_WITH_ID: 'DESELECT_REGIONS_WITH_ID',
-}
-
-export const VIEWER_STATE_ACTION_TYPES = ACTION_TYPES
diff --git a/src/services/effect/newTemplate.effect.ts b/src/services/effect/newTemplate.effect.ts
deleted file mode 100644
index f2c2541fe6fee218be482ad7f67b0b84443a8928..0000000000000000000000000000000000000000
--- a/src/services/effect/newTemplate.effect.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import { Injectable } from "@angular/core";
-import { Actions, Effect, ofType } from "@ngrx/effects";
-import { Observable } from "rxjs";
-import { mapTo } from "rxjs/operators";
-import { DATASETS_ACTIONS_TYPES } from "../state/dataStore.store";
-import { viewerStateNewViewer } from "../state/viewerState/actions";
-
-@Injectable({
-  providedIn: 'root'
-})
-
-export class NewTemplateUseEffect{
-
-  @Effect()
-  public onNewTemplateShouldClearPreviewDataset$: Observable<any>
-
-  constructor(
-    private actions$: Actions
-  ){
-    this.onNewTemplateShouldClearPreviewDataset$ = this.actions$.pipe(
-      ofType(viewerStateNewViewer.type),
-      mapTo({
-        type: DATASETS_ACTIONS_TYPES.CLEAR_PREVIEW_DATASETS
-      })
-    )
-  }
-}
diff --git a/src/services/effect/pluginUseEffect.spec.ts b/src/services/effect/pluginUseEffect.spec.ts
deleted file mode 100644
index 978b28ad0485761025ad70b263e0440e26929326..0000000000000000000000000000000000000000
--- a/src/services/effect/pluginUseEffect.spec.ts
+++ /dev/null
@@ -1,128 +0,0 @@
-import { TestBed } from "@angular/core/testing";
-import { HttpClientModule, HTTP_INTERCEPTORS, HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpResponse, HttpHeaders } from "@angular/common/http";
-import { PluginServiceUseEffect } from "./pluginUseEffect";
-import { Observable, of } from "rxjs";
-import { Action } from "@ngrx/store";
-import { provideMockActions } from "@ngrx/effects/testing";
-import { provideMockStore } from "@ngrx/store/testing";
-import { defaultRootState } from "../stateStore.service";
-import { PLUGINSTORE_CONSTANTS, PLUGINSTORE_ACTION_TYPES } from '../state/pluginState.helper'
-import { Injectable } from "@angular/core";
-import { getRandomHex } from 'common/util'
-import { PluginServices } from "src/plugin";
-import { AngularMaterialModule } from "src/sharedModules";
-import { hot } from "jasmine-marbles";
-import { BS_ENDPOINT } from "src/util/constants";
-
-const actions$: Observable<Action> = of({type: 'TEST'})
-
-const manifest = {
-  name: getRandomHex(),
-  templateURL: 'http://localhost:12345/template.html',
-  scriptURL: 'http://localhost:12345/script.js'
-}
-
-const template = getRandomHex()
-const script = getRandomHex()
-
-@Injectable()
-class HTTPInterceptorClass implements HttpInterceptor{
-  intercept(req: HttpRequest<any>, next: HttpHandler):Observable<HttpEvent<any>>{
-    if(req.url.indexOf('http://localhost:12345') >= 0) {
-      if (req.url.indexOf('manifest.json') >= 0) {
-
-        const headers = new HttpHeaders()
-        headers.set('content-type', 'application/json')
-        return of(new HttpResponse({
-          status: 200,
-          body: manifest,
-          headers
-        }))
-      }
-
-      if (req.url.indexOf('template.html') >= 0) {
-
-        const headers = new HttpHeaders()
-        headers.set('content-type', 'text/html')
-        return of(new HttpResponse({
-          status: 200,
-          body: template,
-          headers
-        }))
-      }
-
-      if (req.url.indexOf('script.js') >= 0) {
-
-        const headers = new HttpHeaders()
-        headers.set('content-type', 'application/javascript')
-        return of(new HttpResponse({
-          status: 200,
-          body: script,
-          headers
-        }))
-      }
-    } 
-    return next.handle(req)
-  }
-}
-
-@Injectable()
-class MockPluginService{
-  public launchNewWidget(arg) {
-    console.log('launch new widget')
-  }
-}
-
-describe('pluginUseEffect.ts', () => {
-
-  let spy
-  beforeEach(() => {
-    TestBed.configureTestingModule({
-      imports: [
-        HttpClientModule,
-        AngularMaterialModule
-      ],
-      providers: [
-        PluginServiceUseEffect,
-        provideMockActions(() => actions$),
-        provideMockStore({
-          initialState: {
-            ...defaultRootState,
-            pluginState:{
-              initManifests: [
-                [ PLUGINSTORE_CONSTANTS.INIT_MANIFEST_SRC, 'http://localhost:12345/manifest.json' ]
-              ]
-            }
-          }
-        }),
-        {
-          provide: HTTP_INTERCEPTORS,
-          useClass: HTTPInterceptorClass,
-          multi: true
-        },
-        {
-          provide: PluginServices,
-          useClass: MockPluginService
-        },
-        {
-          provide: BS_ENDPOINT,
-          useValue: `http://localhost:1234`
-        }
-      ]
-    }).compileComponents()
-    const pluginServices = TestBed.get(PluginServices)
-    spy = spyOn(pluginServices, 'launchNewWidget')
-  })
-
-  it('initManifests should fetch manifest.json', () => {
-    const effect = TestBed.get(PluginServiceUseEffect) as PluginServiceUseEffect
-    expect(
-      effect.initManifests$
-    ).toBeObservable(
-      hot('a', {
-        a: {type: PLUGINSTORE_ACTION_TYPES.CLEAR_INIT_PLUGIN}
-      })
-    )
-    expect(spy).toHaveBeenCalledWith(manifest)
-  })
-})
diff --git a/src/services/effect/pluginUseEffect.ts b/src/services/effect/pluginUseEffect.ts
deleted file mode 100644
index bd0a74772c60d75cceda439cb5402df584c4e2af..0000000000000000000000000000000000000000
--- a/src/services/effect/pluginUseEffect.ts
+++ /dev/null
@@ -1,53 +0,0 @@
-import { Injectable } from "@angular/core"
-import { Effect } from "@ngrx/effects"
-import { select, Store } from "@ngrx/store"
-import { Observable, forkJoin } from "rxjs"
-import { filter, map, startWith, switchMap } from "rxjs/operators"
-import { PluginServices } from "src/plugin/atlasViewer.pluginService.service"
-import { PLUGINSTORE_CONSTANTS, PLUGINSTORE_ACTION_TYPES, pluginStateSelectorInitManifests } from 'src/services/state/pluginState.helper'
-import { HttpClient } from "@angular/common/http"
-import { getHttpHeader } from "src/util/constants"
-
-@Injectable({
-  providedIn: 'root',
-})
-
-export class PluginServiceUseEffect {
-
-  @Effect()
-  public initManifests$: Observable<any>
-
-  constructor(
-    store$: Store<any>,
-    pluginService: PluginServices,
-    http: HttpClient
-  ) {
-    this.initManifests$ = store$.pipe(
-      select(pluginStateSelectorInitManifests),
-      startWith([]),
-      map(arr => {
-        // only launch plugins that has init manifest src label on it
-        return arr.filter(([ source ]) => source === PLUGINSTORE_CONSTANTS.INIT_MANIFEST_SRC)
-      }),
-      filter(arr => arr.length > 0),
-      switchMap(arr => forkJoin(
-        arr.map(([_source, url]) => 
-          http.get(url, {
-            headers: getHttpHeader(),
-            responseType: 'json'
-          })
-        )
-      )),
-      map((jsons: any[]) => {
-        for (const json of jsons){
-          pluginService.launchNewWidget(json)
-        }
-
-        // clear init manifest
-        return {
-          type: PLUGINSTORE_ACTION_TYPES.CLEAR_INIT_PLUGIN,
-        }
-      }),
-    )
-  }
-}
diff --git a/src/services/localFile.service.ts b/src/services/localFile.service.ts
deleted file mode 100644
index 045ac686b54f7b65e2985f7d75c2bce85ce705c6..0000000000000000000000000000000000000000
--- a/src/services/localFile.service.ts
+++ /dev/null
@@ -1,100 +0,0 @@
-import { Injectable } from "@angular/core";
-import { Store } from "@ngrx/store";
-import { SNACKBAR_MESSAGE } from "./state/uiState.store";
-import { IavRootStoreInterface } from "./stateStore.service";
-import { DATASETS_ACTIONS_TYPES } from "./state/dataStore.store";
-
-/**
- * experimental service handling local user files such as nifti and gifti
- */
-
-@Injectable({
-  providedIn: 'root',
-})
-
-export class LocalFileService {
-  public SUPPORTED_EXT = SUPPORTED_EXT
-  private supportedExtSet = new Set(SUPPORTED_EXT)
-
-  constructor(
-    private store: Store<IavRootStoreInterface>,
-  ) {
-
-  }
-
-  private niiUrl: string
-
-  public handleFileDrop(files: File[]) {
-    try {
-      this.validateDrop(files)
-      for (const file of files) {
-        const ext = this.getExtension(file.name)
-        switch (ext) {
-        case NII: {
-          this.handleNiiFile(file)
-          break;
-        }
-        default:
-          throw new Error(`File ${file.name} does not have a file handler`)
-        }
-      }
-    } catch (e) {
-      this.store.dispatch({
-        type: SNACKBAR_MESSAGE,
-        snackbarMessage: `Opening local NIFTI error: ${e.toString()}`,
-      })
-    }
-  }
-
-  private getExtension(filename: string) {
-    const match = /(\.\w*?)$/i.exec(filename)
-    return (match && match[1]) || ''
-  }
-
-  private validateDrop(files: File[]) {
-    if (files.length !== 1) {
-      throw new Error('Interactive atlas viewer currently only supports drag and drop of one file at a time')
-    }
-    for (const file of files) {
-      const ext = this.getExtension(file.name)
-      if (!this.supportedExtSet.has(ext)) {
-        throw new Error(`File ${file.name}${ext === '' ? ' ' : (' with extension ' + ext)} cannot be loaded. The supported extensions are: ${this.SUPPORTED_EXT.join(', ')}`)
-      }
-    }
-  }
-
-  private handleNiiFile(file: File) {
-
-    if (this.niiUrl) {
-      URL.revokeObjectURL(this.niiUrl)
-    }
-    this.niiUrl = URL.createObjectURL(file)
-    
-    this.store.dispatch({
-      type: DATASETS_ACTIONS_TYPES.PREVIEW_DATASET,
-      payload: {
-        file: {
-          mimetype: 'application/json',
-          url: this.niiUrl
-        }
-      }
-    })
-
-    this.showLocalWarning()
-  }
-
-  private showLocalWarning() {
-    this.store.dispatch({
-      type: SNACKBAR_MESSAGE,
-      snackbarMessage: `Warning: sharing URL will not share the loaded local file`,
-    })
-  }
-}
-
-const NII = `.nii`
-const GII = '.gii'
-
-const SUPPORTED_EXT = [
-  NII,
-  GII,
-]
diff --git a/src/services/state/dataState/actions.ts b/src/services/state/dataState/actions.ts
deleted file mode 100644
index 34af72ff7364bb0c575eb3cbf21aae9a739a9890..0000000000000000000000000000000000000000
--- a/src/services/state/dataState/actions.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import { createAction, props } from "@ngrx/store";
-import { IKgDataEntry } from "src/databrowser.fallback";
-
-export const datastateActionToggleFav = createAction(
-  `[datastate] toggleFav`,
-  props<{payload: { fullId: string }}>()
-)
-
-export const datastateActionUpdateFavDataset = createAction(
-  `[datastate] updateFav`,
-  props<{ favDataEntries: any[] }>()
-)
-
-export const datastateActionUnfavDataset = createAction(
-  `[datastate] unFav`,
-  props<{ payload: { fullId: string } }>()
-)
-
-export const datastateActionFavDataset = createAction(
-  `[datastate] fav`,
-  props<{ payload: { fullId: string } }>()
-)
-
-export const datastateActionFetchedDataentries = createAction(
-  `[datastate] fetchedDatastate`,
-  props<{ fetchedDataEntries: IKgDataEntry[] }>()
-)
\ No newline at end of file
diff --git a/src/services/state/dataStore.store.ts b/src/services/state/dataStore.store.ts
deleted file mode 100644
index 962168dd111b4219e69c6f595f5ccf596df2fc95..0000000000000000000000000000000000000000
--- a/src/services/state/dataStore.store.ts
+++ /dev/null
@@ -1,222 +0,0 @@
-/**
- * TODO move to databrowser module
- */
-
-import { Action } from '@ngrx/store'
-import { LOCAL_STORAGE_CONST } from 'src/util/constants'
-import { datastateActionFetchedDataentries, datastateActionUpdateFavDataset } from './dataState/actions'
-import { IHasId } from 'src/util/interfaces'
-import { generalApplyState } from '../stateStore.helper'
-
-/**
- * TODO merge with databrowser.usereffect.ts
- */
-
-export interface DatasetPreview {
-  datasetId: string
-  filename: string
-}
-
-export interface IStateInterface {
-  fetchedDataEntries: IDataEntry[]
-  favDataEntries: Partial<IDataEntry>[]
-  fetchedSpatialData: IDataEntry[]
-}
-
-// TODO deprecate
-export const defaultState = {
-  fetchedDataEntries: [],
-  favDataEntries: (() => {
-    try {
-      const saved = localStorage.getItem(LOCAL_STORAGE_CONST.FAV_DATASET)
-      const arr = JSON.parse(saved) as any[]
-      return arr.every(item => item && !!item.fullId)
-        ? arr
-        : []
-    } catch (e) {
-      // TODO propagate error
-      return []
-    }
-  })(),
-  fetchedSpatialData: [],
-}
-
-export const getStateStore = ({ state: state = defaultState } = {}) => (prevState: IStateInterface = state, action: Partial<IActionInterface>) => {
-
-  switch (action.type) {
-  case datastateActionFetchedDataentries.type:
-  case FETCHED_DATAENTRIES: {
-    return {
-      ...prevState,
-      fetchedDataEntries : action.fetchedDataEntries,
-    }
-  }
-  case FETCHED_SPATIAL_DATA: {
-    return {
-      ...prevState,
-      fetchedSpatialData : action.fetchedDataEntries,
-    }
-  }
-  case datastateActionUpdateFavDataset.type: {
-    const { favDataEntries = [] } = action
-    return {
-      ...prevState,
-      favDataEntries,
-    }
-  }
-  case generalApplyState.type: {
-    const { dataStore } = (action as any).state
-    return dataStore
-  }
-  default: return prevState
-  }
-}
-
-// must export a named function for aot compilation
-// see https://github.com/angular/angular/issues/15587
-// https://github.com/amcdnl/ngrx-actions/issues/23
-// or just google for:
-//
-// angular function expressions are not supported in decorators
-
-const defaultStateStore = getStateStore()
-
-export function stateStore(state, action) {
-  return defaultStateStore(state, action)
-}
-
-export interface IActionInterface extends Action {
-  favDataEntries: IDataEntry[]
-  fetchedDataEntries: IDataEntry[]
-  fetchedSpatialData: IDataEntry[]
-  payload?: any
-}
-
-export const FETCHED_DATAENTRIES = 'FETCHED_DATAENTRIES'
-export const FETCHED_SPATIAL_DATA = `FETCHED_SPATIAL_DATA`
-
-// TODO deprecate in favour of src/ui/datamodule/constants.ts
-
-export interface IActivity {
-  methods: string[]
-  preparation: string[]
-  protocols: string[]
-}
-
-export interface IDataEntry {
-  activity: IActivity[]
-  name: string
-  description: string
-  license: string[]
-  licenseInfo: string[]
-  parcellationRegion: IParcellationRegion[]
-  formats: string[]
-  custodians: string[]
-  contributors: string[]
-  referenceSpaces: IReferenceSpace[]
-  files: File[]
-  publications: IPublication[]
-  embargoStatus: IHasId[]
-
-  methods: string[]
-  protocols: string[]
-
-  preview?: boolean
-
-  /**
-   * TODO typo, should be kgReferences
-   */
-  kgReference: string[]
-
-  id: string
-  fullId: string
-}
-
-export interface IParcellationRegion {
-  id?: string
-  name: string
-}
-
-export interface IReferenceSpace {
-  name: string
-}
-
-export interface IPublication {
-  name: string
-  doi: string
-  cite: string
-}
-
-export interface IProperty {
-  description: string
-  publications: IPublication[]
-}
-
-export interface ILandmark {
-  type: string // e.g. sEEG recording site, etc
-  name: string
-  templateSpace: string // possibily inherited from LandmarkBundle (?)
-  geometry: IPointLandmarkGeometry | IPlaneLandmarkGeometry | IOtherLandmarkGeometry
-  properties: IProperty
-  files: File[]
-}
-
-export interface IDataStateInterface {
-  fetchedDataEntries: IDataEntry[]
-
-  /**
-   * Map that maps parcellation name to a Map, which maps datasetname to Property Object
-   */
-  fetchedMetadataMap: Map<string, Map<string, {properties: IProperty}>>
-}
-
-export interface IPointLandmarkGeometry extends ILandmarkGeometry {
-  position: [number, number, number]
-}
-
-export interface IPlaneLandmarkGeometry extends ILandmarkGeometry {
-  // corners have to be CW or CCW (no zigzag)
-  corners: [[number, number, number], [number, number, number], [number, number, number], [number, number, number]]
-}
-
-export interface IOtherLandmarkGeometry extends ILandmarkGeometry {
-  vertices: Array<[number, number, number]>
-  meshIdx: Array<[number, number, number]>
-}
-
-interface ILandmarkGeometry {
-  type: 'point' | 'plane'
-  space?: 'voxel' | 'real'
-}
-
-export interface IFile {
-  name: string
-  absolutePath: string
-  byteSize: number
-  contentType: string
-}
-
-export interface ViewerPreviewFile {
-  name: string
-  filename: string
-  mimetype: string
-  referenceSpaces: { 
-    name: string 
-    fullId: string
-  }[]
-  url?: string
-  data?: any
-  position?: any
-}
-
-export interface IFileSupplementData {
-  data: any
-}
-
-const ACTION_TYPES = {
-  PREVIEW_DATASET: 'PREVIEW_DATASET',
-  CLEAR_PREVIEW_DATASET: 'CLEAR_PREVIEW_DATASET',
-  CLEAR_PREVIEW_DATASETS: 'CLEAR_PREVIEW_DATASETS'
-}
-
-export const DATASETS_ACTIONS_TYPES = ACTION_TYPES
diff --git a/src/services/state/ngViewerState.store.helper.ts b/src/services/state/ngViewerState.store.helper.ts
deleted file mode 100644
index 6f23c74d1c306a3b74e824fb69c7adc0743d0cc8..0000000000000000000000000000000000000000
--- a/src/services/state/ngViewerState.store.helper.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-// TODO to be merged with ng viewer state after refactor
-export { INgLayerInterface, PANELS } from './ngViewerState/constants'
-
-export {
-  ngViewerActionAddNgLayer,
-  ngViewerActionRemoveNgLayer,
-  ngViewerActionSetPerspOctantRemoval,
-  ngViewerActionToggleMax,
-  ngViewerActionClearView,
-  ngViewerActionSetPanelOrder,
-  ngViewerActionForceShowSegment,
-} from './ngViewerState/actions'
-
-export {
-  ngViewerSelectorClearView,
-  ngViewerSelectorClearViewEntries,
-  ngViewerSelectorNehubaReady,
-  ngViewerSelectorOctantRemoval,
-  ngViewerSelectorPanelMode,
-  ngViewerSelectorPanelOrder,
-  ngViewerSelectorLayers,
-} from './ngViewerState/selectors'
diff --git a/src/services/state/ngViewerState.store.spec.ts b/src/services/state/ngViewerState.store.spec.ts
deleted file mode 100644
index 21173833cf134a9e53274c3017abf97bdef947fb..0000000000000000000000000000000000000000
--- a/src/services/state/ngViewerState.store.spec.ts
+++ /dev/null
@@ -1,100 +0,0 @@
-import { HttpClientTestingModule, HttpTestingController } from "@angular/common/http/testing"
-import { TestBed } from "@angular/core/testing"
-import { provideMockActions } from "@ngrx/effects/testing"
-import { Action } from "@ngrx/store"
-import { provideMockStore } from "@ngrx/store/testing"
-import { Observable, of } from "rxjs"
-import { PureContantService } from "src/util"
-import { generalApplyState } from "../stateStore.helper"
-import { NgViewerUseEffect } from "./ngViewerState.store"
-
-const action$: Observable<Action> = of({ type: 'TEST'})
-const initState = {}
-describe('> ngViewerState.store.ts', () => {
-  describe('> NgViewerUseEffect', () => {
-    let ef: NgViewerUseEffect
-    beforeEach(() => {
-
-      TestBed.configureTestingModule({
-        imports: [
-          HttpClientTestingModule,
-        ],
-        providers: [
-          provideMockActions(() => action$),
-          provideMockStore({ initialState: initState }),
-          {
-            provide: PureContantService,
-            useValue: {
-              useTouchUI$: of(false),
-              backendUrl: `http://localhost:3000/`
-            }
-          }
-        ]
-      })
-    })
-
-    it('> shoudl be insable', () => {
-      ef = TestBed.inject(NgViewerUseEffect)
-      expect(ef).toBeTruthy()
-    })
-
-    describe('> applySavedUserConfig$', () => {
-
-      let ctrl: HttpTestingController
-      beforeEach(() => {
-        ctrl = TestBed.inject(HttpTestingController)
-        ef = TestBed.inject(NgViewerUseEffect)
-      })
-
-      afterEach(() => {
-        ctrl.verify()
-      })
-      
-      it('> if http response errors, user$ should be stream of null', () => {
-        
-        ef.applySavedUserConfig$.subscribe(_action => {
-          // should not emit
-          expect(false).toEqual(true)
-        })
-        const resp1 = ctrl.expectOne('http://localhost:3000/user/config')
-        resp1.error(null, {
-          status: 404,
-        })
-      })
-
-      it('> if http response contains truthy error key, user should return stream of null', () => {
-
-        ef.applySavedUserConfig$.subscribe(_action => {
-          // should not emit
-          expect(false).toEqual(true)
-        })
-        const resp = ctrl.expectOne('http://localhost:3000/user/config')
-        resp.flush({
-          error: true
-        })
-      })
-
-      it('> if http response does not contain error key, should return the resp', () => {
-        
-        const mUserState = {
-          foo: 'baz',
-          baz: 'pineablle'
-        }
-        ef.applySavedUserConfig$.subscribe(action => {
-          expect(action).toEqual(generalApplyState({
-            state: {
-              ...initState,
-              ngViewerState: {
-                ...(initState['ngViewerState'] || {}),
-                ...mUserState,
-              }
-            }
-          }))
-        })
-        const resp = ctrl.expectOne('http://localhost:3000/user/config')
-        resp.flush({ ngViewerState: mUserState})
-
-      })
-    })
-  })
-})
diff --git a/src/services/state/ngViewerState.store.ts b/src/services/state/ngViewerState.store.ts
deleted file mode 100644
index 5b1758d60906076840930cafbf878ab834e7e88d..0000000000000000000000000000000000000000
--- a/src/services/state/ngViewerState.store.ts
+++ /dev/null
@@ -1,407 +0,0 @@
-import { Injectable, OnDestroy } from '@angular/core';
-import { Observable, combineLatest, fromEvent, Subscription, of } from 'rxjs';
-import { Effect, Actions, ofType } from '@ngrx/effects';
-import { withLatestFrom, map, distinctUntilChanged, scan, shareReplay, filter, mapTo, debounceTime, catchError, skip, throttleTime } from 'rxjs/operators';
-import { getNgIds } from 'src/util/fn';
-import { Action, select, Store, createReducer, on } from '@ngrx/store'
-import { CYCLE_PANEL_MESSAGE } from 'src/util/constants';
-import { HttpClient } from '@angular/common/http';
-import { INgLayerInterface, ngViewerActionAddNgLayer, ngViewerActionRemoveNgLayer, ngViewerActionSetPerspOctantRemoval } from './ngViewerState.store.helper'
-import { PureContantService } from 'src/util';
-import { PANELS } from './ngViewerState.store.helper'
-import { ngViewerActionToggleMax, ngViewerActionClearView, ngViewerActionSetPanelOrder, ngViewerActionSwitchPanelMode, ngViewerActionForceShowSegment, ngViewerActionNehubaReady, ngViewerActionCycleViews } from './ngViewerState/actions';
-import { generalApplyState } from '../stateStore.helper';
-import { ngViewerSelectorPanelMode, ngViewerSelectorPanelOrder } from './ngViewerState/selectors';
-import { uiActionSnackbarMessage } from './uiState/actions';
-import { TUserRouteError } from 'src/auth/auth.service';
-import { viewerStateSelectedTemplateSelector } from './viewerState.store.helper';
-
-export function mixNgLayers(oldLayers: INgLayerInterface[], newLayers: INgLayerInterface|INgLayerInterface[]): INgLayerInterface[] {
-  if (newLayers instanceof Array) {
-    return oldLayers.concat(newLayers)
-  } else {
-    return oldLayers.concat({
-      ...newLayers,
-    })
-  }
-}
-
-export interface StateInterface {
-  layers: INgLayerInterface[]
-  forceShowSegment: boolean | null
-  nehubaReady: boolean
-  panelMode: string
-  panelOrder: string
-
-  octantRemoval: boolean
-  showSubstrate: boolean
-  showZoomlevel: boolean
-
-  clearViewQueue: {
-    [key: string]: boolean
-  }
-}
-
-export interface ActionInterface extends Action {
-  layer: INgLayerInterface
-  layers: INgLayerInterface[]
-  forceShowSegment: boolean
-  nehubaReady: boolean
-  payload: any
-}
-
-export const defaultState: StateInterface = {
-  layers: [],
-  forceShowSegment: null,
-  nehubaReady: false,
-  panelMode: PANELS.FOUR_PANEL,
-  panelOrder: `0123`,
-
-  octantRemoval: true,
-  showSubstrate: null,
-  showZoomlevel: null,
-
-  clearViewQueue: {}
-}
-
-export const ngViewerStateReducer = createReducer(
-  defaultState,
-  on(ngViewerActionClearView, (state, { payload }) => {
-    const { clearViewQueue } = state
-    const clearViewQueueUpdated = {...clearViewQueue}
-    for (const key in payload) {
-      clearViewQueueUpdated[key] = payload[key]
-    }
-    return {
-      ...state,
-      clearViewQueue: clearViewQueueUpdated
-    }
-  }),
-  on(ngViewerActionSetPerspOctantRemoval, (state, { octantRemovalFlag }) => {
-    return {
-      ...state,
-      octantRemoval: octantRemovalFlag
-    }
-  }),
-  on(ngViewerActionAddNgLayer, (state, { layer }) => {
-    return {
-      ...state,
-      layers: mixNgLayers(state.layers, layer)
-    }
-  }),
-  on(ngViewerActionSetPanelOrder, (state, { payload }) => {
-    const { panelOrder } = payload
-    return {
-      ...state,
-      panelOrder
-    }
-  }),
-  on(ngViewerActionSwitchPanelMode, (state, { payload }) => {
-    const { panelMode } = payload
-    if (SUPPORTED_PANEL_MODES.indexOf(panelMode as any) < 0) { return state }
-    return {
-      ...state,
-      panelMode
-    }
-  }),
-  on(ngViewerActionRemoveNgLayer, (state, { layer }) => {
-    
-    const newLayers = Array.isArray(layer)
-      ? (() => {
-        const layerNameSet = new Set(layer.map(l => l.name))
-        return state.layers.filter(l => !layerNameSet.has(l.name))
-      })()
-      : state.layers.filter(l => l.name !== layer.name)
-    return {
-      ...state,
-      layers: newLayers
-    }
-  }),
-  on(ngViewerActionForceShowSegment, (state, { forceShowSegment }) => {
-    return {
-      ...state,
-      forceShowSegment
-    }
-  }),
-  on(ngViewerActionNehubaReady, (state, { nehubaReady }) => {
-    return {
-      ...state,
-      nehubaReady
-    }
-  }),
-  on(generalApplyState, (_, { state }) => {
-    const { ngViewerState } = state
-    return ngViewerState
-  })
-)
-
-
-// must export a named function for aot compilation
-// see https://github.com/angular/angular/issues/15587
-// https://github.com/amcdnl/ngrx-actions/issues/23
-// or just google for:
-//
-// angular function expressions are not supported in decorators
-
-export function stateStore(state, action) {
-  return ngViewerStateReducer(state, action)
-}
-
-type TUserConfig = {
-
-}
-
-type TUserConfigResp = TUserConfig & TUserRouteError
-
-@Injectable({
-  providedIn: 'root',
-})
-
-export class NgViewerUseEffect implements OnDestroy {
-  @Effect()
-  public toggleMaximiseMode$: Observable<any>
-
-  @Effect()
-  public unmaximiseOrder$: Observable<any>
-
-  @Effect()
-  public maximiseOrder$: Observable<any>
-
-  @Effect()
-  public toggleMaximiseCycleMessage$: Observable<any>
-
-  @Effect()
-  public cycleViews$: Observable<any>
-
-  @Effect()
-  public removeAllNonBaseLayers$: Observable<any>
-
-  private panelOrder$: Observable<string>
-  private panelMode$: Observable<string>
-
-  private subscriptions: Subscription[] = []
-
-  @Effect()
-  public applySavedUserConfig$: Observable<any>
-
-  constructor(
-    private actions: Actions,
-    private store$: Store<any>,
-    private pureConstantService: PureContantService,
-    private http: HttpClient,
-  ){
-
-    this.applySavedUserConfig$ = this.http.get<TUserConfigResp>(`${this.pureConstantService.backendUrl}user/config`).pipe(
-      map(json => {
-        if (json.error) {
-          throw new Error(json.message || 'User not loggedin.')
-        }
-        return json
-      }),
-      catchError((err,caught) => of(null)),
-      filter(v => !!v),
-      withLatestFrom(this.store$),
-      map(([{ ngViewerState: fetchedNgViewerState }, state]) => {
-        const { ngViewerState } = state
-        return generalApplyState({
-          state: {
-            ...state,
-            ngViewerState: {
-              ...ngViewerState,
-              ...fetchedNgViewerState
-            }}
-        })
-      })
-    )
-
-    const toggleMaxmimise$ = this.actions.pipe(
-      ofType(ngViewerActionToggleMax.type),
-      shareReplay(1),
-    )
-
-    this.panelOrder$ = this.store$.pipe(
-      select(ngViewerSelectorPanelOrder),
-      distinctUntilChanged(),
-    )
-
-    this.panelMode$ = this.store$.pipe(
-      select(ngViewerSelectorPanelMode),
-      distinctUntilChanged(),
-    )
-
-    this.cycleViews$ = this.actions.pipe(
-      ofType(ngViewerActionCycleViews.type),
-      withLatestFrom(this.panelOrder$),
-      map(([_, panelOrder]) => {
-        return ngViewerActionSetPanelOrder({
-          payload: {
-            panelOrder: [...panelOrder.slice(1), ...panelOrder.slice(0, 1)].join(''),
-          }
-        })
-      }),
-    )
-
-    this.maximiseOrder$ = toggleMaxmimise$.pipe(
-      withLatestFrom(
-        combineLatest([
-          this.panelOrder$,
-          this.panelMode$,
-        ]),
-      ),
-      filter(([_action, [_panelOrder, panelMode]]) => panelMode !== PANELS.SINGLE_PANEL),
-      map(([ action, [ oldPanelOrder ] ]) => {
-        const { payload } = action as ActionInterface
-        const { index = 0 } = payload
-
-        const panelOrder = [...oldPanelOrder.slice(index), ...oldPanelOrder.slice(0, index)].join('')
-        return ngViewerActionSetPanelOrder({
-          payload: { panelOrder },
-        })
-      }),
-    )
-
-    this.unmaximiseOrder$ = toggleMaxmimise$.pipe(
-      withLatestFrom(
-        combineLatest([
-          this.panelOrder$,
-          this.panelMode$,
-        ]),
-      ),
-      scan((acc, curr) => {
-        const [action, [panelOrders, panelMode]] = curr
-        return [{
-          action,
-          panelOrders,
-          panelMode,
-        }, ...acc.slice(0, 1)]
-      }, [] as any[]),
-      filter(([ { panelMode } ]) => panelMode === PANELS.SINGLE_PANEL),
-      map(arr => {
-        const {
-          action,
-          panelOrders,
-        } = arr[0]
-
-        const {
-          panelOrders: panelOrdersPrev = null,
-        } = arr[1] || {}
-
-        const { payload } = action as ActionInterface
-        const { index = 0 } = payload
-
-        const panelOrder = panelOrdersPrev || [...panelOrders.slice(index), ...panelOrders.slice(0, index)].join('')
-
-        return ngViewerActionSetPanelOrder({
-          payload: { panelOrder }
-        })
-      }),
-    )
-
-    const scanFn = (acc: string[], curr: string): string[] => [curr, ...acc.slice(0, 1)]
-
-    this.toggleMaximiseMode$ = toggleMaxmimise$.pipe(
-      withLatestFrom(this.panelMode$.pipe(
-        scan(scanFn, []),
-      )),
-      map(([ _, panelModes ]) => {
-        return ngViewerActionSwitchPanelMode({
-          payload: {
-            panelMode: panelModes[0] === PANELS.SINGLE_PANEL
-              ? (panelModes[1] || PANELS.FOUR_PANEL)
-              : PANELS.SINGLE_PANEL,
-          },
-        })
-      }),
-    )
-
-    this.toggleMaximiseCycleMessage$ = combineLatest([
-      this.toggleMaximiseMode$,
-      this.pureConstantService.useTouchUI$,
-    ]).pipe(
-      filter(([_, useMobileUI]) => !useMobileUI),
-      map(([toggleMaximiseMode, _]) => toggleMaximiseMode),
-      filter(({ payload }) => payload.panelMode && payload.panelMode === PANELS.SINGLE_PANEL),
-      mapTo(uiActionSnackbarMessage({
-        snackbarMessage: CYCLE_PANEL_MESSAGE
-      })),
-    )
-
-    /**
-     * simplify with layer browser
-     */
-    const baseNgLayerName$ = this.store$.pipe(
-      select(viewerStateSelectedTemplateSelector),
-      
-      map(templateSelected => {
-        if (!templateSelected) { return [] }
-
-        const { ngId , otherNgIds = []} = templateSelected
-
-        return [
-          ngId,
-          ...otherNgIds,
-          ...templateSelected.parcellations.reduce((acc, curr) => {
-            return acc.concat([
-              curr.ngId,
-              ...getNgIds(curr.regions),
-            ])
-          }, []),
-        ]
-      }),
-      /**
-       * get unique array
-       */
-      map(nonUniqueArray => Array.from(new Set(nonUniqueArray))),
-      /**
-       * remove falsy values
-       */
-      map(arr => arr.filter(v => !!v)),
-    )
-
-    const allLoadedNgLayers$ = this.store$.pipe(
-      select('viewerState'),
-      select('loadedNgLayers'),
-    )
-
-    this.removeAllNonBaseLayers$ = this.actions.pipe(
-      ofType(ACTION_TYPES.REMOVE_ALL_NONBASE_LAYERS),
-      withLatestFrom(
-        combineLatest(
-          baseNgLayerName$,
-          allLoadedNgLayers$,
-        ),
-      ),
-      map(([_, [baseNgLayerNames, loadedNgLayers] ]) => {
-        const baseNameSet = new Set(baseNgLayerNames)
-        return loadedNgLayers.filter(l => !baseNameSet.has(l.name))
-      }),
-      map(layer => {
-        return ngViewerActionRemoveNgLayer({
-          layer
-        })
-      }),
-    )
-  }
-
-  public ngOnDestroy() {
-    while (this.subscriptions.length > 0) {
-      this.subscriptions.pop().unsubscribe()
-    }
-  }
-}
-
-export { INgLayerInterface } 
-
-const ACTION_TYPES = {
-
-  REMOVE_ALL_NONBASE_LAYERS: `REMOVE_ALL_NONBASE_LAYERS`,
-}
-
-export const SUPPORTED_PANEL_MODES = [
-  PANELS.FOUR_PANEL,
-  PANELS.H_ONE_THREE,
-  PANELS.V_ONE_THREE,
-  PANELS.SINGLE_PANEL,
-]
-
-export const NG_VIEWER_ACTION_TYPES = ACTION_TYPES
diff --git a/src/services/state/ngViewerState/actions.ts b/src/services/state/ngViewerState/actions.ts
deleted file mode 100644
index c504a085a33cb3a8c67291a3a5c28da57a687838..0000000000000000000000000000000000000000
--- a/src/services/state/ngViewerState/actions.ts
+++ /dev/null
@@ -1,72 +0,0 @@
-import { createAction, props, createReducer } from "@ngrx/store"
-import { INgLayerInterface } from './constants'
-
-export const ngViewerActionAddNgLayer = createAction(
-  '[ngLayerAction] addNgLayer',
-  props<{ layer: INgLayerInterface|INgLayerInterface[] }>()
-)
-
-export const ngViewerActionRemoveNgLayer = createAction(
-  '[ngLayerAction] removeNgLayer',
-  props<{ layer: Partial<INgLayerInterface>|Partial<INgLayerInterface>[] }>()
-)
-
-export const ngViewerActionSetPerspOctantRemoval = createAction(
-  `[ngViewerAction] setPerspectiveOctant`,
-  props<{ octantRemovalFlag: boolean }>()
-)
-
-export const ngViewerActionToggleMax = createAction(
-  `[ngViewerAction] toggleMax`,
-  props<{ payload: { index: number } }>()
-)
-
-export const ngViewerActionSetPanelOrder = createAction(
-  `[ngViewerAction] setPanelOrder`,
-  props<{ payload: { panelOrder: string } }>()
-)
-
-export const ngViewerActionSwitchPanelMode = createAction(
-  `[ngViewerAction] switchPanelMode`,
-  props<{ payload: { panelMode: string } }>()
-)
-
-export const ngViewerActionForceShowSegment = createAction(
-  `[ngViewerAction] forceShowSegment`,
-  props<{ forceShowSegment: boolean }>()
-)
-
-export const ngViewerActionNehubaReady = createAction(
-  `[ngViewerAction] nehubaReady`,
-  props<{ nehubaReady: boolean }>()
-)
-
-/**
- * Clear viewer view from additional layers such as PMap or connectivity
- * To request view to be cleared, call 
- * this.store$.dispatch(
- *  ngViewerActionClearView({ 
- *    payload: {
- *      ['my-unique-id']: true
- *    }
- *  })
- * )
- * 
- * When finished, call
- * 
- * this.store$.dispatch(
- *   ngViewerActionClearView({
- *    payload: {
- *      ['my-unique-id']: false
- *    }
- *   })
- * )
- */
-export const ngViewerActionClearView = createAction(
-  `[ngViewerAction] clearView`,
-  props<{ payload: { [key: string]: boolean }}>()
-)
-
-export const ngViewerActionCycleViews = createAction(
-  `[ngViewerAction] cycleView`
-)
\ No newline at end of file
diff --git a/src/services/state/ngViewerState/constants.ts b/src/services/state/ngViewerState/constants.ts
deleted file mode 100644
index 3e93ae4d0ec03ceec73175d1cba8254d6b4b894b..0000000000000000000000000000000000000000
--- a/src/services/state/ngViewerState/constants.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-export interface INgLayerInterface {
-  name: string // displayName
-  source: string
-  mixability?: string // base | mixable | nonmixable
-  annotation?: string //
-  id?: string // unique identifier
-  visible?: boolean
-  shader?: string
-  transform?: any
-  opacity?: number
-}
-
-export enum PANELS {
-  FOUR_PANEL = 'FOUR_PANEL',
-  V_ONE_THREE = 'V_ONE_THREE',
-  H_ONE_THREE = 'H_ONE_THREE',
-  SINGLE_PANEL = 'SINGLE_PANEL',
-}
diff --git a/src/services/state/ngViewerState/selectors.spec.ts b/src/services/state/ngViewerState/selectors.spec.ts
deleted file mode 100644
index c44f676f86802ee790f4b7e291e7e79c50ccb849..0000000000000000000000000000000000000000
--- a/src/services/state/ngViewerState/selectors.spec.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-import { ngViewerSelectorClearViewEntries } from './selectors'
-
-let clearViewQueue = {}
-
-describe('> ngViewerState/selectors.ts', () => {
-  describe('> ngViewerSelectorClearViewEntries', () => {
-    beforeEach(() => {
-      clearViewQueue = {}
-    })
-    describe('> when prop is not provided', () => {
-      it('> if clearViewQueue is empty (on startup)', () => {
-        const result = ngViewerSelectorClearViewEntries.projector(clearViewQueue)
-        expect(result).toEqual([])
-      })
-      it('> if clearViewQueue is non empty, but falsy, should return false', () => {
-        clearViewQueue['hello - world'] = null
-        clearViewQueue['oo bar'] = false
-        const result = ngViewerSelectorClearViewEntries.projector(clearViewQueue)
-        expect(result).toEqual([])
-      })
-      it('> if clearViewQueue is non empty and truthy, should return true', () => {
-        clearViewQueue['hello - world'] = 1
-        const result = ngViewerSelectorClearViewEntries.projector(clearViewQueue)
-        expect(result).toEqual(['hello - world'])
-      })
-    })
-  })
-})
\ No newline at end of file
diff --git a/src/services/state/ngViewerState/selectors.ts b/src/services/state/ngViewerState/selectors.ts
deleted file mode 100644
index 7222296d4627a32cf20716d05e2ea9e81d898e2e..0000000000000000000000000000000000000000
--- a/src/services/state/ngViewerState/selectors.ts
+++ /dev/null
@@ -1,42 +0,0 @@
-import { createSelector } from "@ngrx/store";
-
-export const ngViewerSelectorClearViewEntries = createSelector(
-  (state: any) => state?.ngViewerState?.clearViewQueue,
-  (clearViewQueue = {}) => {
-    const returnKeys = []
-    for (const key in clearViewQueue) {
-      if (!!clearViewQueue[key]) returnKeys.push(key)
-    }
-    return returnKeys
-  }
-)
-
-export const ngViewerSelectorClearView = createSelector(
-  ngViewerSelectorClearViewEntries,
-  keys => keys.length > 0
-)
-
-export const ngViewerSelectorPanelOrder = createSelector(
-  state => state['ngViewerState'],
-  ngViewerState => ngViewerState.panelOrder
-)
-
-export const ngViewerSelectorPanelMode = createSelector(
-  state => state['ngViewerState'],
-  ngViewerState => ngViewerState.panelMode
-)
-
-export const ngViewerSelectorOctantRemoval = createSelector(
-  state => state['ngViewerState'],
-  ngViewerState => ngViewerState.octantRemoval
-)
-
-export const ngViewerSelectorNehubaReady = createSelector(
-  state => state['ngViewerState'],
-  ngViewerState => ngViewerState.nehubaReady
-)
-
-export const ngViewerSelectorLayers = createSelector(
-  state => state['ngViewerState'],
-  ngViewerState => ngViewerState?.layers || []
-)
\ No newline at end of file
diff --git a/src/services/state/pluginState.helper.ts b/src/services/state/pluginState.helper.ts
deleted file mode 100644
index e1c48c1941ab716f1bacc6c8980f62c2367a933a..0000000000000000000000000000000000000000
--- a/src/services/state/pluginState.helper.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { createSelector } from "@ngrx/store"
-
-export const PLUGINSTORE_ACTION_TYPES = {
-  SET_INIT_PLUGIN: `SET_INIT_PLUGIN`,
-  CLEAR_INIT_PLUGIN: 'CLEAR_INIT_PLUGIN',
-}
-
-export const pluginStateSelectorInitManifests = createSelector(
-  state => state['pluginState'],
-  pluginState => pluginState.initManifests
-)
-
-export const PLUGINSTORE_CONSTANTS = {
-  INIT_MANIFEST_SRC: 'INIT_MANIFEST_SRC',
-}
diff --git a/src/services/state/pluginState.store.ts b/src/services/state/pluginState.store.ts
deleted file mode 100644
index 85bfa20a915ec8ab2e11497321cd66d95edabb91..0000000000000000000000000000000000000000
--- a/src/services/state/pluginState.store.ts
+++ /dev/null
@@ -1,59 +0,0 @@
-import { Action } from '@ngrx/store'
-import { generalApplyState } from '../stateStore.helper'
-import { PLUGINSTORE_ACTION_TYPES, PLUGINSTORE_CONSTANTS } from './pluginState.helper'
-export const defaultState: StateInterface = {
-  initManifests: []
-}
-
-export interface StateInterface {
-  initManifests: Array<[ string, string|null ]>
-}
-
-export interface ActionInterface extends Action {
-  manifest: {
-    name: string
-    initManifestUrl?: string
-  }
-}
-
-
-export const getStateStore = ({ state = defaultState } = {}) => (prevState: StateInterface = state, action: ActionInterface): StateInterface => {
-  switch (action.type) {
-  case PLUGINSTORE_ACTION_TYPES.SET_INIT_PLUGIN: {
-    const newMap = new Map(prevState.initManifests )
-
-    // reserved source label for init manifest
-    if (action.manifest.name !== PLUGINSTORE_CONSTANTS.INIT_MANIFEST_SRC) { newMap.set(action.manifest.name, action.manifest.initManifestUrl) }
-    return {
-      ...prevState,
-      initManifests: Array.from(newMap),
-    }
-  }
-  case PLUGINSTORE_ACTION_TYPES.CLEAR_INIT_PLUGIN: {
-    const { initManifests } = prevState
-    const newManifests = initManifests.filter(([source]) => source !== PLUGINSTORE_CONSTANTS.INIT_MANIFEST_SRC)
-    return {
-      ...prevState,
-      initManifests: newManifests,
-    }
-  }
-  case generalApplyState.type: {
-    const { pluginState } = (action as any).state
-    return pluginState
-  }
-  default: return prevState
-  }
-}
-
-// must export a named function for aot compilation
-// see https://github.com/angular/angular/issues/15587
-// https://github.com/amcdnl/ngrx-actions/issues/23
-// or just google for:
-//
-// angular function expressions are not supported in decorators
-
-const defaultStateStore = getStateStore()
-
-export function stateStore(state, action) {
-  return defaultStateStore(state, action)
-}
diff --git a/src/services/state/uiState.store.helper.ts b/src/services/state/uiState.store.helper.ts
deleted file mode 100644
index 6f1c187c15b94170fcdc5db6b5ba3d0664d6da75..0000000000000000000000000000000000000000
--- a/src/services/state/uiState.store.helper.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-// TODO merge with uiState.store.ts after refactor completes
-
-export {
-  uiActionSetPreviewingDatasetFiles,
-  uiActionShowSidePanelConnectivity,
-  uiStateCloseSidePanel,
-  uiStateCollapseSidePanel,
-  uiStateExpandSidePanel,
-  uiStateOpenSidePanel,
-  uiStateShowBottomSheet,
-  uiActionSnackbarMessage,
-  uiActionMouseoverLandmark,
-  uiActionMouseoverSegments,
-} from './uiState/actions'
-
-export {
-  uiStatePreviewingDatasetFilesSelector,
-  uiStateMouseOverSegmentsSelector,
-  uiStateMouseoverUserLandmark,
-} from './uiState/selectors'
-
-export enum EnumWidgetTypes{
-  DATASET_PREVIEW,
-}
-
-export interface IDatasetPreviewData{
-  datasetId: string
-  filename: string
-  datasetSchema?: string
-}
-
-export type TypeOpenedWidget = {
-  type: EnumWidgetTypes
-  data: IDatasetPreviewData
-}
-
-export const SHOW_KG_TOS = `SHOW_KG_TOS`
\ No newline at end of file
diff --git a/src/services/state/uiState.store.ts b/src/services/state/uiState.store.ts
deleted file mode 100644
index cab51af1edb88fdbcf360fb5e5a3628e9809dc51..0000000000000000000000000000000000000000
--- a/src/services/state/uiState.store.ts
+++ /dev/null
@@ -1,251 +0,0 @@
-import { Injectable, TemplateRef, OnDestroy } from '@angular/core';
-import { Action, select, Store } from '@ngrx/store'
-
-import { Effect, Actions, ofType } from "@ngrx/effects";
-import { Observable, Subscription } from "rxjs";
-import { filter, map, mapTo, scan, startWith, take } from "rxjs/operators";
-import { COOKIE_VERSION, KG_TOS_VERSION, LOCAL_STORAGE_CONST } from 'src/util/constants'
-import { IavRootStoreInterface, GENERAL_ACTION_TYPES } from '../stateStore.service'
-import { MatBottomSheetRef, MatBottomSheet } from '@angular/material/bottom-sheet';
-import { uiStateCloseSidePanel, uiStateOpenSidePanel, uiStateCollapseSidePanel, uiStateExpandSidePanel, uiActionSetPreviewingDatasetFiles, uiStateShowBottomSheet, uiActionShowSidePanelConnectivity } from './uiState.store.helper';
-import { viewerStateMouseOverCustomLandmark } from './viewerState/actions';
-import { IUiState } from './uiState/common'
-import { uiActionMouseoverLandmark, uiActionMouseoverSegments, uiActionSnackbarMessage } from './uiState/actions';
-export const defaultState: IUiState = {
-
-  previewingDatasetFiles: [],
-
-  mouseOverSegments: [],
-  mouseOverSegment: null,
-
-  mouseOverLandmark: null,
-  mouseOverUserLandmark: null,
-
-  focusedSidePanel: null,
-  sidePanelIsOpen: false,
-  sidePanelExploreCurrentViewIsOpen: false,
-
-  snackbarMessage: null,
-
-  /**
-   * replace with server side logic (?)
-   */
-  agreedCookies: localStorage.getItem(LOCAL_STORAGE_CONST.AGREE_COOKIE) === COOKIE_VERSION,
-  agreedKgTos: localStorage.getItem(LOCAL_STORAGE_CONST.AGREE_KG_TOS) === KG_TOS_VERSION,
-}
-
-export { IUiState }
-
-export const getStateStore = ({ state = defaultState } = {}) => (prevState: IUiState = state, action: ActionInterface) => {
-  switch (action.type) {
-
-  case uiActionSetPreviewingDatasetFiles.type: {
-    const { previewingDatasetFiles } = action as any
-    return {
-      ...prevState,
-      previewingDatasetFiles
-    }
-  }
-  case uiActionMouseoverSegments.type: {
-    const { segments } = action
-    return {
-      ...prevState,
-      mouseOverSegments: segments,
-    }
-  }
-  case MOUSE_OVER_SEGMENT: 
-    return {
-      ...prevState,
-      mouseOverSegment : action.segment,
-    }
-  case viewerStateMouseOverCustomLandmark.type: {
-    const { payload = {} } = action
-    const { userLandmark: mouseOverUserLandmark = null } = payload
-    return {
-      ...prevState,
-      mouseOverUserLandmark,
-    }
-  }
-  case uiActionMouseoverLandmark.type:
-    return {
-      ...prevState,
-      mouseOverLandmark : action.landmark,
-    }
-  case uiActionSnackbarMessage.type:
-  case SNACKBAR_MESSAGE: {
-    const { snackbarMessage } = action
-    /**
-       * Need to use symbol here, or repeated snackbarMessage will not trigger new event
-       */
-    return {
-      ...prevState,
-      snackbarMessage: Symbol(snackbarMessage),
-    }
-  }
-  case uiStateOpenSidePanel.type:
-  case OPEN_SIDE_PANEL:
-    return {
-      ...prevState,
-      sidePanelIsOpen: true,
-    }
-  case uiStateCloseSidePanel.type:
-  case CLOSE_SIDE_PANEL:
-    return {
-      ...prevState,
-      sidePanelIsOpen: false,
-    }
-  case uiActionShowSidePanelConnectivity.type:
-  case uiStateExpandSidePanel.type:
-  case EXPAND_SIDE_PANEL_CURRENT_VIEW:
-    return {
-      ...prevState,
-      sidePanelExploreCurrentViewIsOpen: true,
-    }
-  case uiStateCollapseSidePanel.type:
-  case COLLAPSE_SIDE_PANEL_CURRENT_VIEW:
-    return {
-      ...prevState,
-      sidePanelExploreCurrentViewIsOpen: false,
-    }
-
-  case AGREE_COOKIE: {
-    /**
-       * TODO replace with server side logic
-       */
-    localStorage.setItem(LOCAL_STORAGE_CONST.AGREE_COOKIE, COOKIE_VERSION)
-    return {
-      ...prevState,
-      agreedCookies: true,
-    }
-  }
-  case AGREE_KG_TOS: {
-    /**
-       * TODO replace with server side logic
-       */
-    localStorage.setItem(LOCAL_STORAGE_CONST.AGREE_KG_TOS, KG_TOS_VERSION)
-    return {
-      ...prevState,
-      agreedKgTos: true,
-    }
-  }
-  case GENERAL_ACTION_TYPES.APPLY_STATE: {
-    const { uiState } = (action as any).state
-    return uiState
-  }
-  default: return prevState
-  }
-}
-
-// must export a named function for aot compilation
-// see https://github.com/angular/angular/issues/15587
-// https://github.com/amcdnl/ngrx-actions/issues/23
-// or just google for:
-//
-// angular function expressions are not supported in decorators
-
-const defaultStateStore = getStateStore()
-
-export function stateStore(state, action) {
-  return defaultStateStore(state, action)
-}
-
-export interface ActionInterface extends Action {
-  segment: any | number
-  landmark: any
-  focusedSidePanel?: string
-  segments?: Array<{
-    layer: {
-      name: string
-    }
-    segment: any | null
-  }>
-  snackbarMessage: string
-
-  bottomSheetTemplate: TemplateRef<any>
-
-  payload: any
-}
-
-@Injectable({
-  providedIn: 'root',
-})
-
-export class UiStateUseEffect implements OnDestroy{
-
-  private subscriptions: Subscription[] = []
-
-  private numRegionSelectedWithHistory$: Observable<any[]>
-
-  @Effect()
-  public sidePanelOpen$: Observable<any>
-
-  @Effect()
-  public viewCurrentOpen$: Observable<any>
-
-  private bottomSheetRef: MatBottomSheetRef
-
-  constructor(
-    store$: Store<IavRootStoreInterface>,
-    actions$: Actions,
-    bottomSheet: MatBottomSheet
-  ) {
-    this.numRegionSelectedWithHistory$ = store$.pipe(
-      select('viewerState'),
-      select('regionsSelected'),
-      map(arr => arr.length),
-      startWith(0),
-      scan((acc, curr) => [curr, ...acc], []),
-    )
-
-    this.sidePanelOpen$ = this.numRegionSelectedWithHistory$.pipe(
-      filter(([curr, prev]) => prev === 0 && curr > 0),
-      mapTo({
-        type: OPEN_SIDE_PANEL,
-      }),
-    )
-
-    this.viewCurrentOpen$ = this.numRegionSelectedWithHistory$.pipe(
-      filter(([curr, prev]) => prev === 0 && curr > 0),
-      mapTo({
-        type: EXPAND_SIDE_PANEL_CURRENT_VIEW,
-      }),
-    )
-    
-    this.subscriptions.push(
-      actions$.pipe(
-        ofType(uiStateShowBottomSheet.type)
-      ).subscribe(({ bottomSheetTemplate, config }) => {
-        if (!bottomSheetTemplate) {
-          if (this.bottomSheetRef) {
-            this.bottomSheetRef.dismiss()
-            this.bottomSheetRef = null
-          }
-        } else {
-          this.bottomSheetRef = bottomSheet.open(bottomSheetTemplate, config)
-          this.bottomSheetRef.afterDismissed().subscribe(() => {
-            this.bottomSheetRef = null
-          })
-        }
-      })
-    )
-  }
-
-  ngOnDestroy(){
-    while(this.subscriptions.length > 0) {
-      this.subscriptions.pop().unsubscribe()
-    }
-  }
-}
-
-export const MOUSE_OVER_SEGMENT = `MOUSE_OVER_SEGMENT`
-
-export const CLOSE_SIDE_PANEL = `CLOSE_SIDE_PANEL`
-export const OPEN_SIDE_PANEL = `OPEN_SIDE_PANEL`
-export const COLLAPSE_SIDE_PANEL_CURRENT_VIEW = `COLLAPSE_SIDE_PANEL_CURRENT_VIEW`
-export const EXPAND_SIDE_PANEL_CURRENT_VIEW = `EXPAND_SIDE_PANEL_CURRENT_VIEW`
-
-export const AGREE_COOKIE = `AGREE_COOKIE`
-export const AGREE_KG_TOS = `AGREE_KG_TOS`
-
-export const SNACKBAR_MESSAGE = uiActionSnackbarMessage.type
-export const SHOW_BOTTOM_SHEET = `SHOW_BOTTOM_SHEET`
diff --git a/src/services/state/uiState/actions.ts b/src/services/state/uiState/actions.ts
deleted file mode 100644
index ae2fbc3bd9535428de9c3912a967ea71ee298c2c..0000000000000000000000000000000000000000
--- a/src/services/state/uiState/actions.ts
+++ /dev/null
@@ -1,49 +0,0 @@
-
-import { createAction, props } from '@ngrx/store'
-import { TemplateRef } from '@angular/core'
-import { MatBottomSheetConfig } from '@angular/material/bottom-sheet'
-
-export const uiStateCloseSidePanel = createAction(
-  '[uiState] closeSidePanel'
-)
-
-export const uiStateOpenSidePanel = createAction(
-  '[uiState] openSidePanel'
-)
-
-export const uiStateCollapseSidePanel = createAction(
-  '[uiState] collapseSidePanelCurrentView'
-)
-
-export const uiStateExpandSidePanel = createAction(
-  '[uiState] expandSidePanelCurrentView'
-)
-
-export const uiStateShowBottomSheet = createAction(
-  '[uiState] showBottomSheet',
-  props<{ bottomSheetTemplate: TemplateRef<unknown>, config?: MatBottomSheetConfig }>()
-)
-
-export const uiActionMouseoverLandmark = createAction(
-  `[uiState] mouseoverLandmark`,
-  props<{ landmark: string }>()
-)
-
-export const uiActionMouseoverSegments = createAction(
-  `[uiState] mouseoverSegments`,
-  props<{ segments: any[] }>()
-)
-
-export const uiActionSetPreviewingDatasetFiles = createAction(
-  `[uiState] setDatasetPreviews`,
-  props<{previewingDatasetFiles: {datasetId: string, filename: string}[]}>()
-)
-
-export const uiActionShowSidePanelConnectivity = createAction(
-  `[uiState] showSidePanelConnectivity`
-)
-
-export const uiActionSnackbarMessage = createAction(
-  `[uiState] snackbarMessage`,
-  props<{snackbarMessage: string}>()
-)
\ No newline at end of file
diff --git a/src/services/state/uiState/common.ts b/src/services/state/uiState/common.ts
deleted file mode 100644
index dd5da3140f280c9cef5c7cd29637c2577f47587f..0000000000000000000000000000000000000000
--- a/src/services/state/uiState/common.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-export interface IUiState{
-
-  previewingDatasetFiles: {datasetId: string, filename: string}[]
-
-  mouseOverSegments: Array<{
-    layer: {
-      name: string
-    }
-    segment: any | null
-  }>
-  sidePanelIsOpen: boolean
-  sidePanelExploreCurrentViewIsOpen: boolean
-  mouseOverSegment: any | number
-
-  mouseOverLandmark: string
-  mouseOverUserLandmark: any
-
-  focusedSidePanel: string | null
-
-  snackbarMessage: symbol
-
-  agreedCookies: boolean
-  agreedKgTos: boolean
-}
diff --git a/src/services/state/uiState/selectors.spec.ts b/src/services/state/uiState/selectors.spec.ts
deleted file mode 100644
index 2464c9c8a1a4770a1e1c14afd28cb7c3dd4a3833..0000000000000000000000000000000000000000
--- a/src/services/state/uiState/selectors.spec.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-import { uiStateMouseOverSegmentsSelector } from './selectors'
-
-describe('> uiState/selectors.ts', () => {
-  describe('> mouseOverSegments', () => {
-
-  })
-})
diff --git a/src/services/state/uiState/selectors.ts b/src/services/state/uiState/selectors.ts
deleted file mode 100644
index 483539b2d784f567564bc2963233b8dd91dabcb1..0000000000000000000000000000000000000000
--- a/src/services/state/uiState/selectors.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import { createSelector } from "@ngrx/store";
-import { TMouseOverSegment } from "src/mouseoverModule/type";
-import { IUiState } from './common'
-
-export const uiStatePreviewingDatasetFilesSelector = createSelector(
-  state => state['uiState'],
-  (uiState: IUiState) => uiState['previewingDatasetFiles']
-)
-
-export const uiStateMouseOverSegmentsSelector = createSelector(
-  state => state['uiState'],
-  uiState => uiState['mouseOverSegments'] as TMouseOverSegment[]
-)
-
-export const uiStateMouseOverLandmarkSelector = createSelector(
-  state => state['uiState'],
-  uiState => uiState['mouseOverLandmark'] as string
-)
-
-export const uiStateMouseoverUserLandmark = createSelector(
-  state => state['uiState'],
-  uiState => uiState['mouseOverUserLandmark']
-)
diff --git a/src/services/state/uiState/ui.effects.ts b/src/services/state/uiState/ui.effects.ts
deleted file mode 100644
index 128655ad53c9ec87a3655dc4da19f336ab4f918a..0000000000000000000000000000000000000000
--- a/src/services/state/uiState/ui.effects.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-import { Injectable, OnDestroy } from "@angular/core";
-import { MatSnackBar } from "@angular/material/snack-bar";
-import { Actions, ofType } from "@ngrx/effects";
-import { Subscription } from "rxjs";
-import { generalActionError } from "src/services/stateStore.helper";
-
-@Injectable({
-  providedIn: 'root'
-})
-
-export class UiEffects implements OnDestroy{
-
-  private subscriptions: Subscription[] = []
-
-  constructor(
-    private actions$: Actions,
-    snackBar: MatSnackBar
-  ){
-    this.subscriptions.push(
-      this.actions$.pipe(
-        ofType(generalActionError.type)
-      ).subscribe((payload: any) => {
-        if (!payload.message) console.log(payload)
-        snackBar.open(payload.message || `Error: cannot complete your action.`, 'Dismiss', { duration: 5000 })
-      })
-    )
-  }
-
-  ngOnDestroy(){
-    while (this.subscriptions.length > 0) this.subscriptions.pop().unsubscribe()
-  }
-}
diff --git a/src/services/state/userConfigState.helper.spec.ts b/src/services/state/userConfigState.helper.spec.ts
deleted file mode 100644
index e007700e80d5b030b8d2d70cb41e71d64dfcd136..0000000000000000000000000000000000000000
--- a/src/services/state/userConfigState.helper.spec.ts
+++ /dev/null
@@ -1,47 +0,0 @@
-import { selectorPluginCspPermission } from "./userConfigState.helper"
-
-describe('> userConfigState.helper.ts', () => {
-  describe('> selectorPluginCspPermission', () => {
-    const expectedTrue = {
-      value: true
-    }
-    const expectedFalse = {
-      value: false
-    }
-    describe('> malformed init value', () => {
-      describe('> undefined userconfigstate', () => {
-        it('> return expected false val', () => {
-          const returnVal = selectorPluginCspPermission.projector(null, { key: 'foo-bar' })
-          expect(returnVal).toEqual(expectedFalse)
-        })
-      })
-      describe('> undefined pluginCsp property', () => {
-        it('> return expected false val', () => {
-          const returnVal = selectorPluginCspPermission.projector({}, { key: 'foo-bar' })
-          expect(returnVal).toEqual(expectedFalse)
-        })
-      })
-    })
-
-    describe('> well fored init valu', () => {
-
-      describe('> undefined key', () => {
-        it('> return expected false val', () => {
-          const returnVal = selectorPluginCspPermission.projector({
-            pluginCsp: {'yes-man': true}
-          }, { key: 'foo-bar' })
-          expect(returnVal).toEqual(expectedFalse)
-        })
-      })
-
-      describe('> truthly defined key', () => {
-        it('> return expected true val', () => {
-          const returnVal = selectorPluginCspPermission.projector({ pluginCsp:
-            { 'foo-bar': true }
-          }, { key: 'foo-bar' })
-          expect(returnVal).toEqual(expectedTrue)
-        })
-      })
-    })
-  })
-})
\ No newline at end of file
diff --git a/src/services/state/userConfigState.helper.ts b/src/services/state/userConfigState.helper.ts
deleted file mode 100644
index e71be024c1947ccf0fc3f3aca909cebbceeaf046..0000000000000000000000000000000000000000
--- a/src/services/state/userConfigState.helper.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { createSelector } from "@ngrx/store"
-
-export const selectorPluginCspPermission = createSelector(
-  (state: any) => state.userConfigState,
-  (userConfigState: any, props: any = {}) => {
-    const { key } = props as { key: string }
-    return {
-      value: !!userConfigState?.pluginCsp?.[key]
-    } 
-  }
-)
diff --git a/src/services/state/userConfigState.store.spec.ts b/src/services/state/userConfigState.store.spec.ts
deleted file mode 100644
index 4f1b078d5298d3850bdc07148314234eee216889..0000000000000000000000000000000000000000
--- a/src/services/state/userConfigState.store.spec.ts
+++ /dev/null
@@ -1,88 +0,0 @@
-import { HttpClientTestingModule, HttpTestingController } from "@angular/common/http/testing"
-import { fakeAsync, TestBed, tick } from "@angular/core/testing"
-import { provideMockActions } from "@ngrx/effects/testing"
-import { Action } from "@ngrx/store"
-import { MockStore, provideMockStore } from "@ngrx/store/testing"
-import { from, Observable } from "rxjs"
-import { AngularMaterialModule } from "src/sharedModules"
-import { PureContantService } from "src/util"
-import { DialogService } from "../dialogService.service"
-import { actionUpdatePluginCsp, UserConfigStateUseEffect } from "./userConfigState.store"
-import { viewerStateSelectedParcellationSelector, viewerStateSelectedRegionsSelector, viewerStateSelectedTemplateSelector } from "./viewerState/selectors"
-
-describe('> userConfigState.store.spec.ts', () => {
-  describe('> UserConfigStateUseEffect', () => {
-    let action$: Observable<Action>
-    beforeEach(() => {
-      TestBed.configureTestingModule({
-        imports: [
-          HttpClientTestingModule,
-          AngularMaterialModule,
-        ],
-        providers: [
-          provideMockActions(() => action$),
-          provideMockStore({
-            initialState: {
-              viewerConfigState: {
-                gpuLimit: 1e9,
-                animation: true
-              }
-            }
-          }),
-          DialogService,
-          {
-            provide: PureContantService,
-            useValue: {
-              backendUrl: 'http://localhost:3000/'
-            }
-          }
-        ]
-      })
-
-      const mockStore = TestBed.inject(MockStore)
-      mockStore.overrideSelector(viewerStateSelectedTemplateSelector, null)
-      mockStore.overrideSelector(viewerStateSelectedParcellationSelector, null)
-      mockStore.overrideSelector(viewerStateSelectedRegionsSelector, [])
-    })
-
-    it('> can be init', () => {
-      const useEffect = TestBed.inject(UserConfigStateUseEffect)
-      expect(useEffect).toBeTruthy()
-    })
-    describe('> setInitPluginPermission$', () => {
-      let mockHttp: HttpTestingController
-      let useEffect: UserConfigStateUseEffect
-      const mockpluginPer = {
-        'foo-bar': {
-          'script-src': [
-            '1',
-            '2',
-          ]
-        }
-      }
-      beforeEach(() => {
-        mockHttp = TestBed.inject(HttpTestingController)
-        useEffect = TestBed.inject(UserConfigStateUseEffect)
-      })
-      afterEach(() => {
-        mockHttp.verify()
-      })
-      it('> calls /GET user/pluginPermissions', fakeAsync(() => {
-        let val
-        useEffect.setInitPluginPermission$.subscribe(v => val = v)
-        tick(20)
-        const req = mockHttp.expectOne(`http://localhost:3000/user/pluginPermissions`)
-        req.flush(mockpluginPer)
-        expect(val).toEqual(actionUpdatePluginCsp({ payload: mockpluginPer }))
-      }))
-
-      it('> if get fn fails', fakeAsync(() => {
-        let val
-        useEffect.setInitPluginPermission$.subscribe(v => val = v)
-        const req = mockHttp.expectOne(`http://localhost:3000/user/pluginPermissions`)
-        req.error(null, { status: 500, statusText: 'Internal Error' })
-        expect(val).toEqual(actionUpdatePluginCsp({ payload: {} }))
-      }))
-    })
-  })
-})
\ No newline at end of file
diff --git a/src/services/state/userConfigState.store.ts b/src/services/state/userConfigState.store.ts
deleted file mode 100644
index 81fef3205c4020651d6169012d43c19da67ec0f9..0000000000000000000000000000000000000000
--- a/src/services/state/userConfigState.store.ts
+++ /dev/null
@@ -1,423 +0,0 @@
-import { Injectable, OnDestroy } from "@angular/core";
-import { Actions, Effect, ofType } from "@ngrx/effects";
-import { Action, createAction, createReducer, props, select, Store, on, createSelector } from "@ngrx/store";
-import { combineLatest, from, Observable, of, Subscription } from "rxjs";
-import { catchError, distinctUntilChanged, filter, map, mapTo, share, shareReplay, switchMap, take, withLatestFrom } from "rxjs/operators";
-import { BACKENDURL, LOCAL_STORAGE_CONST } from "src/util//constants";
-import { DialogService } from "../dialogService.service";
-import { recursiveFindRegionWithLabelIndexId } from "src/util/fn";
-import { serialiseParcellationRegion } from 'common/util'
-// Get around the problem of importing duplicated string (ACTION_TYPES), even using ES6 alias seems to trip up the compiler
-// TODO file bug and reverse
-import { HttpClient } from "@angular/common/http";
-import { actionSetMobileUi, viewerStateNewViewer, viewerStateSelectParcellation, viewerStateSetSelectedRegions } from "./viewerState/actions";
-import { viewerStateSelectedParcellationSelector, viewerStateSelectedRegionsSelector, viewerStateSelectedTemplateSelector } from "./viewerState/selectors";
-import { PureContantService } from "src/util";
-
-interface ICsp{
-  'connect-src'?: string[]
-  'script-src'?: string[]
-}
-
-export interface StateInterface {
-  savedRegionsSelection: RegionSelection[]
-  /**
-   * plugin csp - currently store in localStorage
-   * if user log in, store in user profile
-   */
-  pluginCsp: {
-    /**
-     * key === plugin version id 
-     */
-    [key: string]: ICsp
-  }
-}
-
-export interface RegionSelection {
-  templateSelected: any
-  parcellationSelected: any
-  regionsSelected: any[]
-  name: string
-  id: string
-}
-
-/**
- * for serialisation into local storage/database
- */
-interface SimpleRegionSelection {
-  id: string
-  name: string
-  tName: string
-  pName: string
-  rSelected: string[]
-}
-
-interface UserConfigAction extends Action {
-  config?: Partial<StateInterface>
-  payload?: any
-}
-
-export const defaultState: StateInterface = {
-  savedRegionsSelection: [],
-  pluginCsp: {}
-}
-
-export const actionUpdateRegionSelections = createAction(
-  `[userConfig] updateRegionSelections`,
-  props<{ config: { savedRegionsSelection: RegionSelection[]} }>()
-)
-
-export const selectorAllPluginsCspPermission = createSelector(
-  (state: any) => state.userConfigState,
-  userConfigState => userConfigState.pluginCsp
-)
-
-export const actionUpdatePluginCsp = createAction(
-  `[userConfig] updatePluginCspPermission`,
-  props<{
-    payload: {
-      [key: string]: ICsp
-    }
-  }>()
-)
-
-export const ACTION_TYPES = {
-  UPDATE_REGIONS_SELECTIONS: actionUpdateRegionSelections.type,
-  UPDATE_REGIONS_SELECTION: 'UPDATE_REGIONS_SELECTION',
-  SAVE_REGIONS_SELECTION: `SAVE_REGIONS_SELECTIONN`,
-  DELETE_REGIONS_SELECTION: 'DELETE_REGIONS_SELECTION',
-
-  LOAD_REGIONS_SELECTION: 'LOAD_REGIONS_SELECTION',
-}
-
-
-export const userConfigReducer = createReducer(
-  defaultState,
-  on(actionUpdateRegionSelections, (state, { config }) => {
-    const { savedRegionsSelection } = config
-    return {
-      ...state,
-      savedRegionsSelection
-    }
-  }),
-  on(actionUpdatePluginCsp, (state, { payload }) => {
-    return {
-      ...state,
-      pluginCsp: payload
-    }
-  })
-)
-
-@Injectable({
-  providedIn: 'root',
-})
-export class UserConfigStateUseEffect implements OnDestroy {
-
-  private subscriptions: Subscription[] = []
-
-  constructor(
-    private actions$: Actions,
-    private store$: Store<any>,
-    private dialogService: DialogService,
-    private http: HttpClient,
-    private constantSvc: PureContantService,
-  ) {
-    const viewerState$ = this.store$.pipe(
-      select('viewerState'),
-      shareReplay(1),
-    )
-
-    this.parcellationSelected$ = this.store$.pipe(
-      select(viewerStateSelectedParcellationSelector),
-      distinctUntilChanged(),
-    )
-
-    this.tprSelected$ = combineLatest(
-      this.store$.pipe(
-        select(viewerStateSelectedTemplateSelector),
-        distinctUntilChanged(),
-      ),
-      this.parcellationSelected$,
-      this.store$.pipe(
-        select(viewerStateSelectedRegionsSelector)
-        /**
-         * TODO
-         * distinct selectedRegions
-         */
-      ),
-    ).pipe(
-      map(([ templateSelected, parcellationSelected, regionsSelected ]) => {
-        return {
-          templateSelected, parcellationSelected, regionsSelected,
-        }
-      }),
-    )
-
-    this.savedRegionsSelections$ = this.store$.pipe(
-      select('userConfigState'),
-      select('savedRegionsSelection'),
-      shareReplay(1),
-    )
-
-    this.onSaveRegionsSelection$ = this.actions$.pipe(
-      ofType(ACTION_TYPES.SAVE_REGIONS_SELECTION),
-      withLatestFrom(this.tprSelected$),
-      withLatestFrom(this.savedRegionsSelections$),
-
-      map(([[action, tprSelected], savedRegionsSelection]) => {
-        const { payload = {} } = action as UserConfigAction
-        const { name = 'Untitled' } = payload
-
-        const { templateSelected, parcellationSelected, regionsSelected } = tprSelected
-        const newSavedRegionSelection: RegionSelection = {
-          id: Date.now().toString(),
-          name,
-          templateSelected,
-          parcellationSelected,
-          regionsSelected,
-        }
-        return {
-          type: ACTION_TYPES.UPDATE_REGIONS_SELECTIONS,
-          config: {
-            savedRegionsSelection: savedRegionsSelection.concat([newSavedRegionSelection]),
-          },
-        } as UserConfigAction
-      }),
-    )
-
-    this.onDeleteRegionsSelection$ = this.actions$.pipe(
-      ofType(ACTION_TYPES.DELETE_REGIONS_SELECTION),
-      withLatestFrom(this.savedRegionsSelections$),
-      map(([ action, savedRegionsSelection ]) => {
-        const { payload = {} } = action as UserConfigAction
-        const { id } = payload
-        return {
-          type: ACTION_TYPES.UPDATE_REGIONS_SELECTIONS,
-          config: {
-            savedRegionsSelection: savedRegionsSelection.filter(srs => srs.id !== id),
-          },
-        }
-      }),
-    )
-
-    this.onUpdateRegionsSelection$ = this.actions$.pipe(
-      ofType(ACTION_TYPES.UPDATE_REGIONS_SELECTION),
-      withLatestFrom(this.savedRegionsSelections$),
-      map(([ action, savedRegionsSelection]) => {
-        const { payload = {} } = action as UserConfigAction
-        const { id, ...rest } = payload
-        return {
-          type: ACTION_TYPES.UPDATE_REGIONS_SELECTIONS,
-          config: {
-            savedRegionsSelection: savedRegionsSelection
-              .map(srs => srs.id === id
-                ? { ...srs, ...rest }
-                : { ...srs }),
-          },
-        }
-      }),
-    )
-
-    this.subscriptions.push(
-      this.actions$.pipe(
-        ofType(ACTION_TYPES.LOAD_REGIONS_SELECTION),
-        map(action => {
-          const { payload = {}} = action as UserConfigAction
-          const { savedRegionsSelection }: {savedRegionsSelection: RegionSelection} = payload
-          return savedRegionsSelection
-        }),
-        filter(val => !!val),
-        withLatestFrom(this.tprSelected$),
-        switchMap(([savedRegionsSelection, { parcellationSelected, templateSelected, regionsSelected }]) =>
-          from(this.dialogService.getUserConfirm({
-            title: `Load region selection: ${savedRegionsSelection.name}`,
-            message: `This action would cause the viewer to navigate away from the current view. Proceed?`,
-          })).pipe(
-            catchError((e, obs) => of(null)),
-            map(() => {
-              return {
-                savedRegionsSelection,
-                parcellationSelected,
-                templateSelected,
-                regionsSelected,
-              }
-            }),
-            filter(val => !!val),
-          ),
-        ),
-        switchMap(({ savedRegionsSelection, parcellationSelected, templateSelected, regionsSelected }) => {
-          if (templateSelected.name !== savedRegionsSelection.templateSelected.name ) {
-            /**
-             * template different, dispatch viewerStateNewViewer.type
-             */
-            this.store$.dispatch(
-              viewerStateNewViewer({
-                selectParcellation: savedRegionsSelection.parcellationSelected,
-                selectTemplate: savedRegionsSelection.templateSelected,
-              })
-            )
-            return this.parcellationSelected$.pipe(
-              filter(p => p.updated),
-              take(1),
-              map(() => {
-                return {
-                  regionsSelected: savedRegionsSelection.regionsSelected,
-                }
-              }),
-            )
-          }
-
-          if (parcellationSelected.name !== savedRegionsSelection.parcellationSelected.name) {
-            /**
-             * parcellation different, dispatch SELECT_PARCELLATION
-             */
-            this.store$.dispatch(
-              viewerStateSelectParcellation({
-                selectParcellation: savedRegionsSelection.parcellationSelected,
-              })
-            )
-            return this.parcellationSelected$.pipe(
-              filter(p => p.updated),
-              take(1),
-              map(() => {
-                return {
-                  regionsSelected: savedRegionsSelection.regionsSelected,
-                }
-              }),
-            )
-          }
-
-          return of({
-            regionsSelected: savedRegionsSelection.regionsSelected,
-          })
-        }),
-      ).subscribe(({ regionsSelected }) => {
-        this.store$.dispatch(
-          viewerStateSetSelectedRegions({
-            selectRegions: regionsSelected,
-          })
-        )
-      }),
-    )
-
-    this.subscriptions.push(
-      this.store$.pipe(
-        select('viewerConfigState'),
-      ).subscribe(({ gpuLimit, animation }) => {
-
-        if (gpuLimit) {
-          window.localStorage.setItem(LOCAL_STORAGE_CONST.GPU_LIMIT, gpuLimit.toString())
-        }
-        if (typeof animation !== 'undefined' && animation !== null) {
-          window.localStorage.setItem(LOCAL_STORAGE_CONST.ANIMATION, animation.toString())
-        }
-      }),
-    )
-
-    this.subscriptions.push(
-      this.actions$.pipe(
-        ofType(actionSetMobileUi.type),
-        map((action: any) => {
-          const { payload } = action
-          const { useMobileUI } = payload
-          return useMobileUI
-        }),
-        filter(bool => bool !== null),
-      ).subscribe((bool: boolean) => {
-        window.localStorage.setItem(LOCAL_STORAGE_CONST.MOBILE_UI, JSON.stringify(bool))
-      }),
-    )
-
-    this.subscriptions.push(
-      this.actions$.pipe(
-        ofType(ACTION_TYPES.UPDATE_REGIONS_SELECTIONS),
-      ).subscribe(action => {
-        const { config = {} } = action as UserConfigAction
-        const { savedRegionsSelection } = config
-        const simpleSRSs = savedRegionsSelection.map(({ id, name, templateSelected, parcellationSelected, regionsSelected }) => {
-          return {
-            id,
-            name,
-            tName: templateSelected.name,
-            pName: parcellationSelected.name,
-            rSelected: regionsSelected.map(({ ngId, labelIndex }) => serialiseParcellationRegion({ ngId, labelIndex })),
-          } as SimpleRegionSelection
-        })
-
-        /**
-         * TODO save server side on per user basis
-         */
-        window.localStorage.setItem(LOCAL_STORAGE_CONST.SAVED_REGION_SELECTIONS, JSON.stringify(simpleSRSs))
-      }),
-    )
-
-    const savedSRSsString = window.localStorage.getItem(LOCAL_STORAGE_CONST.SAVED_REGION_SELECTIONS)
-    const savedSRSs: SimpleRegionSelection[] = savedSRSsString && JSON.parse(savedSRSsString)
-
-    this.restoreSRSsFromStorage$ = viewerState$.pipe(
-      filter(() => !!savedSRSs),
-      select('fetchedTemplates'),
-      distinctUntilChanged(),
-      map(fetchedTemplates => savedSRSs.map(({ id, name, tName, pName, rSelected }) => {
-        const templateSelected = fetchedTemplates.find(t => t.name === tName)
-        const parcellationSelected = templateSelected && templateSelected.parcellations.find(p => p.name === pName)
-        const regionsSelected = parcellationSelected && rSelected.map(labelIndexId => recursiveFindRegionWithLabelIndexId({
-          regions: parcellationSelected.regions,
-          labelIndexId,
-          inheritedNgId: parcellationSelected.ngId
-        }))
-        return {
-          templateSelected,
-          parcellationSelected,
-          id,
-          name,
-          regionsSelected,
-        } as RegionSelection
-      })),
-      filter(restoredSavedRegions => restoredSavedRegions.every(rs => rs.regionsSelected && rs.regionsSelected.every(r => !!r))),
-      take(1),
-      map(savedRegionsSelection => {
-        return {
-          type: ACTION_TYPES.UPDATE_REGIONS_SELECTIONS,
-          config: { savedRegionsSelection },
-        }
-      }),
-    )
-  }
-
-  public ngOnDestroy() {
-    while (this.subscriptions.length > 0) {
-      this.subscriptions.pop().unsubscribe()
-    }
-  }
-
-  /**
-   * Temmplate Parcellation Regions selected
-   */
-  private tprSelected$: Observable<{templateSelected: any, parcellationSelected: any, regionsSelected: any[]}>
-  private savedRegionsSelections$: Observable<any[]>
-  private parcellationSelected$: Observable<any>
-
-  @Effect()
-  public onSaveRegionsSelection$: Observable<any>
-
-  @Effect()
-  public onDeleteRegionsSelection$: Observable<any>
-
-  @Effect()
-  public onUpdateRegionsSelection$: Observable<any>
-
-  @Effect()
-  public restoreSRSsFromStorage$: Observable<any>
-
-  @Effect()
-  public setInitPluginPermission$ = this.http.get(`${this.constantSvc.backendUrl}user/pluginPermissions`, {
-    responseType: 'json'
-  }).pipe(
-    /**
-     * TODO show warning?
-     */
-    catchError(() => of({})),
-    map((json: any) => actionUpdatePluginCsp({ payload: json }))
-  )
-}
diff --git a/src/services/state/viewerConfig.store.helper.ts b/src/services/state/viewerConfig.store.helper.ts
deleted file mode 100644
index 719b4745979f04cc7df9c98172dbc17bdf09c965..0000000000000000000000000000000000000000
--- a/src/services/state/viewerConfig.store.helper.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import { createSelector } from "@ngrx/store"
-
-export const VIEWER_CONFIG_FEATURE_KEY = 'viewerConfigState'
-export interface IViewerConfigState {
-  gpuLimit: number
-  animation: boolean
-  useMobileUI: boolean
-}
-
-export const viewerConfigSelectorUseMobileUi = createSelector(
-  state => state[VIEWER_CONFIG_FEATURE_KEY],
-  viewerConfigState => viewerConfigState.useMobileUI
-)
diff --git a/src/services/state/viewerConfig.store.ts b/src/services/state/viewerConfig.store.ts
deleted file mode 100644
index 27850b946bd2e0ca51f8616c91fd5a9b6ff52a23..0000000000000000000000000000000000000000
--- a/src/services/state/viewerConfig.store.ts
+++ /dev/null
@@ -1,89 +0,0 @@
-import { Action } from "@ngrx/store";
-import { LOCAL_STORAGE_CONST } from "src/util/constants";
-
-import { IViewerConfigState as StateInterface } from './viewerConfig.store.helper'
-import { actionSetMobileUi } from "./viewerState/actions";
-export { StateInterface }
-
-interface ViewerConfigurationAction extends Action {
-  config: Partial<StateInterface>
-  payload: any
-}
-
-export const CONFIG_CONSTANTS = {
-  /**
-   * byets
-   */
-  gpuLimitMin: 1e8,
-  gpuLimitMax: 1e9,
-  defaultGpuLimit: 1e9,
-  defaultAnimation: true,
-}
-
-export const VIEWER_CONFIG_ACTION_TYPES = {
-  SET_ANIMATION: `SET_ANIMATION`,
-  UPDATE_CONFIG: `UPDATE_CONFIG`,
-  SET_MOBILE_UI: actionSetMobileUi.type,
-}
-
-// get gpu limit
-const lsGpuLimit = localStorage.getItem(LOCAL_STORAGE_CONST.GPU_LIMIT)
-const lsAnimationFlag = localStorage.getItem(LOCAL_STORAGE_CONST.ANIMATION)
-const gpuLimit = lsGpuLimit && !isNaN(Number(lsGpuLimit))
-  ? Number(lsGpuLimit)
-  : CONFIG_CONSTANTS.defaultGpuLimit
-
-// get animation flag
-const animation = lsAnimationFlag && lsAnimationFlag === 'true'
-  ? true
-  : lsAnimationFlag === 'false'
-    ? false
-    : CONFIG_CONSTANTS.defaultAnimation
-
-// get mobile ui setting
-// UA sniff only if not useMobileUI not explicitly set
-const getIsMobile = () => {
-  // https://developer.mozilla.org/en-US/docs/Web/API/Navigator/maxTouchPoints
-  // CC0 or MIT
-  // msMaxTouchPoints is not needed, since IE is not supported
-  return 'maxTouchPoints' in navigator && navigator.maxTouchPoints > 0
-}
-const useMobileUIStroageValue = window && window.localStorage && window.localStorage.getItem(LOCAL_STORAGE_CONST.MOBILE_UI)
-
-export const defaultState: StateInterface = {
-  animation,
-  gpuLimit,
-  useMobileUI: (useMobileUIStroageValue && useMobileUIStroageValue === 'true') || getIsMobile(),
-}
-
-export const getStateStore = ({ state = defaultState } = {}) => (prevState: StateInterface = state, action: ViewerConfigurationAction) => {
-  switch (action.type) {
-  case VIEWER_CONFIG_ACTION_TYPES.SET_MOBILE_UI: {
-    const { payload } = action
-    const { useMobileUI } = payload
-    return {
-      ...prevState,
-      useMobileUI,
-    }
-  }
-  case VIEWER_CONFIG_ACTION_TYPES.UPDATE_CONFIG:
-    return {
-      ...prevState,
-      ...action.config,
-    }
-  default: return prevState
-  }
-}
-
-// must export a named function for aot compilation
-// see https://github.com/angular/angular/issues/15587
-// https://github.com/amcdnl/ngrx-actions/issues/23
-// or just google for:
-//
-// angular function expressions are not supported in decorators
-
-const defaultStateStore = getStateStore()
-
-export function stateStore(state, action) {
-  return defaultStateStore(state, action)
-}
diff --git a/src/services/state/viewerConfig/selectors.ts b/src/services/state/viewerConfig/selectors.ts
deleted file mode 100644
index 4b69c0e58e84b67d30b509f09c8f6df9ff67b787..0000000000000000000000000000000000000000
--- a/src/services/state/viewerConfig/selectors.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import { createSelector } from "@ngrx/store";
-
-export const selectViewerConfigAnimationFlag = createSelector(
-  state => state['viewerConfigState'],
-  viewerConfigState => viewerConfigState['animation']
-)
diff --git a/src/services/state/viewerState.store.helper.spec.ts b/src/services/state/viewerState.store.helper.spec.ts
deleted file mode 100644
index fa0845554d221ea0051658ba8d096145913be883..0000000000000000000000000000000000000000
--- a/src/services/state/viewerState.store.helper.spec.ts
+++ /dev/null
@@ -1,346 +0,0 @@
-import { TestBed } from "@angular/core/testing"
-import { Action } from "@ngrx/store"
-import { provideMockActions } from "@ngrx/effects/testing"
-import { MockStore, provideMockStore } from "@ngrx/store/testing"
-import { Observable, of } from "rxjs"
-import { isNewerThan, ViewerStateHelperEffect } from "./viewerState.store.helper"
-import { viewerStateGetSelectedAtlas, viewerStateSelectedTemplateSelector } from "./viewerState/selectors"
-import { viewerStateHelperSelectParcellationWithId, viewerStateRemoveAdditionalLayer } from "./viewerState/actions"
-import { generalActionError } from "../stateStore.helper"
-import { hot } from "jasmine-marbles"
-
-describe('> viewerState.store.helper.ts', () => {
-  const tmplId = 'test-tmpl-id'
-  const tmplId0 = 'test-tmpl-id-0'
-  describe('> ViewerStateHelperEffect', () => {
-    let effect: ViewerStateHelperEffect
-    let mockStore: MockStore
-    let actions$: Observable<Action>
-    beforeEach(() => {
-      TestBed.configureTestingModule({
-        providers: [
-          ViewerStateHelperEffect,
-          provideMockStore(),
-          provideMockActions(() => actions$)
-        ]
-      })
-
-      mockStore = TestBed.inject(MockStore)
-      mockStore.overrideSelector(viewerStateSelectedTemplateSelector, {
-        ['@id']: tmplId
-      })
-
-      actions$ = of(
-        viewerStateRemoveAdditionalLayer({
-          payload: {
-            ['@id']: 'bla'
-          }
-        })
-      )
-    })
-
-    describe('> if selected atlas has no matching tmpl space', () => {
-      beforeEach(() => {
-        mockStore.overrideSelector(viewerStateGetSelectedAtlas, {
-          templateSpaces: [{
-            ['@id']: tmplId0
-          }]
-        })
-      })
-      it('> should emit gernal error', () => {
-        effect = TestBed.inject(ViewerStateHelperEffect)
-        effect.onRemoveAdditionalLayer$.subscribe(val => {
-          expect(val.type === generalActionError.type)
-        })
-      })
-    })
-
-    describe('> if selected atlas has matching tmpl', () => {
-
-      const parcId0 = 'test-parc-id-0'
-      const parcId1 = 'test-parc-id-1'
-      const tmpSp = {
-        ['@id']: tmplId,
-        availableIn: [{
-          ['@id']: parcId0
-        }],
-      }
-      beforeEach(() => {
-        mockStore.overrideSelector(viewerStateGetSelectedAtlas, {
-          templateSpaces: [
-            tmpSp
-          ],
-          parcellations: [],
-        })
-      })
-
-      describe('> if parc is empty array', () => {
-        it('> should emit with falsy as payload', () => {
-          effect = TestBed.inject(ViewerStateHelperEffect)
-          expect(
-            effect.onRemoveAdditionalLayer$
-          ).toBeObservable(
-            hot('(a|)', {
-              a: viewerStateHelperSelectParcellationWithId({
-                payload: undefined
-              })
-            })
-          )
-        })
-      })
-      describe('> if no parc has eligible @id', () => {
-        beforeEach(() => {
-          mockStore.overrideSelector(viewerStateGetSelectedAtlas, {
-            templateSpaces: [
-              tmpSp
-            ],
-            parcellations: [{
-              ['@id']: parcId1
-            }]
-          })
-        })
-        it('> should emit with falsy as payload', () => {
-          effect = TestBed.inject(ViewerStateHelperEffect)
-          expect(
-            effect.onRemoveAdditionalLayer$
-          ).toBeObservable(
-            hot('(a|)', {
-              a: viewerStateHelperSelectParcellationWithId({
-                payload: undefined
-              })
-            })
-          )
-        })
-      })
-
-      describe('> if some parc has eligible @id', () => {
-        describe('> if no @version is available', () => {
-          const parc1 = {
-            ['@id']: parcId0,
-            name: 'p0-0',
-            baseLayer: true
-          }
-          const parc2 = {
-            ['@id']: parcId0,
-            name: 'p0-1',
-            baseLayer: true
-          }
-          beforeEach(() => {
-
-            mockStore.overrideSelector(viewerStateGetSelectedAtlas, {
-              templateSpaces: [
-                tmpSp
-              ],
-              parcellations: [
-                parc1,
-                parc2
-              ]
-            })
-          })
-          it('> selects the first parc', () => {
-
-            effect = TestBed.inject(ViewerStateHelperEffect)
-            expect(
-              effect.onRemoveAdditionalLayer$
-            ).toBeObservable(
-              hot('(a|)', {
-                a: viewerStateHelperSelectParcellationWithId({
-                  payload: parc1
-                })
-              })
-            )
-          })
-        })
-
-        describe('> if @version is available', () => {
-          
-          describe('> if there exist an entry without @next attribute', () => {
-            
-            const parc1 = {
-              ['@id']: parcId0,
-              name: 'p0-0',
-              baseLayer: true,
-              ['@version']: {
-                ['@next']: 'random-value'
-              }
-            }
-            const parc2 = {
-              ['@id']: parcId0,
-              name: 'p0-1',
-              baseLayer: true,
-              ['@version']: {
-                ['@next']: null
-              }
-            }
-            beforeEach(() => {
-
-              mockStore.overrideSelector(viewerStateGetSelectedAtlas, {
-                templateSpaces: [
-                  tmpSp
-                ],
-                parcellations: [
-                  parc1,
-                  parc2
-                ]
-              })
-            })
-            it('> selects the first one without @next attribute', () => {
-
-              effect = TestBed.inject(ViewerStateHelperEffect)
-              expect(
-                effect.onRemoveAdditionalLayer$
-              ).toBeObservable(
-                hot('(a|)', {
-                  a: viewerStateHelperSelectParcellationWithId({
-                    payload: parc2
-                  })
-                })
-              )
-            })
-          })
-          describe('> if there exist no entry without @next attribute', () => {
-            
-            const parc1 = {
-              ['@id']: parcId0,
-              name: 'p0-0',
-              baseLayer: true,
-              ['@version']: {
-                ['@next']: 'random-value'
-              }
-            }
-            const parc2 = {
-              ['@id']: parcId0,
-              name: 'p0-1',
-              baseLayer: true,
-              ['@version']: {
-                ['@next']: 'another-random-value'
-              }
-            }
-            beforeEach(() => {
-
-              mockStore.overrideSelector(viewerStateGetSelectedAtlas, {
-                templateSpaces: [
-                  tmpSp
-                ],
-                parcellations: [
-                  parc1,
-                  parc2
-                ]
-              })
-            })
-            it('> selects the first one without @next attribute', () => {
-
-              effect = TestBed.inject(ViewerStateHelperEffect)
-              expect(
-                effect.onRemoveAdditionalLayer$
-              ).toBeObservable(
-                hot('(a|)', {
-                  a: viewerStateHelperSelectParcellationWithId({
-                    payload: parc1
-                  })
-                })
-              )
-            })
-          })
-        })
-      })
-    })
-  })
-
-  describe('> isNewerThan', () => {
-    describe('> ill formed versions', () => {
-      it('> in circular references, throws', () => {
-
-        const parc0Circular = {
-
-          "@version": {
-            "@next": "aaa-bbb",
-            "@this": "ccc-ddd",
-            "name": "",
-            "@previous": null,
-          }
-        }
-        const parc1Circular = {
-  
-          "@version": {
-            "@next": "ccc-ddd",
-            "@this": "aaa-bbb",
-            "name": "",
-            "@previous": null,
-          }
-        }
-        const p2 = {
-          ["@id"]: "foo-bar"
-        }
-        const p3 = {
-          ["@id"]: "baz"
-        }
-        expect(() => {
-          isNewerThan([parc0Circular, parc1Circular], p2, p3)
-        }).toThrow()
-      })
-
-      it('> if not found, will throw', () => {
-
-        const parc0Circular = {
-
-          "@version": {
-            "@next": "aaa-bbb",
-            "@this": "ccc-ddd",
-            "name": "",
-            "@previous": null,
-          }
-        }
-        const parc1Circular = {
-  
-          "@version": {
-            "@next": null,
-            "@this": "aaa-bbb",
-            "name": "",
-            "@previous": null,
-          }
-        }
-        const p2 = {
-          ["@id"]: "foo-bar"
-        }
-        const p3 = {
-          ["@id"]: "baz"
-        }
-        expect(() => {
-          isNewerThan([parc0Circular, parc1Circular], p2, p3)
-        }).toThrow()
-      })
-    })
-
-    it('> works on well formed versions', () => {
-
-      const parc0 = {
-        "@version": {
-          "@next": null,
-          "@this": "aaa-bbb",
-          "name": "",
-          "@previous": "ccc-ddd",
-        }
-      }
-      const parc1 = {
-        "@version": {
-          "@next": "aaa-bbb",
-          "@this": "ccc-ddd",
-          "name": "",
-          "@previous": null,
-        }
-      }
-
-      const p0 = {
-        ['@id']: 'aaa-bbb'
-      }
-      const p1 = {
-        ['@id']: 'ccc-ddd'
-      }
-      expect(
-        isNewerThan([parc0, parc1], p0, p1)
-      ).toBeTrue()
-    })
-
-  })
-})
\ No newline at end of file
diff --git a/src/services/state/viewerState.store.helper.ts b/src/services/state/viewerState.store.helper.ts
deleted file mode 100644
index bf5f69fb89b4e9dbed57fd4066927a0254758944..0000000000000000000000000000000000000000
--- a/src/services/state/viewerState.store.helper.ts
+++ /dev/null
@@ -1,205 +0,0 @@
-// TODO merge with viewerstate.store.ts when refactor is done
-import { createReducer, on, ActionReducer, Store, select } from "@ngrx/store";
-import { generalActionError, generalApplyState } from "../stateStore.helper";
-import { Effect, Actions, ofType } from "@ngrx/effects";
-import { Observable } from "rxjs";
-import { withLatestFrom, map } from "rxjs/operators";
-import { Injectable } from "@angular/core";
-
-import {
-  viewerStateNewViewer,
-  viewerStateHelperSelectParcellationWithId,
-  viewerStateNavigateToRegion,
-  viewerStateRemoveAdditionalLayer,
-  viewerStateSelectAtlas,
-  viewerStateSelectParcellation,
-  viewerStateSelectTemplateWithId,
-  viewerStateSetConnectivityRegion,
-  viewerStateNehubaLayerchanged,
-  viewerStateSetFetchedAtlases,
-  viewerStateSetSelectedRegions,
-  viewerStateSetSelectedRegionsWithIds,
-  viewerStateToggleLayer,
-  viewerStateToggleRegionSelect,
-  viewerStateSelectRegionWithIdDeprecated,
-  viewerStateSetViewerMode,
-  viewerStateDblClickOnViewer,
-  viewerStateAddUserLandmarks,
-  viewreStateRemoveUserLandmarks,
-  viewerStateMouseOverCustomLandmark,
-  viewerStateMouseOverCustomLandmarkInPerspectiveView,
-  viewerStateSelectTemplateWithName,
-} from './viewerState/actions'
-
-export {
-  viewerStateNewViewer,
-  viewerStateHelperSelectParcellationWithId,
-  viewerStateNavigateToRegion,
-  viewerStateRemoveAdditionalLayer,
-  viewerStateSelectAtlas,
-  viewerStateSelectParcellation,
-  viewerStateSelectTemplateWithId,
-  viewerStateSetConnectivityRegion,
-  viewerStateNehubaLayerchanged,
-  viewerStateSetFetchedAtlases,
-  viewerStateSetSelectedRegions,
-  viewerStateSetSelectedRegionsWithIds,
-  viewerStateToggleLayer,
-  viewerStateToggleRegionSelect,
-  viewerStateSelectRegionWithIdDeprecated,
-  viewerStateSetViewerMode,
-  viewerStateDblClickOnViewer,
-  viewerStateAddUserLandmarks,
-  viewreStateRemoveUserLandmarks,
-  viewerStateMouseOverCustomLandmark,
-  viewerStateMouseOverCustomLandmarkInPerspectiveView,
-  viewerStateSelectTemplateWithName,
-}
-
-import {
-  viewerStateSelectedRegionsSelector,
-  viewerStateSelectedTemplateSelector,
-  viewerStateSelectedParcellationSelector,
-  viewerStateGetSelectedAtlas,
-  viewerStateCustomLandmarkSelector,
-  viewerStateFetchedTemplatesSelector,
-  viewerStateNavigationStateSelector,
-} from './viewerState/selectors'
-import { IHasId } from "src/util/interfaces";
-
-export {
-  viewerStateSelectedRegionsSelector,
-  viewerStateSelectedTemplateSelector,
-  viewerStateSelectedParcellationSelector,
-  viewerStateCustomLandmarkSelector,
-  viewerStateFetchedTemplatesSelector,
-  viewerStateNavigationStateSelector,
-}
-
-interface IViewerStateHelperStore{
-  fetchedAtlases: any[]
-  selectedAtlasId: string
-  overlayingAdditionalParcellations: any[]
-}
-
-const initialState: IViewerStateHelperStore = {
-  fetchedAtlases: [],
-  selectedAtlasId: null,
-  overlayingAdditionalParcellations: []
-}
-
-function handleToggleLayerAction(reducer: ActionReducer<any>): ActionReducer<any>{
-  return function(state, action) {
-    switch(action.type){
-    case viewerStateToggleLayer.type: {
-      const { payload } = action as any
-      const { templateSelected } = (state && state['viewerState']) || {}
-
-      const selectParcellation = templateSelected?.parcellations.find(p => p['@id'] === payload['@id'])
-      return reducer(state, viewerStateSelectParcellation({ selectParcellation }))
-    }
-    default: reducer(state, action)
-    }
-    return reducer(state, action)
-  }
-}
-
-export const viewerStateMetaReducers = [
-  handleToggleLayerAction
-]
-
-@Injectable({
-  providedIn: 'root'
-})
-
-export class ViewerStateHelperEffect{
-  @Effect()
-  onRemoveAdditionalLayer$: Observable<any> = this.actions$.pipe(
-    ofType(viewerStateRemoveAdditionalLayer.type),
-    withLatestFrom(
-      this.store$.pipe(
-        select(viewerStateGetSelectedAtlas)
-      ),
-      this.store$.pipe(
-        select(viewerStateSelectedTemplateSelector)
-      )
-    ),
-    map(([ { payload }, selectedAtlas, selectedTemplate ]) => {
-      const tmpl = selectedAtlas['templateSpaces'].find(t => t['@id'] === selectedTemplate['@id'])
-      if (!tmpl) {
-        return generalActionError({
-          message: `templateSpace with id ${selectedTemplate['@id']} cannot be found in atlas with id ${selectedAtlas['@id']}`
-        })
-      }
-
-      const eligibleParcIdSet = new Set(
-        tmpl.availableIn.map(p => p['@id'])
-      )
-      const baseLayers = selectedAtlas['parcellations'].filter(fullP => fullP['baseLayer'] && eligibleParcIdSet.has(fullP['@id']))
-      const baseLayer = baseLayers.find(layer => !!layer['@version'] && !layer['@version']['@next']) || baseLayers[0]
-      return viewerStateHelperSelectParcellationWithId({ payload: baseLayer })
-    })
-  )
-
-  constructor(
-    private store$: Store<any>,
-    private actions$: Actions
-  ){
-
-  }
-}
-
-export const viewerStateHelperReducer = createReducer(
-  initialState,
-  on(viewerStateSetFetchedAtlases, (state, { fetchedAtlases }) => ({ ...state, fetchedAtlases })),
-  on(viewerStateSelectAtlas, (state, { atlas }) => ({ ...state, selectedAtlasId: atlas['@id'] })),
-  on(generalApplyState, (_prevState, { state }) => ({ ...state[viewerStateHelperStoreName] })),
-)
-
-export const viewerStateHelperStoreName = 'viewerStateHelper'
-
-export const defaultState = initialState
-
-interface IVersion{
-  "@next": string
-  "@this": string
-  "name": string
-  "@previous": string
-}
-
-interface IHasVersion{
-  ['@version']: IVersion
-}
-
-export function isNewerThan(arr: IHasVersion[], srcObj: IHasId, compObj: IHasId): boolean {
-
-  function* GenNewerVersions(flag){
-    let it = 0
-    const newest =  arr.find((v => v['@version'] && v['@version']['@this'] === srcObj['@id']))
-    if (!newest) throw new Error(`GenNewerVersions error newest element isn't found`)
-    yield newest
-    let currPreviousId = newest['@version'][ flag ? '@next' : '@previous' ]
-    while (currPreviousId) {
-      it += 1
-      if (it>100) throw new Error(`iteration excced 100, did you include a loop?`)
-
-      const curr = arr.find(v => v['@version']['@this'] === currPreviousId)
-      if (!curr) throw new Error(`GenNewerVersions error, version id ${currPreviousId} not found`)
-      currPreviousId = curr['@version'][ flag ? '@next' : '@previous' ]
-      yield curr
-    }
-  }
-  for (const obj of GenNewerVersions(true)) {
-    if (obj['@version']['@this'] === compObj['@id']) {
-      return false
-    }
-  }
-
-  for (const obj of GenNewerVersions(false)) {
-    if (obj['@version']['@this'] === compObj['@id']) {
-      return true
-    }
-  }
-
-  throw new Error(`isNewerThan error, neither srcObj nor compObj exist in array`)
-}
diff --git a/src/services/state/viewerState.store.ts b/src/services/state/viewerState.store.ts
deleted file mode 100644
index ed2422c70f8c2746d8e6d79fb903cb866f126452..0000000000000000000000000000000000000000
--- a/src/services/state/viewerState.store.ts
+++ /dev/null
@@ -1,470 +0,0 @@
-import { Injectable } from '@angular/core';
-import { Actions, Effect, ofType } from '@ngrx/effects';
-import { Action, select, Store } from '@ngrx/store'
-import { Observable } from 'rxjs';
-import { distinctUntilChanged, filter, map, shareReplay, startWith, withLatestFrom, mapTo } from 'rxjs/operators';
-import { IUserLandmark } from 'src/atlasViewer/atlasViewer.apiService.service';
-import { INgLayerInterface } from 'src/atlasViewer/atlasViewer.component';
-import { getViewer } from 'src/util/fn';
-import { LoggingService } from 'src/logging';
-import { IavRootStoreInterface } from '../stateStore.service';
-import { GENERAL_ACTION_TYPES } from '../stateStore.service'
-import { CLOSE_SIDE_PANEL } from './uiState.store';
-import {
-  viewerStateSetSelectedRegions,
-  viewerStateSetConnectivityRegion,
-  viewerStateSelectParcellation,
-  viewerStateSelectRegionWithIdDeprecated,
-  viewerStateCustomLandmarkSelector,
-  viewerStateDblClickOnViewer,
-  viewerStateAddUserLandmarks,
-  viewreStateRemoveUserLandmarks,
-  viewerStateMouseOverCustomLandmark,
-  viewerStateMouseOverCustomLandmarkInPerspectiveView,
-  viewerStateNewViewer
-} from './viewerState.store.helper';
-import { cvtNehubaConfigToNavigationObj } from 'src/state';
-import {
-  viewerStateChangeNavigation,
-  viewerStateNehubaLayerchanged,
-  viewerStateSetViewerMode,
-  actionSelectLandmarks
-} from './viewerState/actions';
-import { serialiseParcellationRegion } from "common/util"
-
-export interface StateInterface {
-  fetchedTemplates: any[]
-
-  templateSelected: any | null
-  parcellationSelected: any | null
-  regionsSelected: any[]
-
-  viewerMode: string
-
-  landmarksSelected: any[]
-  userLandmarks: IUserLandmark[]
-
-  navigation: any | null
-  dedicatedView: string[]
-
-  loadedNgLayers: INgLayerInterface[]
-  connectivityRegion: string | null
-  overwrittenColorMap: string | null
-
-  standaloneVolumes: any[]
-}
-
-export interface ActionInterface extends Action {
-  fetchedTemplate?: any[]
-
-  selectTemplate?: any
-  selectParcellation?: any
-  selectRegions?: any[]
-  selectRegionIds: string[]
-  deselectRegions?: any[]
-  dedicatedView?: string
-
-  updatedParcellation?: any
-
-  landmarks: IUserLandmark[]
-  deselectLandmarks: IUserLandmark[]
-
-  navigation?: any
-
-  payload: any
-
-  connectivityRegion?: string
-}
-
-export const defaultState: StateInterface = {
-
-  landmarksSelected : [],
-  fetchedTemplates : [],
-  loadedNgLayers: [],
-  regionsSelected: [],
-  viewerMode: null,
-  userLandmarks: [],
-  dedicatedView: null,
-  navigation: null,
-  parcellationSelected: null,
-  templateSelected: null,
-  connectivityRegion: '',
-  overwrittenColorMap: null,
-  standaloneVolumes: []
-}
-
-export const getStateStore = ({ state = defaultState } = {}) => (prevState: Partial<StateInterface> = state, action: ActionInterface) => {
-  switch (action.type) {
-  /**
-     * TODO may be obsolete. test when nifti become available
-     */
-  case LOAD_DEDICATED_LAYER: {
-    const dedicatedView = prevState.dedicatedView
-      ? prevState.dedicatedView.concat(action.dedicatedView)
-      : [action.dedicatedView]
-    return {
-      ...prevState,
-      dedicatedView,
-    }
-  }
-  case UNLOAD_DEDICATED_LAYER:
-    return {
-      ...prevState,
-      dedicatedView : prevState.dedicatedView
-        ? prevState.dedicatedView.filter(dv => dv !== action.dedicatedView)
-        : [],
-    }
-  case CLEAR_STANDALONE_VOLUMES:
-    return {
-      ...prevState,
-      standaloneVolumes: []
-    }
-  case viewerStateNewViewer.type: {
-
-    const {
-      selectParcellation: parcellation,
-      navigation,
-      selectTemplate,
-    } = action
-    const navigationFromTemplateSelected = cvtNehubaConfigToNavigationObj(selectTemplate?.nehubaConfig?.dataset?.initialNgState)
-    return {
-      ...prevState,
-      templateSelected : selectTemplate,
-      parcellationSelected : parcellation,
-      // taken care of by effect.ts
-      // regionsSelected : [],
-
-      // taken care of by effect.ts
-      // landmarksSelected : [],
-      navigation : navigation || navigationFromTemplateSelected,
-      dedicatedView : null,
-    }
-  }
-  case FETCHED_TEMPLATE : {
-    return {
-      ...prevState,
-      fetchedTemplates: prevState.fetchedTemplates.concat(action.fetchedTemplate),
-    }
-  }
-  case viewerStateChangeNavigation.type:
-  case CHANGE_NAVIGATION : {
-    return {
-      ...prevState,
-      navigation : action.navigation,
-    }
-  }
-  case viewerStateSelectParcellation.type:
-  case SELECT_PARCELLATION : {
-    const { selectParcellation } = action
-    return {
-      ...prevState,
-      parcellationSelected: selectParcellation,
-      // taken care of by effect.ts
-      // regionsSelected: []
-    }
-  }
-  case viewerStateSetSelectedRegions.type:
-  case SELECT_REGIONS: {
-    const { selectRegions } = action
-    return {
-      ...prevState,
-      regionsSelected: selectRegions,
-    }
-  }
-  case viewerStateSetViewerMode.type: {
-    return {
-      ...prevState,
-      viewerMode: action.payload
-    }
-  }
-  case DESELECT_LANDMARKS : {
-    return {
-      ...prevState,
-      landmarksSelected : prevState.landmarksSelected.filter(lm => action.deselectLandmarks.findIndex(dLm => dLm.name === lm.name) < 0),
-    }
-  }
-  case actionSelectLandmarks.type: {
-    return {
-      ...prevState,
-      landmarksSelected : action.landmarks,
-    }
-  }
-  case USER_LANDMARKS : {
-    return {
-      ...prevState,
-      userLandmarks: action.landmarks,
-    }
-  }
-  /**
-     * TODO
-     * duplicated with ngViewerState.layers ?
-     */
-  case viewerStateNehubaLayerchanged.type: {
-    const viewer = getViewer()
-    if (!viewer) {
-      return {
-        ...prevState,
-        loadedNgLayers: [],
-      }
-    } else {
-      return {
-        ...prevState,
-        loadedNgLayers: (viewer.layerManager.managedLayers as any[]).map(obj => ({
-          name : obj.name,
-          type : obj.initialSpecification.type,
-          source : obj.sourceUrl,
-          visible : obj.visible,
-        }) as INgLayerInterface),
-      }
-    }
-  }
-  case GENERAL_ACTION_TYPES.APPLY_STATE: {
-    const { viewerState } = (action as any).state
-    return viewerState
-  }
-  case viewerStateSetConnectivityRegion.type:
-  case SET_CONNECTIVITY_REGION:
-    return {
-      ...prevState,
-      connectivityRegion: action.connectivityRegion,
-    }
-  case CLEAR_CONNECTIVITY_REGION:
-    return {
-      ...prevState,
-      connectivityRegion: '',
-    }
-  case SET_OVERWRITTEN_COLOR_MAP:
-    return {
-      ...prevState,
-      overwrittenColorMap: action.payload || '',
-    }
-  default :
-    return prevState
-  }
-}
-
-// must export a named function for aot compilation
-// see https://github.com/angular/angular/issues/15587
-// https://github.com/amcdnl/ngrx-actions/issues/23
-// or just google for:
-//
-// angular function expressions are not supported in decorators
-
-const defaultStateStore = getStateStore()
-
-export function stateStore(state, action) {
-  return defaultStateStore(state, action)
-}
-
-export const LOAD_DEDICATED_LAYER = 'LOAD_DEDICATED_LAYER'
-export const UNLOAD_DEDICATED_LAYER = 'UNLOAD_DEDICATED_LAYER'
-
-export const FETCHED_TEMPLATE = 'FETCHED_TEMPLATE'
-export const CHANGE_NAVIGATION = viewerStateChangeNavigation.type
-
-export const SELECT_PARCELLATION = viewerStateSelectParcellation.type
-
-export const DESELECT_REGIONS = `DESELECT_REGIONS`
-export const SELECT_REGIONS_WITH_ID = viewerStateSelectRegionWithIdDeprecated.type
-// export const SET_VIEWER_MODE = viewerStateSetViewerMode.type
-export const SELECT_LANDMARKS = `SELECT_LANDMARKS`
-export const SELECT_REGIONS = viewerStateSetSelectedRegions.type
-export const DESELECT_LANDMARKS = `DESELECT_LANDMARKS`
-export const USER_LANDMARKS = `USER_LANDMARKS`
-
-export const SET_CONNECTIVITY_REGION = `SET_CONNECTIVITY_REGION`
-export const CLEAR_CONNECTIVITY_REGION = `CLEAR_CONNECTIVITY_REGION`
-export const SET_OVERWRITTEN_COLOR_MAP = `SET_OVERWRITTEN_COLOR_MAP`
-export const CLEAR_STANDALONE_VOLUMES = `CLEAR_STANDALONE_VOLUMES`
-
-@Injectable({
-  providedIn: 'root',
-})
-
-export class ViewerStateUseEffect {
-  constructor(
-    private actions$: Actions,
-    private store$: Store<IavRootStoreInterface>,
-    private log: LoggingService,
-  ) {
-
-    const viewerState$ = this.store$.pipe(
-      select('viewerState'),
-      shareReplay(1)
-    )
-    this.currentLandmarks$ = this.store$.pipe(
-      select(viewerStateCustomLandmarkSelector),
-      shareReplay(1),
-    )
-
-    this.removeUserLandmarks = this.actions$.pipe(
-      ofType(ACTION_TYPES.REMOVE_USER_LANDMARKS),
-      withLatestFrom(this.currentLandmarks$),
-      map(([action, currentLandmarks]) => {
-        const { landmarkIds } = (action as ActionInterface).payload
-        for ( const rmId of landmarkIds ) {
-          const idx = currentLandmarks.findIndex(({ id }) => id === rmId)
-          if (idx < 0) { this.log.warn(`remove userlandmark with id ${rmId} does not exist`) }
-        }
-        const removeSet = new Set(landmarkIds)
-        return {
-          type: USER_LANDMARKS,
-          landmarks: currentLandmarks.filter(({ id }) => !removeSet.has(id)),
-        }
-      }),
-    )
-
-    this.addUserLandmarks$ = this.actions$.pipe(
-      ofType(viewerStateAddUserLandmarks.type),
-      withLatestFrom(this.currentLandmarks$),
-      map(([action, currentLandmarks]) => {
-        const { landmarks } = action as ActionInterface
-        const landmarkMap = new Map()
-        for (const landmark of currentLandmarks) {
-          const { id } = landmark
-          landmarkMap.set(id, landmark)
-        }
-        for (const landmark of landmarks) {
-          const { id } = landmark
-          if (landmarkMap.has(id)) {
-            this.log.warn(`Attempting to add a landmark that already exists, id: ${id}`)
-          } else {
-            landmarkMap.set(id, landmark)
-          }
-        }
-        const userLandmarks = Array.from(landmarkMap).map(([_id, landmark]) => landmark)
-        return {
-          type: USER_LANDMARKS,
-          landmarks: userLandmarks,
-        }
-      }),
-    )
-
-    this.mouseoverUserLandmarks = this.actions$.pipe(
-      ofType(viewerStateMouseOverCustomLandmarkInPerspectiveView.type),
-      withLatestFrom(this.currentLandmarks$),
-      map(([ action, currentLandmarks ]) => {
-        const { payload } = action as any
-        const { label } = payload
-        if (!label) {
-          return viewerStateMouseOverCustomLandmark({
-            payload: {
-              userLandmark: null
-            }
-          })
-        }
-
-        const idx = Number(label.replace('label=', ''))
-        if (isNaN(idx)) {
-          this.log.warn(`Landmark index could not be parsed as a number: ${idx}`)
-          return viewerStateMouseOverCustomLandmark({
-            payload: { userLandmark: null }
-          })
-        }
-        return viewerStateMouseOverCustomLandmark({
-          payload: {
-            userLandmark: currentLandmarks[idx]
-          }
-        })
-      }),
-
-    )
-
-    const doubleClickOnViewer$ = this.actions$.pipe(
-      ofType(ACTION_TYPES.DOUBLE_CLICK_ON_VIEWER),
-      map(action => {
-        const { payload } = action as any
-        const { segments, landmark, userLandmark } = payload
-        return { segments, landmark, userLandmark }
-      }),
-      shareReplay(1),
-    )
-
-    this.doubleClickOnViewerToggleRegions$ = doubleClickOnViewer$.pipe(
-      filter(({ segments }) => segments && segments.length > 0),
-      withLatestFrom(viewerState$.pipe(
-        select('regionsSelected'),
-        distinctUntilChanged(),
-        startWith([]),
-      )),
-      map(([{ segments }, regionsSelected]) => {
-        const selectedSet = new Set<string>(regionsSelected.map(serialiseParcellationRegion))
-        const toggleArr = segments.map(({ segment, layer }) => serialiseParcellationRegion({ ngId: layer.name, ...segment }))
-
-        const deleteFlag = toggleArr.some(id => selectedSet.has(id))
-
-        for (const id of toggleArr) {
-          if (deleteFlag) { selectedSet.delete(id) } else { selectedSet.add(id) }
-        }
-
-        return viewerStateSelectRegionWithIdDeprecated({
-          selectRegionIds: [...selectedSet],
-        })
-      }),
-    )
-
-    this.doubleClickOnViewerToggleLandmark$ = doubleClickOnViewer$.pipe(
-      filter(({ landmark }) => !!landmark),
-      withLatestFrom(viewerState$.pipe(
-        select('landmarksSelected'),
-        startWith([]),
-      )),
-      map(([{ landmark }, selectedSpatialDatas]) => {
-
-        const selectedIdx = selectedSpatialDatas.findIndex(data => data.name === landmark.name)
-
-        const newSelectedSpatialDatas = selectedIdx >= 0
-          ? selectedSpatialDatas.filter((_, idx) => idx !== selectedIdx)
-          : selectedSpatialDatas.concat(landmark)
-
-        return actionSelectLandmarks({
-          landmarks: newSelectedSpatialDatas,
-        })
-      }),
-    )
-
-    this.doubleClickOnViewerToogleUserLandmark$ = doubleClickOnViewer$.pipe(
-      filter(({ userLandmark }) => userLandmark),
-    )
-
-    this.onStandAloneVolumesExistCloseMatDrawer$ = viewerState$.pipe(
-      select('standaloneVolumes'),
-      filter(v => v && Array.isArray(v) && v.length > 0),
-      mapTo({
-        type: CLOSE_SIDE_PANEL
-      })
-    )
-  }
-
-  private currentLandmarks$: Observable<any[]>
-
-  @Effect()
-  public onStandAloneVolumesExistCloseMatDrawer$: Observable<any>
-
-  @Effect()
-  public mouseoverUserLandmarks: Observable<any>
-
-  @Effect()
-  public removeUserLandmarks: Observable<any>
-
-  @Effect()
-  public addUserLandmarks$: Observable<any>
-
-  @Effect()
-  public doubleClickOnViewerToggleRegions$: Observable<any>
-
-  @Effect()
-  public doubleClickOnViewerToggleLandmark$: Observable<any>
-
-  // @Effect()
-  public doubleClickOnViewerToogleUserLandmark$: Observable<any>
-}
-
-const ACTION_TYPES = {
-  REMOVE_USER_LANDMARKS: viewreStateRemoveUserLandmarks.type,
-
-  SINGLE_CLICK_ON_VIEWER: 'SINGLE_CLICK_ON_VIEWER',
-  DOUBLE_CLICK_ON_VIEWER: viewerStateDblClickOnViewer.type
-}
-
-export const VIEWERSTATE_ACTION_TYPES = ACTION_TYPES
diff --git a/src/services/state/viewerState/actions.ts b/src/services/state/viewerState/actions.ts
deleted file mode 100644
index d20ff0cd29af5855535d0ca27fc519e9204aa360..0000000000000000000000000000000000000000
--- a/src/services/state/viewerState/actions.ts
+++ /dev/null
@@ -1,146 +0,0 @@
-import { createAction, props } from "@ngrx/store"
-import { IRegion } from './constants'
-
-export const viewerStateNewViewer = createAction(
-  `[viewerState] newViewer`,
-  props<{
-    selectTemplate: any
-    selectParcellation: any
-    navigation?: any
-  }>()
-)
-
-export const viewerStateSetSelectedRegionsWithIds = createAction(
-  `[viewerState] setSelectedRegionsWithIds`,
-  props<{ selectRegionIds: string[] }>()
-)
-
-export const viewerStateSetSelectedRegions = createAction(
-  '[viewerState] setSelectedRegions',
-  props<{ selectRegions: IRegion[] }>()
-)
-
-export const viewerStateSetConnectivityRegion = createAction(
-  `[viewerState] setConnectivityRegion`,
-  props<{ connectivityRegion: any }>()
-)
-
-export const viewerStateNehubaLayerchanged = createAction(
-  `[viewerState] nehubaLayerChanged`,
-)
-
-export const viewerStateNavigateToRegion = createAction(
-  `[viewerState] navigateToRegion`,
-  props<{ payload: { region: any } }>()
-)
-
-export const viewerStateToggleRegionSelect = createAction(
-  `[viewerState] toggleRegionSelect`,
-  props<{ payload: { region: any } }>()
-)
-
-export const viewerStateSetFetchedAtlases = createAction(
-  '[viewerState] setFetchedatlases',
-  props<{ fetchedAtlases: any[] }>()
-)
-
-export const viewerStateSelectAtlas = createAction(
-  `[viewerState] selectAtlas`,
-  props<{
-    atlas: {
-      ['@id']: string
-      template?: {
-        ['@id']: string
-      }
-    }
-  }>()
-)
-
-export const viewerStateHelperSelectParcellationWithId = createAction(
-  `[viewerStateHelper] selectParcellationWithId`,
-  props<{ payload: { ['@id']: string } }>()
-)
-
-export const viewerStateSelectParcellation = createAction(
-  `[viewerState] selectParcellation`,
-  props<{ selectParcellation: any }>()
-)
-
-export const viewerStateSelectTemplateWithName = createAction(
-  `[viewerState] selectTemplateWithName`,
-  props<{ payload: { name: string } }>()
-)
-
-export const viewerStateSelectTemplateWithId = createAction(
-  `[viewerState] selectTemplateWithId`,
-  props<{ payload: { ['@id']: string }, config?: { selectParcellation: { ['@id']: string } } }>()
-)
-
-export const viewerStateToggleLayer = createAction(
-  `[viewerState] toggleLayer`,
-  props<{ payload: { ['@id']: string }  }>()
-)
-
-export const viewerStateRemoveAdditionalLayer = createAction(
-  `[viewerState] removeAdditionalLayer`,
-  props<{ payload?: { ['@id']: string } }>()
-)
-
-export const viewerStateSelectRegionWithIdDeprecated = createAction(
-  `[viewerState] [deprecated] selectRegionsWithId`,
-  props<{ selectRegionIds: string[] }>()
-)
-
-export const viewerStateSetViewerMode = createAction(
-  `[viewerState] setViewerMode`,
-  props<{payload: string}>()
-)
-
-export const viewerStateDblClickOnViewer = createAction(
-  `[viewerState] dblClickOnViewer`,
-  props<{ payload: { annotation: any, segments: any, landmark: any, userLandmark: any } }>()
-)
-
-export const viewerStateAddUserLandmarks = createAction(
-  `[viewerState] addUserlandmark,`,
-  props<{ landmarks: any[] }>()
-)
-
-export const viewreStateRemoveUserLandmarks = createAction(
-  `[viewerState] removeUserLandmarks`,
-  props<{ payload: { landmarkIds: string[] } }>()
-)
-
-export const viewerStateMouseOverCustomLandmark = createAction(
-  '[viewerState] mouseOverCustomLandmark',
-  props<{ payload: { userLandmark: any } }>()
-)
-
-export const viewerStateMouseOverCustomLandmarkInPerspectiveView = createAction(
-  `[viewerState] mouseOverCustomLandmarkInPerspectiveView`,
-  props<{ payload: { label: string } }>()
-)
-
-export const viewerStateChangeNavigation = createAction(
-  `[viewerState] changeNavigation`,
-  props<{ navigation: any }>()
-)
-
-export const actionSetMobileUi = createAction(
-  `[viewerState] setMobileUi`,
-  props<{ payload: { useMobileUI: boolean } }>()
-)
-
-export const actionAddToRegionsSelectionWithIds = createAction(
-  `[viewerState] addToRegionSelectionWithIds`,
-  props<{
-    selectRegionIds: string[]
-  }>()
-)
-
-export const actionSelectLandmarks = createAction(
-  `[viewerState] selectLandmarks`,
-  props<{
-    landmarks: any[]
-  }>()
-)
diff --git a/src/services/state/viewerState/constants.ts b/src/services/state/viewerState/constants.ts
deleted file mode 100644
index dbfa8c7800f0cc602fe1b1f942b7088552fe20ce..0000000000000000000000000000000000000000
--- a/src/services/state/viewerState/constants.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export interface IRegion{
-  name: string
-  [key: string]: string
-}
diff --git a/src/services/state/viewerState/selectors.spec.ts b/src/services/state/viewerState/selectors.spec.ts
deleted file mode 100644
index 6f13c133919aa50774b6e72eb15f2f872e1dfcc8..0000000000000000000000000000000000000000
--- a/src/services/state/viewerState/selectors.spec.ts
+++ /dev/null
@@ -1,247 +0,0 @@
-import {
-  viewerStateGetOverlayingAdditionalParcellations,
-  viewerStateAtlasParcellationSelector,
-  viewerStateAtlasLatestParcellationSelector,
-  viewerStateParcVersionSelector,
-} from './selectors'
-
-
-const atlas1 = {
-  '@id': 'atlas-1',
-  name: 'atlas-1-name',
-  templateSpaces: [
-    {
-      '@id': 'atlas-1-tmpl-1',
-      name: 'atlas-1-tmpl-1',
-      availableIn: [
-        {
-          '@id': 'atlas-1-parc-1',
-          name: 'atlas-1-parc-1'
-        },
-        {
-          '@id': 'atlas-1-parc-2',
-          name: 'atlas-1-parc-2'
-        }
-      ]
-    }
-  ],
-  parcellations: [
-    {
-      '@id': 'atlas-1-parc-1',
-      name: 'atlas-1-parc-1',
-      baseLayer: true
-    },
-    {
-      '@id': 'atlas-1-parc-2',
-      name: 'atlas-1-parc-2'
-    }
-  ]
-}
-
-const tmpl1 = {
-  '@id': 'atlas-1-tmpl-1',
-  name: 'atlas-1-tmpl-1',
-  parcellations: [
-    {
-      '@id': 'atlas-1-parc-1',
-      name: 'atlas-1-parc-1'
-    },
-    {
-      '@id': 'atlas-1-parc-2',
-      name: 'atlas-1-parc-2'
-    }
-  ]
-}
-
-const atlas2 = {
-  '@id': 'atlas-2',
-  name: 'atlas-2-name',
-  templateSpaces: [
-    {
-      '@id': 'atlas-2-tmpl-1',
-      name: 'atlas-2-tmpl-1',
-      availableIn: [
-        {
-          '@id': 'atlas-2-parc-1',
-          name: 'atlas-2-parc-1'
-        },
-        {
-          '@id': 'atlas-2-parc-2',
-          name: 'atlas-2-parc-2'
-        }
-      ]
-    },
-    {
-      '@id': 'atlas-2-tmpl-2',
-      name: 'atlas-2-tmpl-2',
-      availableIn: [
-        {
-          '@id': 'atlas-2-parc-1',
-          name: 'atlas-2-parc-1'
-        },
-        {
-          '@id': 'atlas-2-parc-2',
-          name: 'atlas-2-parc-2'
-        }
-      ]
-    }
-  ],
-  parcellations: [
-    {
-      '@id': 'atlas-2-parc-1',
-      name: 'atlas-2-parc-1',
-      "@version": {
-        "@next": "atlas-2-parc-2",
-        "@this": "atlas-2-parc-1",
-        "name": "atlas-2-parc-1",
-        "@previous": null
-      }
-    },
-    {
-      '@id': 'atlas-2-parc-2',
-      name: 'atlas-2-parc-2',
-      "@version": {
-        "@next": null,
-        "@this": "atlas-2-parc-2",
-        "name": "atlas-2-parc-2",
-        "@previous": "atlas-2-parc-1"
-      }
-    }
-  ]
-}
-
-const tmpl2 = {
-  '@id': 'atlas-2-tmpl-1',
-  name: 'atlas-2-tmpl-1',
-  parcellations: [
-    {
-      '@id': 'atlas-2-parc-1',
-      name: 'atlas-2-parc-1'
-    },
-    {
-      '@id': 'atlas-2-parc-2',
-      name: 'atlas-2-parc-2'
-    }
-  ]
-}
-
-
-const tmpl2_2 = {
-  '@id': 'atlas-2-tmpl-2',
-  name: 'atlas-2-tmpl-2',
-  parcellations: [
-    {
-      '@id': 'atlas-2-parc-1',
-      name: 'atlas-2-parc-1'
-    },
-    {
-      '@id': 'atlas-2-parc-2',
-      name: 'atlas-2-parc-2'
-    }
-  ]
-}
-
-const fetchedAtlases = [
-  atlas1,
-  atlas2
-]
-
-const fetchedTemplates = [
-  tmpl1,
-  tmpl2,
-  tmpl2_2
-]
-
-describe('viewerState/selector.ts', () => {
-  describe('> viewerStateGetOverlayingAdditionalParcellations', () => {
-    describe('> if atlas has no basic layer', () => {
-      it('> should return empty array', () => {
-
-        const parcs = viewerStateGetOverlayingAdditionalParcellations.projector({
-          fetchedAtlases,
-          selectedAtlasId: atlas2['@id']
-        }, {
-          parcellationSelected: tmpl2.parcellations[0]
-        })
-        
-        expect(parcs).toEqual([])
-      })
-    })
-
-    describe('> if atlas has basic layer', () => {
-      describe('> if non basiclayer is selected', () => {
-        it('> should return non empty array', () => {
-          const parc = atlas1.parcellations.find(p => !p['baseLayer'])
-          const parcs = viewerStateGetOverlayingAdditionalParcellations.projector({
-            fetchedAtlases,
-            selectedAtlasId: atlas1['@id']
-          }, {
-            parcellationSelected: parc
-          })
-          expect(parcs.length).toEqual(1)
-          expect(parcs[0]['@id']).toEqual(parc['@id'])
-        })
-      })
-
-      describe('> if basic layer is selected', () => {
-        it('> should return empty array', () => {
-          const parc = atlas1.parcellations.find(p => !!p['baseLayer'])
-          const parcs = viewerStateGetOverlayingAdditionalParcellations.projector({
-            fetchedAtlases,
-            selectedAtlasId: atlas1['@id']
-          }, {
-            parcellationSelected: parc
-          })
-          expect(parcs.length).toEqual(0)
-        })
-      })
-    })
-  })
-
-  describe('> viewerStateAtlasParcellationSelector', () => {
-    const check = (atlasJson, templates) => {
-
-      const parcs = viewerStateAtlasParcellationSelector.projector({
-        fetchedAtlases,
-        selectedAtlasId: atlasJson['@id']
-      }, {
-        fetchedTemplates
-      })
-      const templateParcs = []
-      for (const tmpl of templates) {
-        templateParcs.push(...tmpl.parcellations)
-      }
-      for (const parc of parcs) {
-        const firstHalf = templateParcs.find(p => p['@id'] === parc['@id'])
-        const secondHalf = atlasJson.parcellations.find(p => p['@id'] === parc['@id'])
-        expect(firstHalf).toBeTruthy()
-        expect(secondHalf).toBeTruthy()
-        //TODO compare strict equality of firsthalf+secondhalf with parc
-      }
-    }
-
-    it('> should work', () => {
-      check(atlas1, [tmpl1, tmpl2, tmpl2_2])
-      check(atlas2, [tmpl1, tmpl2, tmpl2_2])
-    })
-  })
-
-  describe('> viewerStateAtlasLatestParcellationSelector', () => {
-    it('> should only show 1 parc', () => {
-      const parcs = viewerStateAtlasLatestParcellationSelector.projector(atlas2.parcellations)
-      expect(parcs.length).toEqual(1)
-    })
-  })
-
-  describe('> viewerStateParcVersionSelector', () => {
-    it('> should work', () => {
-      const parcs = viewerStateParcVersionSelector.projector(atlas2.parcellations, {
-        parcellationSelected: atlas2.parcellations[0]
-      })
-      expect(parcs.length).toEqual(2)
-
-      expect(parcs[0]['@version']['@next']).toBeFalsy()
-      expect(parcs[parcs.length-1]['@version']['@previous']).toBeFalsy()
-    })
-  })
-})
\ No newline at end of file
diff --git a/src/services/state/viewerState/selectors.ts b/src/services/state/viewerState/selectors.ts
deleted file mode 100644
index 61cd2b7dd54d4f3275ea01c5aa63fbebe60e54f7..0000000000000000000000000000000000000000
--- a/src/services/state/viewerState/selectors.ts
+++ /dev/null
@@ -1,231 +0,0 @@
-import { createSelector } from "@ngrx/store"
-import { viewerStateHelperStoreName } from "../viewerState.store.helper"
-
-export const viewerStateSelectedRegionsSelector = createSelector(
-  state => state['viewerState'],
-  viewerState => viewerState['regionsSelected']
-)
-
-export const viewerStateCustomLandmarkSelector = createSelector(
-  state => state['viewerState'],
-  viewerState => viewerState['userLandmarks']
-)
-
-const flattenFetchedTemplatesIntoParcellationsReducer = (acc, curr) => {
-  const parcelations = (curr['parcellations'] || []).map(p => {
-    return {
-      ...p,
-      useTheme: curr['useTheme']
-    }
-  })
-
-  return acc.concat( parcelations )
-}
-
-export const viewerStateFetchedTemplatesSelector = createSelector(
-  state => state['viewerState'],
-  viewerState => viewerState['fetchedTemplates']
-)
-
-export const viewerStateSelectedTemplateSelector = createSelector(
-  state => state['viewerState'],
-  viewerState => viewerState?.['templateSelected']
-)
-
-export const viewerStateSelectorStandaloneVolumes = createSelector(
-  state => state['viewerState'],
-  viewerState => viewerState['standaloneVolumes']
-)
-
-/**
- * viewerStateSelectedTemplateSelector may have it navigation mutated to allow for initiliasation of viewer at the correct navigation
- * in some circumstances, it may be required to get the original navigation object
- */
-export const viewerStateSelectedTemplatePureSelector = createSelector(
-  viewerStateFetchedTemplatesSelector,
-  viewerStateSelectedTemplateSelector,
-  (fetchedTemplates, selectedTemplate) => {
-    if (!selectedTemplate) return null
-    return fetchedTemplates.find(t => t['@id'] === selectedTemplate['@id'])
-  }
-)
-
-export const viewerStateSelectedParcellationSelector = createSelector(
-  state => state['viewerState'],
-  viewerState => viewerState['parcellationSelected']
-)
-
-export const viewerStateNavigationStateSelector = createSelector(
-  state => state['viewerState'],
-  viewerState => viewerState['navigation']
-)
-
-export const viewerStateAllRegionsFlattenedRegionSelector = createSelector(
-  viewerStateSelectedParcellationSelector,
-  parc => {
-    const returnArr = []
-    const processRegion = region => {
-      const { children, ...rest } = region
-      returnArr.push({ ...rest })
-      region.children && Array.isArray(region.children) && region.children.forEach(processRegion)
-    }
-    if (parc && parc.regions && Array.isArray(parc.regions)) {
-      parc.regions.forEach(processRegion)
-    }
-    return returnArr
-  }
-)
-
-export const viewerStateOverwrittenColorMapSelector = createSelector(
-  state => state['viewerState'],
-  viewerState => viewerState['overwrittenColorMap']
-)
-
-export const viewerStateStandAloneVolumes = createSelector(
-  state => state['viewerState'],
-  viewerState => viewerState['standaloneVolumes']
-)
-
-export const viewerStateSelectorNavigation = createSelector(
-  state => state['viewerState'],
-  viewerState => viewerState['navigation']
-)
-
-export const viewerStateViewerModeSelector = createSelector(
-  state => state['viewerState'],
-  viewerState => viewerState['viewerMode']
-)
-
-export const viewerStateGetOverlayingAdditionalParcellations = createSelector(
-  state => state[viewerStateHelperStoreName],
-  state => state['viewerState'],
-  (viewerHelperState, viewerState ) => {
-    const { selectedAtlasId, fetchedAtlases } = viewerHelperState
-    const { parcellationSelected } = viewerState
-    const selectedAtlas = selectedAtlasId && fetchedAtlases.find(a => a['@id'] === selectedAtlasId)
-    const hasBaseLayer = selectedAtlas?.parcellations.find(p => p.baseLayer)
-    if (!hasBaseLayer) return []
-    const atlasLayer =  selectedAtlas?.parcellations.find(p => p['@id'] === (parcellationSelected && parcellationSelected['@id']))
-    const isBaseLayer = atlasLayer && atlasLayer.baseLayer
-    return (!!atlasLayer && !isBaseLayer) ? [{
-      ...(parcellationSelected || {} ),
-      ...atlasLayer
-    }] : []
-  }
-)
-
-export const viewerStateFetchedAtlasesSelector = createSelector(
-  state => state[viewerStateHelperStoreName],
-  helperState => helperState['fetchedAtlases']
-)
-
-export const viewerStateGetSelectedAtlas = createSelector(
-  state => state[viewerStateHelperStoreName],
-  helperState => {
-    if (!helperState) return null
-    const { selectedAtlasId, fetchedAtlases } = helperState
-    if (!selectedAtlasId) return null
-    return selectedAtlasId && fetchedAtlases.find(a => a['@id'] === selectedAtlasId)
-  }
-)
-
-export const viewerStateAtlasParcellationSelector = createSelector(
-  state => state[viewerStateHelperStoreName],
-  state => state['viewerState'],
-  (viewerHelperState, viewerState) => {
-    const { selectedAtlasId, fetchedAtlases } = viewerHelperState
-    const { fetchedTemplates } = viewerState
-
-    const allParcellations = fetchedTemplates.reduce(flattenFetchedTemplatesIntoParcellationsReducer, [])
-
-    const selectedAtlas = selectedAtlasId && fetchedAtlases.find(a => a['@id'] === selectedAtlasId)
-    const atlasLayers = selectedAtlas?.parcellations
-      .map(p => {
-        const otherHalfOfParc = allParcellations.find(parc => parc['@id'] === p['@id']) || {}
-        return {
-          ...p,
-          ...otherHalfOfParc,
-        }
-      })
-    return atlasLayers
-  }
-)
-
-export const viewerStateAtlasLatestParcellationSelector = createSelector(
-  viewerStateAtlasParcellationSelector,
-  parcs => (parcs && parcs.filter( p => !p['@version'] || !p['@version']['@next']) || [])
-)
-
-export const viewerStateParcVersionSelector = createSelector(
-  viewerStateAtlasParcellationSelector,
-  state => state['viewerState'],
-  (allAtlasParcellations, viewerState) => {
-    if (!viewerState || !viewerState.parcellationSelected) return []
-    const returnParc = []
-    const foundParc = allAtlasParcellations.find(p => p['@id'] === viewerState.parcellationSelected['@id'])
-    if (!foundParc) return []
-    returnParc.push(foundParc)
-    const traverseParc = parc => {
-      if (!parc) return []
-      if (!parc['@version']) return []
-      if (parc['@version']['@next']) {
-        const nextParc = allAtlasParcellations.find(p => p['@id'] === parc['@version']['@next'])
-        if (nextParc) {
-          const nextParcAlreadyIncluded = returnParc.find(p => p['@id'] === nextParc['@id'])
-          if (!nextParcAlreadyIncluded) {
-            returnParc.unshift(nextParc)
-            traverseParc(nextParc)
-          }
-        }
-      }
-
-      if (parc['@version']['@previous']) {
-        const previousParc = allAtlasParcellations.find(p => p['@id'] === parc['@version']['@previous'])
-        if (previousParc) {
-          const previousParcAlreadyIncluded = returnParc.find(p => p['@id'] === previousParc['@id'])
-          if (!previousParcAlreadyIncluded) {
-            returnParc.push(previousParc)
-            traverseParc(previousParc)
-          }
-        }
-      }
-    }
-    traverseParc(foundParc)
-    return returnParc
-  }
-)
-
-
-export const viewerStateSelectedTemplateFullInfoSelector = createSelector(
-  viewerStateGetSelectedAtlas,
-  viewerStateFetchedTemplatesSelector,
-  (selectedAtlas, fetchedTemplates) => {
-    if (!selectedAtlas) return null
-    const { templateSpaces } = selectedAtlas
-    return templateSpaces.map(templateSpace => {
-      const fullTemplateInfo = fetchedTemplates.find(t => t['@id'] === templateSpace['@id'])
-      return {
-        ...templateSpace,
-        ...(fullTemplateInfo || {}),
-        darktheme: (fullTemplateInfo || {}).useTheme === 'dark'
-      }
-    })
-  }
-)
-
-export const viewerStateContextedSelectedRegionsSelector = createSelector(
-  viewerStateSelectedRegionsSelector,
-  viewerStateGetSelectedAtlas,
-  viewerStateSelectedTemplatePureSelector,
-  viewerStateSelectedParcellationSelector,
-  (regions, atlas, template, parcellation) => regions.map(r => {
-    return {
-      ...r,
-      context: {
-        atlas,
-        template,
-        parcellation
-      }
-    }
-  })
-)
diff --git a/src/services/stateStore.helper.ts b/src/services/stateStore.helper.ts
deleted file mode 100644
index 172c58258dec163d118529afe22aeb3ef4af1ce4..0000000000000000000000000000000000000000
--- a/src/services/stateStore.helper.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { createAction, props } from "@ngrx/store";
-
-export const GENERAL_ACTION_TYPES = {
-  APPLY_STATE: 'APPLY_STATE',
-}
-
-export const generalApplyState = createAction(
-  GENERAL_ACTION_TYPES.APPLY_STATE,
-  props<{ state: any }>()
-)
-
-export const generalActionError = createAction(
-  `[generalActionError]`,
-  props<{ message: string }>()
-)
diff --git a/src/services/stateStore.service.spec.ts b/src/services/stateStore.service.spec.ts
deleted file mode 100644
index 1fb8e0058c9d33c10a4fa249d435e9f84c84f7ce..0000000000000000000000000000000000000000
--- a/src/services/stateStore.service.spec.ts
+++ /dev/null
@@ -1,144 +0,0 @@
-import { getMultiNgIdsRegionsLabelIndexMap } from './stateStore.service'
-
-const getRandomDummyData = () => Math.round(Math.random() * 1000).toString(16)
-
-const region1 = {
-  name: 'region 1',
-  labelIndex: 15,
-  ngId: 'left',
-  dummydata: getRandomDummyData()
-}
-const region2 = {
-  name: 'region 2',
-  labelIndex: 16,
-  ngId: 'right',
-  dummydata: getRandomDummyData()
-}
-const region3 = {
-  name: 'region 3',
-  labelIndex: 17,
-  ngId: 'right',
-  dummydata: getRandomDummyData()
-}
-
-const dummyParcellationWithNgId = {
-  name: 'dummy parcellation name',
-  regions: [
-    region1,
-    region2,
-    region3
-  ]
-}
-
-const dummyParcellationWithoutNgId = {
-  name: 'dummy parcellation name',
-  ngId: 'toplevel',
-  regions: [
-    region1,
-    region2,
-    region3
-  ].map(({ ngId, ...rest }) => {
-    return {
-      ...rest,
-      ...(ngId === 'left' ? { ngId } : {})
-    }
-  })
-}
-
-describe('stateStore.service.ts', () => {
-  describe('getMultiNgIdsRegionsLabelIndexMap', () => {
-    describe('should not mutate original regions', () => {
-      
-    })
-
-    describe('should populate map properly', () => {
-      const map = getMultiNgIdsRegionsLabelIndexMap(dummyParcellationWithNgId)
-      it('populated map should have 2 top level', () => {
-        expect(map.size).toBe(2)
-      })
-
-      it('should container left and right top level', () => {
-        expect(map.get('left')).toBeTruthy()
-        expect(map.get('right')).toBeTruthy()
-      })
-
-      it('left top level should have 1 member', () => {
-        const leftMap = map.get('left')
-        expect(leftMap.size).toBe(1)
-      })
-
-      it('left top level should map 15 => region1', () => {
-        const leftMap = map.get('left')
-        expect(leftMap.get(15)).toEqual(region1)
-      })
-
-      it('right top level should have 2 member', () => {
-        const rightMap = map.get('right')
-        expect(rightMap.size).toBe(2)
-      })
-
-      it('right top level should map 16 => region2, 17 => region3', () => {
-        const rightMap = map.get('right')
-        expect(rightMap.get(16)).toEqual(region2)
-        expect(rightMap.get(17)).toEqual(region3)
-      })
-    })
-
-    describe('should allow inheritance of ngId', () => {
-
-      const map = getMultiNgIdsRegionsLabelIndexMap(dummyParcellationWithoutNgId)
-      it('populated map should have 2 top level', () => {
-        expect(map.size).toBe(2)
-      })
-
-      it('should container left and right top level', () => {
-        expect(map.get('left')).toBeTruthy()
-        expect(map.get('toplevel')).toBeTruthy()
-      })
-
-      it('left top level should have 1 member', () => {
-        const leftMap = map.get('left')
-        expect(leftMap.size).toBe(1)
-      })
-
-      it('left top level should map 15 => region1', () => {
-        const leftMap = map.get('left')
-        expect(leftMap.get(15)).toEqual(region1)
-      })
-
-      it('toplevel top level should have 2 member', () => {
-        const toplevelMap = map.get('toplevel')
-        expect(toplevelMap.size).toBe(2)
-      })
-
-      it('toplevel top level should map 16 => region2, 17 => region3', () => {
-        const toplevelMap = map.get('toplevel')
-        expect(toplevelMap.get(16).dummydata).toEqual(region2.dummydata)
-        expect(toplevelMap.get(17).dummydata).toEqual(region3.dummydata)
-      })
-    })
-
-    describe('should allow inheritance of attr when specified', () => {
-      const attr = {
-        dummyattr: 'default dummy attr'
-      }
-      const map = getMultiNgIdsRegionsLabelIndexMap({
-        ...dummyParcellationWithNgId,
-        dummyattr: 'p dummy attr'
-      }, attr)
-      it('every region should have dummyattr set properly', () => {
-        let regions = []
-        for (const [ _key1, mMap ] of Array.from(map)) {
-          for (const [_key2, rs] of Array.from(mMap)) {
-            regions = [...regions, rs]
-          }
-        }
-        
-        for (const r of regions) {
-          expect(r.dummyattr).toEqual('p dummy attr')
-        }
-      })
-
-    })
-  })
-})
\ No newline at end of file
diff --git a/src/services/stateStore.service.ts b/src/services/stateStore.service.ts
deleted file mode 100644
index b9d57ed8c8b50e314553449914360265b99178e2..0000000000000000000000000000000000000000
--- a/src/services/stateStore.service.ts
+++ /dev/null
@@ -1,169 +0,0 @@
-import { filter } from 'rxjs/operators';
-
-export {
-  recursiveFindRegionWithLabelIndexId
-} from 'src/util/fn'
-
-export { getNgIds } from 'src/util/fn'
-
-import {
-  ActionInterface as NgViewerActionInterface,
-  defaultState as ngViewerDefaultState,
-  StateInterface as NgViewerStateInterface,
-  stateStore as ngViewerState,
-} from './state/ngViewerState.store'
-import {
-  defaultState as pluginDefaultState,
-  StateInterface as PluginStateInterface,
-  stateStore as pluginState,
-} from './state/pluginState.store'
-import {
-  ActionInterface as UIActionInterface,
-  defaultState as uiDefaultState,
-  IUiState,
-  stateStore as uiState,
-} from './state/uiState.store'
-import {
-  ACTION_TYPES as USER_CONFIG_ACTION_TYPES,
-  defaultState as userConfigDefaultState,
-  StateInterface as UserConfigStateInterface,
-  userConfigReducer as userConfigState,
-} from './state/userConfigState.store'
-import {
-  defaultState as viewerConfigDefaultState,
-  StateInterface as ViewerConfigStateInterface,
-  stateStore as viewerConfigState,
-} from './state/viewerConfig.store'
-import {
-  ActionInterface as ViewerActionInterface,
-  defaultState as viewerDefaultState,
-  StateInterface as ViewerStateInterface,
-  stateStore as viewerState,
-} from './state/viewerState.store'
-
-import { 
-  defaultState as defaultViewerHelperState,
-  viewerStateHelperStoreName
-} from './state/viewerState.store.helper'
-
-export { pluginState }
-export { viewerConfigState }
-export { NgViewerStateInterface, NgViewerActionInterface, ngViewerState }
-export { ViewerStateInterface, ViewerActionInterface, viewerState }
-export { IUiState, UIActionInterface, uiState }
-export { userConfigState,  USER_CONFIG_ACTION_TYPES}
-
-export { CHANGE_NAVIGATION, DESELECT_LANDMARKS, FETCHED_TEMPLATE, SELECT_PARCELLATION, SELECT_REGIONS, USER_LANDMARKS } from './state/viewerState.store'
-export { IDataEntry, IParcellationRegion, FETCHED_DATAENTRIES, FETCHED_SPATIAL_DATA, ILandmark, IOtherLandmarkGeometry, IPlaneLandmarkGeometry, IPointLandmarkGeometry, IProperty, IPublication, IReferenceSpace, IFile, IFileSupplementData } from './state/dataStore.store'
-export { CLOSE_SIDE_PANEL, MOUSE_OVER_SEGMENT, OPEN_SIDE_PANEL, COLLAPSE_SIDE_PANEL_CURRENT_VIEW, EXPAND_SIDE_PANEL_CURRENT_VIEW } from './state/uiState.store'
-export { UserConfigStateUseEffect } from './state/userConfigState.store'
-
-export { GENERAL_ACTION_TYPES, generalActionError } from './stateStore.helper'
-
-// TODO deprecate
-export function safeFilter(key: string) {
-  return filter((state: any) =>
-    (typeof state !== 'undefined' && state !== null) &&
-    typeof state[key] !== 'undefined' && state[key] !== null)
-}
-
-export function getMultiNgIdsRegionsLabelIndexMap(parcellation: any = {}, inheritAttrsOpt: any = { ngId: 'root' }): Map<string, Map<number, any>> {
-  const map: Map<string, Map<number, any>> = new Map()
-  
-  const inheritAttrs = Object.keys(inheritAttrsOpt)
-  if (inheritAttrs.indexOf('children') >=0 ) throw new Error(`children attr cannot be inherited`)
-
-  const processRegion = (region: any) => {
-    const { ngId: rNgId } = region
-    const existingMap = map.get(rNgId)
-    const labelIndex = Number(region.labelIndex)
-    if (labelIndex) {
-      if (!existingMap) {
-        const newMap = new Map()
-        newMap.set(labelIndex, region)
-        map.set(rNgId, newMap)
-      } else {
-        existingMap.set(labelIndex, region)
-      }
-    }
-
-    if (region.children && Array.isArray(region.children)) {
-      for (const r of region.children) {
-        const copiedRegion = { ...r }
-        for (const attr of inheritAttrs){
-          copiedRegion[attr] = copiedRegion[attr] || region[attr] || parcellation[attr]
-        }
-        processRegion(copiedRegion)
-      }
-    }
-  }
-
-  if (!parcellation) throw new Error(`parcellation needs to be defined`)
-  if (!parcellation.regions) throw new Error(`parcellation.regions needs to be defined`)
-  if (!Array.isArray(parcellation.regions)) throw new Error(`parcellation.regions needs to be an array`)
-
-  for (const region of parcellation.regions){
-    const copiedregion = { ...region }
-    for (const attr of inheritAttrs){
-      copiedregion[attr] = copiedregion[attr] || parcellation[attr]
-    }
-    processRegion(copiedregion)
-  }
-
-  return map
-}
-
-/**
- * labelIndexMap maps label index to region
- * @TODO deprecate
- */
-export function getLabelIndexMap(regions: any[]): Map<number, any> {
-  const returnMap = new Map()
-
-  const reduceRegions = (rs: any[]) => {
-    rs.forEach(region => {
-      if ( region.labelIndex ) { returnMap.set(Number(region.labelIndex),
-        Object.assign({}, region, {labelIndex : Number(region.labelIndex)}))
-      }
-      if ( region.children && region.children.constructor === Array ) { reduceRegions(region.children) }
-    })
-  }
-
-  if (regions && regions.forEach) { reduceRegions(regions) }
-  return returnMap
-}
-
-/**
- *
- * @param regions regions to deep iterate to find all ngId 's, filtering out falsy values
- * n.b. returns non unique list
- */
-export interface DedicatedViewState {
-  dedicatedView: string | null
-}
-
-// @TODO deprecate
-export function isDefined(obj) {
-  return typeof obj !== 'undefined' && obj !== null
-}
-
-export interface IavRootStoreInterface {
-  pluginState: PluginStateInterface
-  viewerConfigState: ViewerConfigStateInterface
-  ngViewerState: NgViewerStateInterface
-  viewerState: ViewerStateInterface
-  dataStore: any
-  uiState: IUiState
-  userConfigState: UserConfigStateInterface
-}
-
-export const defaultRootState: any = {
-  pluginState: pluginDefaultState,
-  dataStore: {},
-  ngViewerState: ngViewerDefaultState,
-  uiState: uiDefaultState,
-  userConfigState: userConfigDefaultState,
-  viewerConfigState: viewerConfigDefaultState,
-  viewerState: viewerDefaultState,
-  [viewerStateHelperStoreName]: defaultViewerHelperState
-}
diff --git a/src/services/templateCoordinatesTransformation.service.spec.ts b/src/services/templateCoordinatesTransformation.service.spec.ts
deleted file mode 100644
index 0aa477d103f4dfe8b0894314e4a08bfde4666cfb..0000000000000000000000000000000000000000
--- a/src/services/templateCoordinatesTransformation.service.spec.ts
+++ /dev/null
@@ -1,139 +0,0 @@
-import { TemplateCoordinatesTransformation } from './templateCoordinatesTransformation.service'
-import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'
-import { TestBed, fakeAsync, tick } from '@angular/core/testing'
-
-describe('templateCoordinatesTransformation.service.spec.ts', () => {
-  describe('TemplateCoordinatesTransformation', () => {
-    beforeEach(() => {
-      TestBed.configureTestingModule({
-        imports: [
-          HttpClientTestingModule
-        ],
-        providers: [
-          TemplateCoordinatesTransformation
-        ]
-      })
-    })
-
-    afterEach(() => {
-      const ctrl = TestBed.inject(HttpTestingController)
-      ctrl.verify()
-    })
-
-    describe('getPointCoordinatesForTemplate', () => {
-      it('should instantiate service properly', () => {
-        const service = TestBed.inject(TemplateCoordinatesTransformation)
-        expect(service).toBeTruthy()
-        expect(service.getPointCoordinatesForTemplate).toBeTruthy()
-      })
-
-      it('should transform argument properly', () => {
-        const service = TestBed.inject(TemplateCoordinatesTransformation)
-        const httpTestingController = TestBed.inject(HttpTestingController)
-
-        // subscriptions are necessary for http fetch to occur
-        service.getPointCoordinatesForTemplate(
-          TemplateCoordinatesTransformation.VALID_TEMPLATE_SPACE_NAMES.MNI152,
-          TemplateCoordinatesTransformation.VALID_TEMPLATE_SPACE_NAMES.BIG_BRAIN,
-          [1,2,3]
-        ).subscribe((_ev) => {
-          
-        })
-        const req = httpTestingController.expectOne(service.url)
-        expect(req.request.method).toEqual('POST')
-        expect(
-          JSON.parse(req.request.body)
-        ).toEqual({
-          'source_space': TemplateCoordinatesTransformation.VALID_TEMPLATE_SPACE_NAMES.MNI152,
-          'target_space': TemplateCoordinatesTransformation.VALID_TEMPLATE_SPACE_NAMES.BIG_BRAIN,
-          'source_points': [
-            [1e-6, 2e-6, 3e-6]
-          ]
-        })
-        req.flush({})
-      })
-
-      it('transforms mapped space name(s)', () => {
-        const service = TestBed.inject(TemplateCoordinatesTransformation)
-        const httpTestingController = TestBed.inject(HttpTestingController)
-
-        const key = Array.from(TemplateCoordinatesTransformation.NameMap.keys())[0]
-        service.getPointCoordinatesForTemplate(
-          key,
-          TemplateCoordinatesTransformation.VALID_TEMPLATE_SPACE_NAMES.BIG_BRAIN,
-          [1,2,3]
-        ).subscribe((_ev) => {
-          
-        })
-        const req = httpTestingController.expectOne(service.url)
-        expect(req.request.method).toEqual('POST')
-        expect(
-          JSON.parse(req.request.body)
-        ).toEqual({
-          'source_space': TemplateCoordinatesTransformation.NameMap.get(key),
-          'target_space': TemplateCoordinatesTransformation.VALID_TEMPLATE_SPACE_NAMES.BIG_BRAIN,
-          'source_points': [
-            [1e-6, 2e-6, 3e-6]
-          ]
-        })
-        req.flush({})
-      })
-
-
-      it('should transform response properly', () => {
-
-        const service = TestBed.inject(TemplateCoordinatesTransformation)
-        const httpTestingController = TestBed.inject(HttpTestingController)
-
-        service.getPointCoordinatesForTemplate(
-          TemplateCoordinatesTransformation.VALID_TEMPLATE_SPACE_NAMES.MNI152,
-          TemplateCoordinatesTransformation.VALID_TEMPLATE_SPACE_NAMES.BIG_BRAIN,
-          [1,2,3]
-        ).subscribe(({ status, result }) => {
-          expect(status).toEqual('completed')
-          expect(result).toEqual([1e6, 2e6, 3e6])
-        })
-        const req = httpTestingController.expectOne(service.url)
-        req.flush({
-          'target_points':[
-            [1, 2, 3]
-          ]
-        })
-      })
-
-      it('if server returns >=400, fallback gracefully', () => {
-        const service = TestBed.inject(TemplateCoordinatesTransformation)
-        const httpTestingController = TestBed.inject(HttpTestingController)
-
-        service.getPointCoordinatesForTemplate(
-          TemplateCoordinatesTransformation.VALID_TEMPLATE_SPACE_NAMES.MNI152,
-          TemplateCoordinatesTransformation.VALID_TEMPLATE_SPACE_NAMES.BIG_BRAIN,
-          [1,2,3]
-        ).subscribe(({ status }) => {
-          expect(status).toEqual('error')
-        })
-        const req = httpTestingController.expectOne(service.url)
-        
-        req.flush('intercepted', { status: 500, statusText: 'internal server error' })
-      })
-
-      it('if server does not respond after 3s, fallback gracefully', fakeAsync(() => {
-
-        const service = TestBed.inject(TemplateCoordinatesTransformation)
-        const httpTestingController = TestBed.inject(HttpTestingController)
-
-        service.getPointCoordinatesForTemplate(
-          TemplateCoordinatesTransformation.VALID_TEMPLATE_SPACE_NAMES.MNI152,
-          TemplateCoordinatesTransformation.VALID_TEMPLATE_SPACE_NAMES.BIG_BRAIN,
-          [1,2,3]
-        ).subscribe(({ status, statusText }) => {
-          expect(status).toEqual('error')
-          expect(statusText).toEqual(`Timeout after 3s`)
-        })
-        const req = httpTestingController.expectOne(service.url)
-        tick(4000)
-        expect(req.cancelled).toBe(true)
-      }))
-    })
-  })
-})
diff --git a/src/services/templateCoordinatesTransformation.service.ts b/src/services/templateCoordinatesTransformation.service.ts
deleted file mode 100644
index c9fcf3a9d5b191ecf9acc3b68890f06cfceb549c..0000000000000000000000000000000000000000
--- a/src/services/templateCoordinatesTransformation.service.ts
+++ /dev/null
@@ -1,77 +0,0 @@
-import {Injectable} from "@angular/core";
-import {HttpClient, HttpHeaders, HttpErrorResponse} from "@angular/common/http";
-import { catchError, timeout, map } from "rxjs/operators";
-import { of, Observable } from "rxjs";
-import { environment } from 'src/environments/environment'
-
-export interface ITemplateCoordXformResp{
-  status: 'pending' | 'error' | 'completed'
-  statusText?: string
-  result? : [number, number, number]
-}
-
-@Injectable({
-  providedIn: 'root',
-})
-export class TemplateCoordinatesTransformation {
-
-  static VALID_TEMPLATE_SPACE_NAMES = {
-    MNI152: 'MNI 152 ICBM 2009c Nonlinear Asymmetric',
-    COLIN27: 'MNI Colin 27',
-    BIG_BRAIN: 'Big Brain (Histology)',
-    INFANT: 'Infant Atlas',
-  }
-
-  static NameMap = new Map([
-    ['MNI152 2009c nonl asym', TemplateCoordinatesTransformation.VALID_TEMPLATE_SPACE_NAMES.MNI152],
-    ["Big Brain", TemplateCoordinatesTransformation.VALID_TEMPLATE_SPACE_NAMES.BIG_BRAIN]
-  ])
-
-  constructor(private httpClient: HttpClient) {}
-
-  public url = `${environment.SPATIAL_TRANSFORM_BACKEND.replace(/\/$/, '')}/v1/transform-points`
-
-  // jasmine marble cannot test promise properly
-  // see https://github.com/ngrx/platform/issues/498#issuecomment-337465179
-  // in order to properly test with marble, use obs instead of promise
-  getPointCoordinatesForTemplate(sourceTemplateName: string, targetTemplateName: string, coordinatesInNm: [number, number, number]): Observable<ITemplateCoordXformResp> {
-    const httpOptions = {
-      headers: new HttpHeaders({
-        'Content-Type':  'application/json'
-      })
-    }
-    const srcTmplName = Object.values(TemplateCoordinatesTransformation.VALID_TEMPLATE_SPACE_NAMES).includes(sourceTemplateName)
-      ? sourceTemplateName
-      : TemplateCoordinatesTransformation.NameMap.get(sourceTemplateName)
-
-    const targetTmplName = Object.values(TemplateCoordinatesTransformation.VALID_TEMPLATE_SPACE_NAMES).includes(targetTemplateName)
-      ? targetTemplateName
-      : TemplateCoordinatesTransformation.NameMap.get(targetTemplateName)
-    
-    return this.httpClient.post(
-      this.url,
-      JSON.stringify({
-        'source_points': [[...coordinatesInNm.map(c => c/1e6)]],
-        'source_space': srcTmplName,
-        'target_space': targetTmplName
-      }),
-      httpOptions
-    ).pipe(
-      map(resp => {
-        return {
-          status: 'completed',
-          result: resp['target_points'][0].map((r: number)=> r * 1e6)
-        } as ITemplateCoordXformResp
-      }),
-      catchError(err => {
-        if (err instanceof HttpErrorResponse) {
-          return of(({ status: 'error', statusText: err.message } as ITemplateCoordXformResp))
-        } else {
-          return of(({ status: 'error', statusText: err.toString() } as ITemplateCoordXformResp))
-        }
-      }),
-      timeout(3000),
-      catchError(() => of(({ status: 'error', statusText: `Timeout after 3s` } as ITemplateCoordXformResp))),
-    )
-  }
-}
\ No newline at end of file
diff --git a/src/share/share.directive.ts b/src/share/share.directive.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e3200cba6816e4182048d4b629f9f9c6e5fe56ef
--- /dev/null
+++ b/src/share/share.directive.ts
@@ -0,0 +1,18 @@
+import { Directive, HostListener } from "@angular/core";
+import { MatBottomSheet } from "@angular/material/bottom-sheet";
+import { ShareSheetComponent } from "./shareSheet/shareSheet.component"
+
+
+@Directive({
+  selector: `[sxplr-share-view]`
+})
+
+export class ShareDirective{
+  constructor(private btmSht: MatBottomSheet){
+
+  }
+  @HostListener('click')
+  onClick(){
+    this.btmSht.open(ShareSheetComponent)
+  }
+}
diff --git a/src/share/share.module.ts b/src/share/share.module.ts
index 9634e12279dd09f165c6a6d4fd34653cbf52550f..f3137eaadc83a3aaead101efe1b76cc600301409 100644
--- a/src/share/share.module.ts
+++ b/src/share/share.module.ts
@@ -6,6 +6,8 @@ import { SaneUrl } from "./saneUrl/saneUrl.component";
 import { CommonModule } from "@angular/common";
 import { ReactiveFormsModule, FormsModule } from "@angular/forms";
 import { AuthModule } from "src/auth";
+import { ShareSheetComponent } from "./shareSheet/shareSheet.component";
+import { ShareDirective } from "./share.directive";
 
 @NgModule({
   imports: [
@@ -19,10 +21,14 @@ import { AuthModule } from "src/auth";
   declarations: [
     ClipboardCopy,
     SaneUrl,
+    ShareSheetComponent,
+    ShareDirective,
   ],
   exports: [
     ClipboardCopy,
     SaneUrl,
+    ShareSheetComponent,
+    ShareDirective,
   ]
 })
 
diff --git a/src/share/shareSheet/shareSheet.component.ts b/src/share/shareSheet/shareSheet.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8d4b9f963ebafc0b83850010bf8b18e70f7d6a15
--- /dev/null
+++ b/src/share/shareSheet/shareSheet.component.ts
@@ -0,0 +1,24 @@
+import { Component } from "@angular/core";
+import { MatDialog } from "@angular/material/dialog";
+import { ARIA_LABELS } from 'common/constants'
+import { SaneUrl } from "../saneUrl/saneUrl.component"
+
+@Component({
+  selector: 'sxplr-share-sheet',
+  templateUrl: './shareSheet.template.html',
+  styleUrls: [
+    `./shareSheet.style.css`
+  ]
+})
+
+export class ShareSheetComponent{
+  public ARIA_LABELS = ARIA_LABELS
+
+  constructor(private dialog: MatDialog){
+
+  }
+
+  openShareSaneUrl(){
+    this.dialog.open(SaneUrl, { ariaLabel: ARIA_LABELS.SHARE_CUSTOM_URL })
+  }
+}
diff --git a/src/share/shareSheet/shareSheet.style.css b/src/share/shareSheet/shareSheet.style.css
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/share/shareSheet/shareSheet.template.html b/src/share/shareSheet/shareSheet.template.html
new file mode 100644
index 0000000000000000000000000000000000000000..cea5242302cf57ff1cc718ac1b2d98b7a9d79f0c
--- /dev/null
+++ b/src/share/shareSheet/shareSheet.template.html
@@ -0,0 +1,34 @@
+<h4 class="mat-h4">
+  Share via
+</h4>
+<mat-nav-list>
+  <mat-list-item iav-clipboard-copy
+    [attr.aria-label]="ARIA_LABELS.SHARE_COPY_URL_CLIPBOARD"
+    [attr.tab-index]="10">
+    <mat-icon
+      class="mr-4"
+      fontSet="fas"
+      fontIcon="fa-copy">
+    </mat-icon>
+    <span>
+      Copy link to this view
+    </span>
+  </mat-list-item>
+  <mat-list-item
+    [attr.aria-label]="ARIA_LABELS.SHARE_CUSTOM_URL"
+    [attr.tab-index]="10"
+    (click)="openShareSaneUrl()"
+    [matTooltip]="ARIA_LABELS.SHARE_CUSTOM_URL"
+    >
+    <mat-icon
+      class="mr-4"
+      fontSet="fas"
+      fontIcon="fa-link">
+    </mat-icon>
+
+    <span>
+      Create custom URL
+    </span>
+
+  </mat-list-item>
+</mat-nav-list>
diff --git a/src/sharedModules/angularMaterial.module.ts b/src/sharedModules/angularMaterial.module.ts
index d5384113b07877cc3d0c0a726387c2c38d5e64bf..6f29b8913a23db32f47c96f936d0d043dde498e6 100644
--- a/src/sharedModules/angularMaterial.module.ts
+++ b/src/sharedModules/angularMaterial.module.ts
@@ -28,11 +28,16 @@ import {MatMenuModule} from "@angular/material/menu";
 import { MatToolbarModule } from '@angular/material/toolbar'
 
 import { ClipboardModule } from '@angular/cdk/clipboard'
+import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
+import {MatProgressBarModule} from "@angular/material/progress-bar";
+import {MatProgressSpinnerModule} from "@angular/material/progress-spinner";
 
 const defaultDialogOption: MatDialogConfig = new MatDialogConfig()
 
 @NgModule({
   imports: [
+    BrowserAnimationsModule,
+    
     MatButtonModule,
     MatSnackBarModule,
     MatCheckboxModule,
@@ -60,6 +65,8 @@ const defaultDialogOption: MatDialogConfig = new MatDialogConfig()
     ScrollingModule,
     MatToolbarModule,
     ClipboardModule,
+    MatProgressBarModule,
+    MatProgressSpinnerModule,
   ],
   exports: [
     MatButtonModule,
@@ -89,12 +96,13 @@ const defaultDialogOption: MatDialogConfig = new MatDialogConfig()
     ScrollingModule,
     MatToolbarModule,
     ClipboardModule,
+    MatProgressBarModule,
+    MatProgressSpinnerModule,
   ],
   providers: [{
     provide: MAT_DIALOG_DEFAULT_OPTIONS,
     useValue: {
       ...defaultDialogOption,
-      panelClass: 'iav-dialog-class',
     },
   }],
 })
diff --git a/src/spotlight/spot-light.module.ts b/src/spotlight/spot-light.module.ts
index 6ae55158469a7ac8a575697a4e477d12de6a352e..a485e0d23622a2d4637334154cc6d6c0b9207c97 100644
--- a/src/spotlight/spot-light.module.ts
+++ b/src/spotlight/spot-light.module.ts
@@ -15,11 +15,8 @@ import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
     BrowserAnimationsModule,
     CommonModule
   ],
-  exports:[
+  exports: [
     SlSpotlightDirective
-  ],
-  entryComponents: [
-    SpotlightBackdropComponent
   ]
 })
 export class SpotLightModule { }
diff --git a/src/state/actions.ts b/src/state/actions.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2b29757e099d292ee870c3115a26e906a814b7d2
--- /dev/null
+++ b/src/state/actions.ts
@@ -0,0 +1,16 @@
+import { createAction, props } from "@ngrx/store";
+import { MainState, nameSpace } from "./const"
+
+export const generalActionError = createAction(
+  `${nameSpace} generalActionError`,
+  props<{
+    message: string
+  }>()
+)
+
+export const generalApplyState = createAction(
+  `${nameSpace} generalApplyState`,
+  props<{
+    state: MainState
+  }>()
+)
diff --git a/src/state/annotations/actions.ts b/src/state/annotations/actions.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8527aaaf3ee8c8271868a8844113f52799ad9bdf
--- /dev/null
+++ b/src/state/annotations/actions.ts
@@ -0,0 +1,21 @@
+import { createAction, props } from "@ngrx/store"
+import { nameSpace } from "./const"
+import { UnionAnnotation } from "./store"
+
+export const clearAllAnnotations = createAction(
+  `${nameSpace} clearAllAnnotations`
+)
+
+export const rmAnnotations = createAction(
+  `${nameSpace} rmAnnotations`,
+  props<{
+    annotations: {'@id': string }[]
+  }>()
+)
+
+export const addAnnotations = createAction(
+  `${nameSpace} addAnnotations`,
+  props<{
+    annotations: UnionAnnotation[]
+  }>()
+)
diff --git a/src/state/annotations/const.ts b/src/state/annotations/const.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0ca3a95036366f410633ade16db8d2a4e1665064
--- /dev/null
+++ b/src/state/annotations/const.ts
@@ -0,0 +1 @@
+export const nameSpace = `[state.annotations]`
\ No newline at end of file
diff --git a/src/state/annotations/index.ts b/src/state/annotations/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..13a9be8ec3d7ce59ed27481285119c496fce0f63
--- /dev/null
+++ b/src/state/annotations/index.ts
@@ -0,0 +1,4 @@
+export * as actions from "./actions"
+export { Annotation, AnnotationState, reducer, defaultState, TypesOfDetailedAnnotations, UnionAnnotation, AnnotationColor } from "./store"
+export { nameSpace } from "./const"
+export * as selectors from "./selectors"
diff --git a/src/state/annotations/selectors.ts b/src/state/annotations/selectors.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5504a6bb1fb7ab03a4053a365c24d55290a50036
--- /dev/null
+++ b/src/state/annotations/selectors.ts
@@ -0,0 +1,32 @@
+import { createSelector } from "@ngrx/store"
+import { nameSpace } from "./const"
+import { Annotation, AnnotationState } from "./store"
+import { selectors as atlasSelectionSelectors } from "../atlasSelection"
+
+const selectStore = state => state[nameSpace] as AnnotationState
+
+export const annotations = createSelector(
+  selectStore,
+  state => state.annotations
+)
+
+export const spaceFilteredAnnotations = createSelector(
+  selectStore,
+  atlasSelectionSelectors.selectStore,
+  (annState, atlasSelState) => annState.annotations.filter(ann => {
+    const spaceId = atlasSelState.selectedTemplate['@id']
+    if (ann['openminds']) {
+      return (ann as Annotation<'openminds'>).openminds.coordinateSpace['@id'] === spaceId
+    }
+
+    if (ann['line']) {
+      return (ann as Annotation<'line'>).line.pointA.coordinateSpace['@id'] === spaceId
+        && (ann as Annotation<'line'>).line.pointB.coordinateSpace['@id'] === spaceId
+    }
+
+    if (ann['box']) {
+      return (ann as Annotation<'box'>).box.pointA.coordinateSpace['@id'] === spaceId
+        &&  (ann as Annotation<'box'>).box.pointB.coordinateSpace['@id'] === spaceId
+    }
+  })
+)
diff --git a/src/state/annotations/store.ts b/src/state/annotations/store.ts
new file mode 100644
index 0000000000000000000000000000000000000000..21198ff816f5315721746a0cbeba8d8f55c90d47
--- /dev/null
+++ b/src/state/annotations/store.ts
@@ -0,0 +1,72 @@
+import { createReducer, on } from "@ngrx/store"
+import { OpenMINDSCoordinatePoint } from "src/atlasComponents/sapi"
+import * as actions from "./actions"
+
+type Line = {
+  pointA: OpenMINDSCoordinatePoint
+  pointB: OpenMINDSCoordinatePoint
+}
+
+type BBox = {
+  pointA: OpenMINDSCoordinatePoint
+  pointB: OpenMINDSCoordinatePoint
+}
+
+export type TypesOfDetailedAnnotations = {
+  openminds: OpenMINDSCoordinatePoint
+  line: Line
+  box: BBox
+}
+
+export enum AnnotationColor {
+  WHITE="WHITE",
+  RED="RED",
+  BLUE="BLUE",
+}
+
+export type Annotation<T extends keyof TypesOfDetailedAnnotations> = {
+  "@id": string
+  name: string
+  description?: string
+  color?: AnnotationColor
+} & { [key in T] : TypesOfDetailedAnnotations[T]}
+
+export type UnionAnnotation = Annotation<'openminds'> | Annotation<'line'> | Annotation<'box'>
+
+export type AnnotationState = {
+  annotations: UnionAnnotation[]
+}
+
+export const defaultState: AnnotationState = {
+  annotations: []
+}
+
+const reducer = createReducer(
+  defaultState,
+  on(
+    actions.addAnnotations,
+    (state, { annotations }) => {
+      return {
+        ...state,
+        annotations: [
+          ...state.annotations,
+          ...annotations,
+        ]
+      }
+    }
+  ),
+  on(
+    actions.rmAnnotations,
+    (state, { annotations }) => {
+      const annIdToBeRemoved = annotations.map(ann => ann["@id"])
+      return {
+        ...state,
+        annotations: state.annotations.filter(ann => !annIdToBeRemoved.includes(ann["@id"]) )
+      }
+    }
+  )
+)
+
+export {
+  reducer
+}
diff --git a/src/state/atlasAppearance/action.ts b/src/state/atlasAppearance/action.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6a4ce26e148d801c726ba23c3eee906e736aa510
--- /dev/null
+++ b/src/state/atlasAppearance/action.ts
@@ -0,0 +1,30 @@
+import { createAction, props } from "@ngrx/store";
+import { CustomLayer, nameSpace } from "./const"
+
+export const setOctantRemoval = createAction(
+  `${nameSpace} setOctantRemoval`,
+  props<{
+    flag: boolean
+  }>()
+)
+
+export const setShowDelineation = createAction(
+  `${nameSpace} setShowDelineation`,
+  props<{
+    flag: boolean
+  }>()
+)
+
+export const addCustomLayer = createAction(
+  `${nameSpace} addCustomLayer`,
+  props<{
+    customLayer: CustomLayer
+  }>()
+)
+
+export const removeCustomLayer = createAction(
+  `${nameSpace} removeCustomLayer`,
+  props<{
+    id: string
+  }>()
+)
diff --git a/src/state/atlasAppearance/const.ts b/src/state/atlasAppearance/const.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c6c24f31b6ffc7e8bebf9647f9b0b4f538c0f710
--- /dev/null
+++ b/src/state/atlasAppearance/const.ts
@@ -0,0 +1,52 @@
+import { SapiRegionModel } from "src/atlasComponents/sapi"
+export const nameSpace = `[state.atlasAppearance]`
+
+type CustomLayerBase = {
+  id: string
+}
+
+export type ColorMapCustomLayer = {
+  clType: 'customlayer/colormap' | 'baselayer/colormap'
+  colormap: WeakMap<SapiRegionModel, number[]>
+} & CustomLayerBase
+
+export type ThreeSurferCustomLayer = {
+  clType: 'baselayer/threesurfer'
+  source: string
+  laterality: 'left' | 'right'
+  name: string
+} & CustomLayerBase
+
+export type ThreeSurferCustomLabelLayer = {
+  clType: 'baselayer/threesurfer-label'
+  source: string
+  laterality: 'left' | 'right'
+} & CustomLayerBase
+
+export type NgLayerCustomLayer = {
+  clType: 'customlayer/nglayer' | 'baselayer/nglayer'
+  source: string
+  visible?: boolean
+  shader?: string
+  transform?: number[][]
+  opacity?: number
+  segments?: (number|string)[]
+  // type?: string
+
+  // annotation?: string // TODO what is this used for?
+} & CustomLayerBase
+
+/**
+ * custom layer is a catch all term that apply **any** special looks
+ * to an atlas. it could include:
+ * 
+ * - different colormap
+ * - different volume (pmap)
+ * - different indicies
+ * 
+ * It is up to the viewer on how to interprete these information.
+ * each instance **must** contain a clType and an id
+ * - clType facilitates viewer on how to interprete the custom layer
+ * - id allows custom layer to be removed, if necessary
+ */
+export type CustomLayer = ColorMapCustomLayer | NgLayerCustomLayer | ThreeSurferCustomLayer | ThreeSurferCustomLabelLayer
diff --git a/src/state/atlasAppearance/index.ts b/src/state/atlasAppearance/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..739f036f65baf71930eff7ae9fd37067cfb4e012
--- /dev/null
+++ b/src/state/atlasAppearance/index.ts
@@ -0,0 +1,4 @@
+export * as actions from "./action"
+export * as selectors from "./selector"
+export { nameSpace, ColorMapCustomLayer, CustomLayer, NgLayerCustomLayer } from "./const"
+export { reducer, AtlasAppearanceStore, defaultState } from "./store"
diff --git a/src/state/atlasAppearance/selector.ts b/src/state/atlasAppearance/selector.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b7eac7bc1701c6e490a6a72ccd9cf8412d4e7aeb
--- /dev/null
+++ b/src/state/atlasAppearance/selector.ts
@@ -0,0 +1,20 @@
+import { createSelector } from "@ngrx/store"
+import { nameSpace } from "./const"
+import { AtlasAppearanceStore } from "./store"
+
+const selectStore = state => state[nameSpace] as AtlasAppearanceStore
+
+export const octantRemoval = createSelector(
+  selectStore,
+  state => state.octantRemoval
+)
+
+export const showDelineation = createSelector(
+  selectStore,
+  state => state.showDelineation
+)
+
+export const customLayers = createSelector(
+  selectStore,
+  state => state.customLayers
+)
diff --git a/src/state/atlasAppearance/store.ts b/src/state/atlasAppearance/store.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6c060da26b4696af0865d0f5c545fdc0d42cffd4
--- /dev/null
+++ b/src/state/atlasAppearance/store.ts
@@ -0,0 +1,62 @@
+import { createReducer, on } from "@ngrx/store"
+import * as actions from "./action"
+import { CustomLayer } from "./const"
+
+export type AtlasAppearanceStore = {
+
+  octantRemoval: boolean
+  showDelineation: boolean
+  customLayers: CustomLayer[]
+}
+
+export const defaultState: AtlasAppearanceStore = {
+  octantRemoval: true,
+  showDelineation: true,
+  customLayers: []
+}
+
+export const reducer = createReducer(
+  defaultState,
+  on(
+    actions.setOctantRemoval,
+    (state, { flag }) => {
+      return {
+        ...state,
+        octantRemoval: flag
+      }
+    }
+  ),
+  on(
+    actions.setShowDelineation,
+    (state, { flag }) => {
+      return {
+        ...state,
+        showDelineation: flag
+      }
+    }
+  ),
+  on(
+    actions.addCustomLayer,
+    (state, { customLayer }) => {
+      const { customLayers } = state
+
+      return {
+        ...state,
+        customLayers: [
+          customLayer,
+          ...customLayers.filter(l => l.id !== customLayer.id)
+        ]
+      }
+    }
+  ),
+  on(
+    actions.removeCustomLayer,
+    (state, { id }) => {
+      const { customLayers } = state
+      return {
+        ...state,
+        customLayers: customLayers.filter(l => l.id !== id)
+      }
+    }
+  )
+)
diff --git a/src/state/atlasSelection/actions.ts b/src/state/atlasSelection/actions.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8787234b909287c331473dfe6d0cceb578375111
--- /dev/null
+++ b/src/state/atlasSelection/actions.ts
@@ -0,0 +1,181 @@
+import { createAction, props } from "@ngrx/store";
+import { SapiAtlasModel, SapiParcellationModel, SapiRegionModel, SapiSpaceModel } from "src/atlasComponents/sapi";
+import { BreadCrumb, nameSpace, ViewerMode, AtlasSelectionState } from "./const"
+
+export const selectAtlas = createAction(
+  `${nameSpace} selectAtlas`,
+  props<{
+    atlas: SapiAtlasModel
+  }>()
+)
+
+export const selectTemplate = createAction(
+  `${nameSpace} selectTemplate`,
+  props<{
+    template: SapiSpaceModel
+  }>()
+)
+
+export const selectParcellation = createAction(
+  `${nameSpace} selectParcellation`,
+  props<{
+    parcellation: SapiParcellationModel
+  }>()
+)
+
+/**
+ * setAtlasSelectionState is called as a final step to (potentially) set:
+ * - selectedAtlas
+ * - selectedTemplate
+ * - selectedParcellation
+ * 
+ * It is up to the dispatcher to ensure that the selection makes sense.
+ * If any field is unset, it will take the default value from the store.
+ * It is **specifically** not setup to do **anymore** than atlas, template and parcellation
+ * 
+ * We may setup post hook for navigation adjustments/etc.
+ * Probably easier is simply subscribe to store and react to selectedTemplate selector
+ */
+export const setAtlasSelectionState = createAction(
+  `${nameSpace} setAtlasSelectionState`,
+  props<Partial<AtlasSelectionState>>()
+)
+
+export const setSelectedParcellationAllRegions = createAction(
+  `${nameSpace} setSelectedParcellationAllRegions`,
+  props<{
+    regions: SapiRegionModel[]
+  }>()
+)
+
+export const selectRegion = createAction(
+  `${nameSpace} selectRegion`,
+  props<{
+    region: SapiRegionModel
+  }>()
+)
+
+export const setSelectedRegions = createAction(
+  `${nameSpace} setSelectedRegions`,
+  props<{
+    regions: SapiRegionModel[]
+  }>()
+)
+
+export const setStandAloneVolumes = createAction(
+  `${nameSpace} setStandAloneVolumes`,
+  props<{
+    standAloneVolumes: string[]
+  }>()
+)
+
+export const setNavigation = createAction(
+  `${nameSpace} setNavigation`,
+  props<{
+    navigation: {
+      position: number[]
+      orientation: number[]
+      zoom: number
+      perspectiveOrientation: number[]
+      perspectiveZoom: number
+    }
+  }>()
+)
+
+export const setViewerMode = createAction(
+  `${nameSpace} setViewerMode`,
+  props<{
+    viewerMode: ViewerMode
+  }>()
+)
+
+export const showBreadCrumb = createAction(
+  `${nameSpace} showBreadCrumb`,
+  props<{
+    breadcrumb: BreadCrumb
+  }>()
+)
+
+export const dismissBreadCrumb = createAction(
+  `${nameSpace} dismissBreadCrumb`,
+  props<{
+    id: string
+  }>()
+)
+
+export const clearSelectedRegions = createAction(
+  `${nameSpace} clearSelectedRegions`
+)
+
+export const selectATPById = createAction(
+  `${nameSpace} selectATPById`,
+  props<{
+    atlasId?: string
+    templateId?: string
+    parcellationId?: string
+  }>()
+)
+
+export const clearNonBaseParcLayer = createAction(
+  `${nameSpace} clearNonBaseParcLayer`
+)
+
+export const clearStandAloneVolumes = createAction(
+  `${nameSpace} clearStandAloneVolumes`
+)
+
+/**
+ * n.b. position in nm!
+ * TODO this action currently sucks.
+ * it depends on nehuba being available
+ * and if so, potentially ease move there
+ * 
+ * should be moved to nehuba/store/navigateTo instead
+ */
+export const navigateTo = createAction(
+  `${nameSpace} navigateTo`,
+  props<{
+    navigation: Partial<{
+      position: number[]
+      orientation: number[]
+      zoom: number
+      perspectiveOrientation: number[]
+      perspectiveZoom: number
+    }>
+    physical?: boolean
+    animation?: boolean
+  }>()
+)
+
+export const navigateToRegion = createAction(
+  `${nameSpace} navigateToRegion`,
+  props<{
+    region: SapiRegionModel
+  }>()
+)
+
+export const clearViewerMode = createAction(
+  `${nameSpace} clearViewerMode`,
+)
+
+export const toggleRegionSelect = createAction(
+  `${nameSpace} toggleRegionSelect`,
+  props<{
+    region: SapiRegionModel
+  }>()
+)
+
+export const toggleRegionSelectById = createAction(
+  `${nameSpace} toggleRegionSelectById`,
+  props<{
+    id: string
+  }>()
+)
+
+export const viewSelRegionInNewSpace = createAction(
+  `${nameSpace} viewSelRegionInNewSpace`,
+  props<{
+    region: SapiRegionModel
+    template: SapiSpaceModel
+  }>()
+)
diff --git a/src/state/atlasSelection/const.ts b/src/state/atlasSelection/const.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1499e2472a896352406389a50a6eacaa14752539
--- /dev/null
+++ b/src/state/atlasSelection/const.ts
@@ -0,0 +1,33 @@
+import { SapiAtlasModel, SapiParcellationModel, SapiRegionModel, SapiSpaceModel } from "src/atlasComponents/sapi"
+
+export const nameSpace = `[state.atlasSelection]`
+export type ViewerMode = 'annotating' | 'key frame'
+export type BreadCrumb = {
+  id: string
+  name: string
+}
+
+export type AtlasSelectionState = {
+  selectedAtlas: SapiAtlasModel
+  selectedTemplate: SapiSpaceModel
+  selectedParcellation: SapiParcellationModel
+  selectedParcellationAllRegions: SapiRegionModel[]
+
+  selectedRegions: SapiRegionModel[]
+  standAloneVolumes: string[]
+
+  /**
+   * the navigation may mean something very different
+   * depending on if the user is using threesurfer/nehuba view
+   */
+  navigation: {
+    position: number[]
+    orientation: number[]
+    zoom: number
+    perspectiveOrientation: number[]
+    perspectiveZoom: number
+  }
+
+  viewerMode: ViewerMode
+  breadcrumbs: BreadCrumb[]
+}
diff --git a/src/state/atlasSelection/effects.spec.ts b/src/state/atlasSelection/effects.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..556e002c56f518ba5957629a178c5249ad37d234
--- /dev/null
+++ b/src/state/atlasSelection/effects.spec.ts
@@ -0,0 +1,402 @@
+import { TestBed } from "@angular/core/testing"
+import { provideMockActions } from "@ngrx/effects/testing"
+import { Action } from "@ngrx/store"
+import { MockStore, provideMockStore } from "@ngrx/store/testing"
+import { hot } from "jasmine-marbles"
+import { Observable, of, throwError } from "rxjs"
+import { SAPI, SAPIModule, SapiRegionModel, SapiAtlasModel, SapiSpaceModel, SapiParcellationModel } from "src/atlasComponents/sapi"
+import { IDS } from "src/atlasComponents/sapi/constants"
+import { actions, selectors } from "."
+import { Effect } from "./effects"
+import * as mainActions from "../actions"
+import { atlasSelection } from ".."
+
+describe("> effects.ts", () => {
+  describe("> Effect", () => {
+
+    let actions$ = new Observable<Action>()
+    let hoc1left: SapiRegionModel
+    let hoc1leftCentroid: SapiRegionModel
+    let hoc1leftCentroidWrongSpc: SapiRegionModel
+
+    beforeEach(async () => {
+      TestBed.configureTestingModule({
+        imports: [
+          // HttpClientTestingModule,
+          SAPIModule
+        ],
+        providers: [
+          Effect,
+          provideMockStore(),
+          provideMockActions(() => actions$)
+        ]
+      })
+
+      /**
+       * only need to populate hoc1 left once
+       */
+      if (!hoc1left) {
+
+        const sapisvc = TestBed.inject(SAPI)
+        const regions = await sapisvc.getParcRegions(IDS.ATLAES.HUMAN, IDS.PARCELLATION.JBA29, IDS.TEMPLATES.MNI152).toPromise()
+        hoc1left = regions.find(r => /hoc1/i.test(r.name) && /left/i.test(r.name))
+        if (!hoc1left) throw new Error(`cannot find hoc1 left`)
+        hoc1leftCentroid = JSON.parse(JSON.stringify(hoc1left)) 
+        hoc1leftCentroid.hasAnnotation.bestViewPoint = {
+          coordinateSpace: {
+            '@id': IDS.TEMPLATES.BIG_BRAIN
+          },
+          coordinates: [{
+            value: 1
+          }, {
+            value: 2
+          }, {
+            value: 3
+          }]
+        }
+        hoc1leftCentroidWrongSpc = JSON.parse(JSON.stringify(hoc1leftCentroid))
+        hoc1leftCentroidWrongSpc.hasAnnotation.bestViewPoint.coordinateSpace['@id'] = IDS.TEMPLATES.COLIN27
+      }
+    })
+
+    it('> can be init', () => {
+      const effects = TestBed.inject(Effect)
+      expect(effects).toBeTruthy()
+    })
+
+    describe('> selectTemplate$', () => {
+
+      describe('> when transiting from template A to template B', () => {
+        describe('> if the current navigation is correctly formed', () => {
+          it('> uses current navigation param', () => {
+
+          })
+        })
+
+        describe('> if current navigation is malformed', () => {
+          it('> if current navigation is undefined, use nehubaConfig of last template', () => {
+          })
+  
+          it('> if current navigation is empty object, use nehubaConfig of last template', () => {
+          })
+        })
+  
+      })
+
+      it('> if coordXform returns error', () => {
+
+      })
+
+      it('> if coordXform complete', () => {
+
+      })
+
+    })
+
+    describe("> onTemplateParcSelectionPostHook", () => {
+      describe("> 0", () => {
+      })
+      describe("> 1", () => {
+        const currNavigation = {
+          orientation: [0, 0, 0, 1],
+          perspectiveOrientation: [0, 0, 0, 1],
+          perspectiveZoom: 1,
+          position: [1, 2, 3], 
+          zoom: 1
+        }
+        beforeEach(() => {
+          const store = TestBed.inject(MockStore)
+          store.overrideSelector(atlasSelection.selectors.navigation, currNavigation)
+        })
+        describe("> when atlas is different", () => {
+          describe("> if no atlas prior", () => {
+
+            it("> navigation should be reset", () => {
+              const effects = TestBed.inject(Effect)
+              const hook = effects.onTemplateParcSelectionPostHook[1]
+              const obs = hook({
+                current: {
+                  atlas: null,
+                  parcellation: null,
+                  template: null
+                },
+                previous: {
+                  atlas: {
+                    "@id": IDS.ATLAES.RAT
+                  } as any,
+                  parcellation: {
+                    "@id": IDS.PARCELLATION.WAXHOLMV4
+                  } as any,
+                  template: {
+                    "@id": IDS.TEMPLATES.WAXHOLM
+                  } as any,
+                }
+              })
+
+              expect(obs).toBeObservable(
+                hot('(a|)', {
+                  a: {
+                    navigation: null
+                  }
+                })
+              )
+            })
+          })
+          describe("> if different atlas prior", () => {
+
+            it("> navigation should be reset", () => {
+              const effects = TestBed.inject(Effect)
+              const hook = effects.onTemplateParcSelectionPostHook[1]
+              const obs = hook({
+                current: {
+                  atlas: {
+                    "@id": IDS.ATLAES.HUMAN
+                  } as any,
+                  parcellation: {
+                    "@id": IDS.PARCELLATION.JBA29
+                  } as any,
+                  template: {
+                    "@id": IDS.TEMPLATES.MNI152
+                  } as any,
+                },
+                previous: {
+                  atlas: {
+                    "@id": IDS.ATLAES.RAT
+                  } as any,
+                  parcellation: {
+                    "@id": IDS.PARCELLATION.WAXHOLMV4
+                  } as any,
+                  template: {
+                    "@id": IDS.TEMPLATES.WAXHOLM
+                  } as any,
+                }
+              })
+
+              expect(obs).toBeObservable(
+                hot('(a|)', {
+                  a: {
+                    navigation: null
+                  }
+                })
+              )
+            })
+          })
+        })
+      })
+    })
+  
+    describe('> if selected atlas has no matching tmpl space', () => {
+
+      it('> should emit gernal error', () => {
+
+      })
+    })
+
+    describe('> if selected atlas has matching tmpl', () => {
+
+      describe('> if parc is empty array', () => {
+        it('> should emit with falsy as payload', () => {
+
+        })
+      })
+      describe('> if no parc has eligible @id', () => {
+
+        it('> should emit with falsy as payload', () => {
+
+        })
+      })
+
+      describe('> if some parc has eligible @id', () => {
+        describe('> if no @version is available', () => {
+
+          it('> selects the first parc', () => {
+
+          })
+        })
+
+        describe('> if @version is available', () => {
+          
+          describe('> if there exist an entry without @next attribute', () => {
+            
+            it('> selects the first one without @next attribute', () => {
+            })
+          })
+          describe('> if there exist no entry without @next attribute', () => {
+            
+            it('> selects the first one without @next attribute', () => {
+
+            })
+          })
+        })
+      })
+    })
+
+    describe('> onNavigateToRegion', () => {
+      beforeEach(async () => {
+        actions$ = hot('a', {
+          a: actions.navigateToRegion({
+            region: hoc1left
+          })
+        })
+        const mockStore = TestBed.inject(MockStore)
+        mockStore.overrideSelector(selectors.selectedAtlas, {
+          "@id": IDS.ATLAES.HUMAN
+        } as SapiAtlasModel)
+        mockStore.overrideSelector(selectors.selectedTemplate, {
+          "@id": IDS.TEMPLATES.MNI152
+        } as SapiSpaceModel)
+        mockStore.overrideSelector(selectors.selectedParcellation, {
+          "@id": IDS.PARCELLATION.JBA29
+        } as SapiParcellationModel)
+      })
+
+      describe('> if atlas, template, parc is not set', () => {
+        const atp = [{
+          name: 'atlas',
+          atpSelector: selectors.selectedAtlas
+        },{
+          name: 'template',
+          atpSelector: selectors.selectedTemplate
+        },{
+          name: 'parcellation',
+          atpSelector: selectors.selectedParcellation
+        }]
+        for (const { name, atpSelector } of atp) {
+          describe(`> if ${name} is unset`, () => {
+
+            beforeEach(() => {
+              const mockStore = TestBed.inject(MockStore)
+              mockStore.overrideSelector(atpSelector, null)
+            })
+
+            it('> returns general error', () => {
+              const effect = TestBed.inject(Effect)
+              expect(effect.onNavigateToRegion).toBeObservable(
+                hot('a', {
+                  a: mainActions.generalActionError({
+                    message: `atlas, template, parcellation or region not set`
+                  })
+                })
+              )
+            })
+          })
+        }
+      })
+
+      describe('> if atlas, template, parc is set, but region unset', () => {
+
+        beforeEach(() => {
+          actions$ = hot('a', {
+            a: actions.navigateToRegion({
+              region: null
+            })
+          })
+        })
+        it('> returns general error', () => {
+          const effect = TestBed.inject(Effect)
+          expect(effect.onNavigateToRegion).toBeObservable(
+            hot('a', {
+              a: mainActions.generalActionError({
+                message: `atlas, template, parcellation or region not set`
+              })
+            })
+          )
+        })
+      })
+
+      describe('> if inputs are fine', () => {
+        let getRegionSpy: jasmine.Spy
+        let regionGetDetailSpy: jasmine.Spy = jasmine.createSpy()
+        beforeEach(() => {
+          const sapi = TestBed.inject(SAPI)
+          getRegionSpy = spyOn(sapi, 'getRegion')
+          getRegionSpy.and.returnValue({
+            getDetail: regionGetDetailSpy
+          })
+          regionGetDetailSpy.and.returnValue(
+            of(hoc1leftCentroid)
+          )
+        })
+        afterEach(() => {
+          if (getRegionSpy) getRegionSpy.calls.reset()
+          if (regionGetDetailSpy) regionGetDetailSpy.calls.reset()
+        })
+        it('> getRegionDetailSpy is called, and calls navigateTo', () => {
+          const eff = TestBed.inject(Effect)
+          expect(eff.onNavigateToRegion).toBeObservable(
+            hot(`a`, {
+              a: actions.navigateTo({
+                navigation: {
+                  position: [1e6, 2e6, 3e6]
+                },
+                animation: true
+              })
+            })
+          )
+          expect(getRegionSpy).toHaveBeenCalledTimes(1)
+          expect(getRegionSpy).toHaveBeenCalledWith(IDS.ATLAES.HUMAN, IDS.PARCELLATION.JBA29, hoc1left["@id"])
+        })
+
+        describe('> mal formed return', () => {
+          describe('> returns null', () => {
+            beforeEach(() => {
+
+              regionGetDetailSpy.and.returnValue(
+                of(null)
+              )
+            })
+            it('> generalactionerror', () => {
+
+              const eff = TestBed.inject(Effect)
+              expect(eff.onNavigateToRegion).toBeObservable(
+                hot(`a`, {
+                  a: mainActions.generalActionError({
+                    message: `getting region detail error! cannot get coordinates`
+                  })
+                })
+              )
+            })
+          })
+          describe('> general throw', () => {
+            beforeEach(() => {
+              regionGetDetailSpy.and.returnValue(
+                throwError(`oh noes`)
+              )
+            })
+            it('> generalactionerror', () => {
+
+              const eff = TestBed.inject(Effect)
+              expect(eff.onNavigateToRegion).toBeObservable(
+                hot(`a`, {
+                  a: mainActions.generalActionError({
+                    message: `Error getting region centroid`
+                  })
+                })
+              )
+            })
+
+          })
+          describe('> does not contain props attr', () => {
+
+            beforeEach(() => {
+              regionGetDetailSpy.and.returnValue(
+                of(hoc1left)
+              )
+            })
+            it('> generalactionerror', () => {
+
+              const eff = TestBed.inject(Effect)
+              expect(eff.onNavigateToRegion).toBeObservable(
+                hot(`a`, {
+                  a: mainActions.generalActionError({
+                    message: `getting region detail error! cannot get coordinates`
+                  })
+                })
+              )
+            })
+          })
+        })
+      })
+    })
+  })
+})
\ No newline at end of file
diff --git a/src/state/atlasSelection/effects.ts b/src/state/atlasSelection/effects.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2c95b75602df2ec483983d910f61ceccdcc7525d
--- /dev/null
+++ b/src/state/atlasSelection/effects.ts
@@ -0,0 +1,449 @@
+import { Injectable } from "@angular/core";
+import { Actions, createEffect, ofType } from "@ngrx/effects";
+import { concat, forkJoin, merge, Observable, of } from "rxjs";
+import { catchError, filter, map, mapTo, switchMap, switchMapTo, take, withLatestFrom } from "rxjs/operators";
+import { SAPI, SapiAtlasModel, SapiParcellationModel, SAPIRegion, SapiRegionModel, SapiSpaceModel } from "src/atlasComponents/sapi";
+import * as mainActions from "../actions"
+import { select, Store } from "@ngrx/store";
+import { selectors, actions } from '.'
+import { fromRootStore } from "./util";
+import { AtlasSelectionState } from "./const"
+import { ParcellationIsBaseLayer } from "src/atlasComponents/sapiViews/core/parcellation/parcellationIsBaseLayer.pipe";
+import { OrderParcellationByVersionPipe } from "src/atlasComponents/sapiViews/core/parcellation/parcellationVersion.pipe";
+import { atlasAppearance, atlasSelection } from "..";
+import { ParcellationSupportedInSpacePipe } from "src/atlasComponents/sapiViews/util/parcellationSupportedInSpace.pipe";
+import { InterSpaceCoordXformSvc } from "src/atlasComponents/sapi/core/space/interSpaceCoordXform.service";
+
+type OnTmplParcHookArg = {
+  previous: {
+    atlas: SapiAtlasModel
+    template: SapiSpaceModel
+    parcellation: SapiParcellationModel
+  }
+  current: {
+    atlas: SapiAtlasModel
+    template: SapiSpaceModel
+    parcellation: SapiParcellationModel
+  }
+}
+
+@Injectable()
+export class Effect {
+
+  onTemplateParcSelectionPostHook: ((arg: OnTmplParcHookArg) => Observable<Partial<AtlasSelectionState>>)[] = [
+    /**
+     * This hook gets the region associated with the selected parcellation and template,
+     * and then set selectedParcellationAllRegions to it
+     */
+    ({ current }) => {
+      const { atlas, parcellation, template } = current
+      return (
+        !!atlas && !!parcellation && !!template
+          ? this.sapiSvc.getParcRegions(atlas["@id"], parcellation["@id"], template["@id"])
+          : of([])
+      ).pipe(
+        map(regions => {
+          return {
+            selectedParcellationAllRegions: regions
+          }
+        })
+      )
+    },
+    ({ current, previous }) => {
+      const prevSpcName = InterSpaceCoordXformSvc.TmplIdToValidSpaceName(previous?.template?.["@id"])
+      const currSpcName = InterSpaceCoordXformSvc.TmplIdToValidSpaceName(current?.template?.["@id"])
+
+      /**
+       * if trans-species, return default state for navigation
+       */
+      if (previous?.atlas?.["@id"] !== current?.atlas?.["@id"]) {
+        return of({
+          navigation: null
+        })
+      }
+
+      /**
+       * if either space name is undefined, return default state for navigation
+       */
+      if (!prevSpcName || !currSpcName) {
+        return of({
+          navigation: atlasSelection.defaultState.navigation
+        })
+      }
+      return this.store.pipe(
+        select(atlasSelection.selectors.navigation),
+        take(1),
+        switchMap(({ position, ...rest }) => 
+          this.interSpaceCoordXformSvc.transform(prevSpcName, currSpcName, position as [number, number, number]).pipe(
+            map(value => {
+              if (value.status === "error") {
+                return {}
+              }
+              return {
+                navigation: {
+                  ...rest,
+                  position: value.result,
+                }
+              } as Partial<AtlasSelectionState>
+            })
+          )
+        )
+      )
+    }
+  ]
+
+  parcSupportedInSpacePipe = new ParcellationSupportedInSpacePipe(this.sapiSvc)
+  onTemplateParcSelection = createEffect(() => merge<{ template: SapiSpaceModel, parcellation: SapiParcellationModel }>(
+    this.action.pipe(
+      ofType(actions.selectTemplate),
+      map(({ template }) => {
+        return {
+          template,
+          parcellation: null
+        }
+      })
+    ),
+    this.action.pipe(
+      ofType(actions.selectParcellation),
+      map(({ parcellation }) => {
+        return {
+          template: null,
+          parcellation
+        }
+      })
+    )
+  ).pipe(
+    withLatestFrom(this.store),
+    switchMap(([ { template, parcellation }, store ]) => {
+      const currTmpl = selectors.selectedTemplate(store)
+      const currParc = selectors.selectedParcellation(store)
+      const currAtlas = selectors.selectedAtlas(store)
+      return this.parcSupportedInSpacePipe.transform(
+        parcellation || currParc,
+        template || currTmpl
+      ).pipe(
+        switchMap(flag => {
+          /**
+           * if desired parc is supported in tmpl, emit them
+           */
+          if (flag) {
+            return of({
+              atlas: currAtlas,
+              template: template || currTmpl,
+              parcellation: parcellation || currParc,
+            })
+          }
+          /**
+           * if template is defined, find the first parcellation that is supported
+           */
+          if (!!template) {
+            return concat(
+              ...currAtlas.parcellations.map(
+                p => this.parcSupportedInSpacePipe.transform(p["@id"], template).pipe(
+                  filter(flag => flag),
+                  switchMap(() => this.sapiSvc.getParcDetail(currAtlas["@id"], p['@id'])),
+                )
+              )
+            ).pipe(
+              take(1),
+              map(parcellation => {
+                return {
+                  atlas: currAtlas,
+                  template,
+                  parcellation
+                }
+              })
+            )
+          }
+          if (!!parcellation) {
+            return concat(
+              ...currAtlas.spaces.map(
+                sp => this.parcSupportedInSpacePipe.transform(parcellation["@id"], sp["@id"]).pipe(
+                  filter(flag => flag),
+                  switchMap(() => this.sapiSvc.getSpaceDetail(currAtlas["@id"], sp['@id'])),
+                )
+              )
+            ).pipe(
+              take(1),
+              map(template => {
+                return {
+                  atlas: currAtlas,
+                  template,
+                  parcellation
+                }
+              })
+            )
+          }
+          throw new Error(`neither template nor parcellation has been defined!`)
+        }),
+        switchMap(({ atlas, template, parcellation }) => 
+          forkJoin(
+            this.onTemplateParcSelectionPostHook.map(fn => fn({ previous: { atlas: currAtlas, template: currTmpl, parcellation: currParc }, current: { atlas, template, parcellation } }))
+          ).pipe(
+            map(partialStates => {
+              let returnState: Partial<AtlasSelectionState> = {
+                selectedAtlas: atlas,
+                selectedTemplate: template,
+                selectedParcellation: parcellation
+              }
+              for (const s of partialStates) {
+                returnState = {
+                  ...returnState,
+                  ...s,
+                }
+              }
+              return actions.setAtlasSelectionState(returnState)
+            })
+          )
+        )
+      )
+    })
+  ))
+
+  onAtlasSelectionSelectTmplParc = createEffect(() => this.action.pipe(
+    ofType(actions.selectAtlas),
+    filter(action => !!action.atlas),
+    switchMap(({ atlas }) => {
+      const selectedParc = atlas.parcellations.find(p => /290/.test(p["@id"])) || atlas.parcellations[0]
+      return this.sapiSvc.getParcDetail(atlas["@id"], selectedParc["@id"], { priority: 10 }).pipe(
+        map(parcellation => {
+          return {
+            parcellation,
+            atlas
+          }
+        })
+      )
+    }),
+    switchMap(({ atlas, parcellation }) => {
+      const spacdIds = parcellation.brainAtlasVersions.map(bas => bas.coordinateSpace) as { "@id": string }[]
+      return forkJoin(
+        spacdIds.filter(
+          spaceId => atlas.spaces.map(spc => spc["@id"]).indexOf(spaceId["@id"]) >= 0
+        ).map(spaceId =>
+          this.sapiSvc.getSpaceDetail(atlas["@id"], spaceId["@id"])
+        )
+      ).pipe(
+        switchMap(spaces => {
+          const selectedSpace = spaces.find(s => /152/.test(s.fullName)) || spaces[0]
+          return forkJoin(
+            this.onTemplateParcSelectionPostHook.map(fn => fn({ previous: null, current: { atlas, parcellation, template: selectedSpace } }))
+          ).pipe(
+            map(partialStates => {
+
+              let returnState: Partial<AtlasSelectionState> = {
+                selectedAtlas: atlas,
+                selectedTemplate: selectedSpace,
+                selectedParcellation: parcellation
+              }
+              for (const s of partialStates) {
+                returnState = {
+                  ...returnState,
+                  ...s,
+                }
+              }
+              return actions.setAtlasSelectionState(returnState)
+            })
+          )
+        })
+      )
+    })
+  ))
+
+  onATPSelectionClearBaseLayerColorMap = createEffect(() => this.store.pipe(
+    select(selectors.selectedParcAllRegions),
+    withLatestFrom(
+      this.store.pipe(
+        select(atlasAppearance.selectors.customLayers),
+        map(layers => layers.filter(l => l.clType === "baselayer/colormap"))
+      )
+    ),
+    switchMap(([regions, layers]) => {
+      const map = new Map<SapiRegionModel, number[]>()
+      for (const region of regions) {
+        map.set(region, SAPIRegion.GetDisplayColor(region))
+      }
+      const actions = [
+        ...layers.map(({ id }) =>
+          atlasAppearance.actions.removeCustomLayer({
+            id
+          })
+        ),
+        atlasAppearance.actions.addCustomLayer({
+          customLayer: {
+            clType: "baselayer/colormap",
+            id: 'base-colormap-id',
+            colormap: map
+          }
+        })
+      ]
+      return of(...actions)
+    })
+  ))
+
+
+  onAtlasSelClearStandAloneVolumes = createEffect(() => this.action.pipe(
+    ofType(actions.selectAtlas),
+    mapTo(actions.setStandAloneVolumes({
+      standAloneVolumes: []
+    }))
+  ))
+
+  onClearRegion = createEffect(() => this.action.pipe(
+    ofType(actions.clearSelectedRegions),
+    mapTo(actions.setSelectedRegions({
+      regions: []
+    }))
+  ))
+
+  onNonBaseLayerRemoval = createEffect(() => this.action.pipe(
+    ofType(actions.clearNonBaseParcLayer),
+    switchMapTo(
+      this.store.pipe(
+        fromRootStore.allAvailParcs(this.sapiSvc),
+        map(parcs => {
+          const baseLayers = parcs.filter(this.parcellationIsBaseLayerPipe.transform)
+          const newestLayer = this.orderParcellationByVersionPipe.transform(baseLayers)
+          return actions.selectParcellation({
+            parcellation: newestLayer
+          })
+        })  
+      )
+    )
+  ))
+
+  private parcellationIsBaseLayerPipe = new ParcellationIsBaseLayer()
+  private orderParcellationByVersionPipe = new OrderParcellationByVersionPipe()
+
+  onClearStandAloneVolumes = createEffect(() => this.action.pipe(
+    ofType(actions.clearStandAloneVolumes),
+    mapTo(actions.setStandAloneVolumes({
+      standAloneVolumes: []
+    }))
+  ))
+
+  /**
+   * nb for template selection
+   * navigation should be transformed
+   * see selectTemplate$ in spec.ts
+   */
+  onSelectATPById = createEffect(() => this.action.pipe(
+    ofType(actions.selectATPById),
+    mapTo(mainActions.generalActionError({
+      message: `NYI, onSelectATPById`
+    }))
+  ))
+  
+  onClearViewerMode = createEffect(() => this.action.pipe(
+    ofType(actions.clearViewerMode),
+    mapTo(actions.setViewerMode({ viewerMode: null }))
+  ))
+
+  onToggleRegionSelectById = createEffect(() => this.action.pipe(
+    ofType(actions.toggleRegionSelectById),
+    mapTo(mainActions.generalActionError({
+      message: `NYI onToggleRegionSelectById`
+    }))
+  ))
+
+  onNavigateToRegion = createEffect(() => this.action.pipe(
+    ofType(actions.navigateToRegion),
+    withLatestFrom(
+      this.store.pipe(
+        select(selectors.selectedTemplate)
+      ),
+      this.store.pipe(
+        select(selectors.selectedAtlas)
+      ),
+      this.store.pipe(
+        select(selectors.selectedParcellation)
+      )
+    ),
+    switchMap(([{ region }, selectedTemplate, selectedAtlas, selectedParcellation]) => {
+      if (!selectedAtlas || !selectedTemplate || !selectedParcellation || !region)  {
+        return of(
+          mainActions.generalActionError({
+            message: `atlas, template, parcellation or region not set`
+          })
+        )
+      }
+
+      if (region.hasAnnotation?.bestViewPoint && region.hasAnnotation.bestViewPoint.coordinateSpace['@id'] === selectedTemplate["@id"]) {
+        return of(
+          actions.navigateTo({
+            animation: true,
+            navigation: {
+              position: region.hasAnnotation.bestViewPoint.coordinates.map(v => v.value * 1e6)
+            }
+          })
+        )
+      }
+      
+      return this.sapiSvc.getRegion(selectedAtlas['@id'], selectedParcellation['@id'], region["@id"]).getDetail(selectedTemplate["@id"]).pipe(
+        map(detailedRegion => {
+          if (!detailedRegion?.hasAnnotation?.bestViewPoint?.coordinates) {
+            return mainActions.generalActionError({
+              message: `getting region detail error! cannot get coordinates`
+            })
+          }
+          return actions.navigateTo({
+            animation: true,
+            navigation: {
+              position: detailedRegion.hasAnnotation.bestViewPoint.coordinates.map(v => v.value * 1e6)
+            }
+          })
+        }),
+        catchError((_err, _obs) => of(
+          mainActions.generalActionError({
+            message: `Error getting region centroid`
+          })
+        ))
+      )
+    })
+  ))
+
+  onSelAtlasTmplParcClearRegion = createEffect(() => merge(
+    this.action.pipe(
+      ofType(actions.selectAtlas)
+    ),
+    this.action.pipe(
+      ofType(actions.selectTemplate)
+    ),
+    this.action.pipe(
+      ofType(actions.selectParcellation)
+    )
+  ).pipe(
+    switchMapTo(
+      of(
+        actions.setSelectedRegions({
+          regions: []
+        })
+      )
+    )
+  ))
+
+  onRegionToggleSelect = createEffect(() => this.action.pipe(
+    ofType(actions.toggleRegionSelect),
+    withLatestFrom(
+      this.store.pipe(
+        select(selectors.selectedRegions)
+      )
+    ),
+    map(([ { region }, regions ]) => {
+      const selectedRegionsIndicies = regions.map(r => r["@id"])
+      const roiIndex = selectedRegionsIndicies.indexOf(region["@id"])
+      return actions.setSelectedRegions({
+        regions: roiIndex >= 0
+          ? [...regions.slice(0, roiIndex), ...regions.slice(roiIndex + 1)]
+          : [...regions, region]
+      })
+    })
+  ))
+
+  constructor(
+    private action: Actions,
+    private sapiSvc: SAPI,
+    private store: Store,
+    private interSpaceCoordXformSvc: InterSpaceCoordXformSvc,
+  ){
+  }
+}
\ No newline at end of file
diff --git a/src/state/atlasSelection/index.ts b/src/state/atlasSelection/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5ef795456578636c52a28ce08189b4feec31bdb5
--- /dev/null
+++ b/src/state/atlasSelection/index.ts
@@ -0,0 +1,6 @@
+export * as selectors from "./selectors"
+export { fromRootStore } from "./util"
+export { nameSpace, AtlasSelectionState } from "./const"
+export { reducer, defaultState } from "./store"
+export * as actions from "./actions"
+export { Effect } from "./effects"
\ No newline at end of file
diff --git a/src/state/atlasSelection/selectors.ts b/src/state/atlasSelection/selectors.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ad3079a560f2d12eb3cf5655279122b8cf21cd75
--- /dev/null
+++ b/src/state/atlasSelection/selectors.ts
@@ -0,0 +1,51 @@
+import { createSelector } from "@ngrx/store"
+import { nameSpace, AtlasSelectionState } from "./const"
+
+export const viewerStateHelperStoreName = 'viewerStateHelper'
+
+export const selectStore = (state: any) => state[nameSpace] as AtlasSelectionState
+
+export const selectedAtlas = createSelector(
+  selectStore,
+  state => state.selectedAtlas
+)
+
+export const selectedTemplate = createSelector(
+  selectStore,
+  state => state.selectedTemplate
+)
+
+export const selectedParcellation = createSelector(
+  selectStore,
+  state => state.selectedParcellation
+)
+
+export const selectedParcAllRegions = createSelector(
+  selectStore,
+  state => state.selectedParcellationAllRegions
+)
+
+export const selectedRegions = createSelector(
+  selectStore,
+  state => state.selectedRegions
+)
+
+export const standaloneVolumes = createSelector(
+  selectStore,
+  state => state.standAloneVolumes
+)
+
+export const navigation = createSelector(
+  selectStore,
+  state => state.navigation
+)
+
+export const viewerMode = createSelector(
+  selectStore,
+  state => state.viewerMode
+)
+
+export const breadCrumbs = createSelector(
+  selectStore,
+  state => state.breadcrumbs
+)
\ No newline at end of file
diff --git a/src/state/atlasSelection/store.ts b/src/state/atlasSelection/store.ts
new file mode 100644
index 0000000000000000000000000000000000000000..08848c1efa9b81e649bfc8d7c29d3cc6581f77ed
--- /dev/null
+++ b/src/state/atlasSelection/store.ts
@@ -0,0 +1,139 @@
+import { createReducer, on } from "@ngrx/store";
+import { SapiAtlasModel, SapiParcellationModel, SapiRegionModel, SapiSpaceModel } from "src/atlasComponents/sapi";
+import * as actions from "./actions"
+import { AtlasSelectionState } from "./const"
+
+function getRegionLabelIndex(atlas: SapiAtlasModel, tmpl: SapiSpaceModel, parc: SapiParcellationModel, region: SapiRegionModel) {
+  const lblIdx = Number(region?.hasAnnotation?.internalIdentifier)
+  if (isNaN(lblIdx)) return null
+  return lblIdx
+}
+
+export const defaultState: AtlasSelectionState = {
+  selectedAtlas: null,
+  selectedParcellation: null,
+  selectedParcellationAllRegions: [],
+  selectedRegions: [],
+  selectedTemplate: null,
+  standAloneVolumes: [],
+  navigation: null,
+  viewerMode: null,
+  breadcrumbs: []
+}
+
+const reducer = createReducer(
+  defaultState,
+  on(
+    actions.setAtlasSelectionState,
+    (state, partialState) => {
+      return {
+        ...state,
+        ...partialState
+      }
+    }
+  ),
+  on(
+    actions.setSelectedParcellationAllRegions,
+    (state, { regions }) => {
+      return {
+        ...state,
+        selectedParcellationAllRegions: regions
+      }
+    }
+  ),
+  on(
+    actions.selectRegion,
+    (state, { region }) => {
+      /**
+       * if roi does not have visualizedIn defined
+       * or internal identifier
+       * 
+       * ignore
+       */
+      const { selectedAtlas, selectedParcellation, selectedTemplate } = state
+      if (
+        !region.hasAnnotation?.visualizedIn
+        && !getRegionLabelIndex(selectedAtlas, selectedTemplate, selectedParcellation, region)
+      ) {
+        return { ...state }
+      }
+      const selected = state.selectedRegions.includes(region)
+      return {
+        ...state,
+        selectedRegions: selected
+          ? [ ]
+          : [ region ]
+      }
+    }
+  ),
+  on(
+    actions.setSelectedRegions,
+    (state, { regions }) => {
+      return {
+        ...state,
+        selectedRegions: regions
+      }
+    }
+  ),
+  on(
+    actions.setStandAloneVolumes,
+    (state, { standAloneVolumes }) => {
+      return {
+        ...state,
+        standAloneVolumes
+      }
+    }
+  ),
+  on(
+    actions.setNavigation,
+    (state, { navigation }) => {
+      return {
+        ...state,
+        navigation
+      }
+    }
+  ),
+  on(
+    actions.setViewerMode,
+    (state, { viewerMode }) => {
+      return {
+        ...state,
+        viewerMode
+      }
+    }
+  ),
+  on(
+    actions.showBreadCrumb,
+    (state, { breadcrumb }) => {
+      return {
+        ...state,
+        breadcrumbs: [
+          ...state.breadcrumbs.filter(bc => bc.id !== breadcrumb.id),
+          breadcrumb
+        ]
+      }
+    }
+  ),
+  on(
+    actions.selectAtlas,
+    (state, { atlas }) => {
+      return {
+        ...state,
+        selectedAtlas: atlas
+      }
+    }
+  ),
+  on(
+    actions.dismissBreadCrumb,
+    (state, { id }) => {
+      return {
+        ...state,
+        breadcrumbs: state.breadcrumbs.filter(bc => bc.id !== id)
+      }
+    }
+  )
+)
+
+export {
+  reducer
+}
diff --git a/src/state/atlasSelection/util.ts b/src/state/atlasSelection/util.ts
new file mode 100644
index 0000000000000000000000000000000000000000..cf64466bb4375a6ffa06bb50ec99fea60ee5b8fd
--- /dev/null
+++ b/src/state/atlasSelection/util.ts
@@ -0,0 +1,53 @@
+import { createSelector, select } from "@ngrx/store";
+import { forkJoin, pipe } from "rxjs";
+import { distinctUntilChanged, map, switchMap } from "rxjs/operators";
+import { SAPI, SapiAtlasModel, SapiParcellationModel, SapiSpaceModel } from "src/atlasComponents/sapi";
+import { jsonEqual } from "src/util/json";
+import * as selectors from "./selectors"
+
+const allAvailSpaces = (sapi: SAPI) => pipe(
+  select(selectors.selectedAtlas),
+  switchMap(atlas => forkJoin(
+    atlas.spaces.map(spcWId => sapi.getSpaceDetail(atlas["@id"], spcWId["@id"])))
+  )
+)
+
+const allAvailParcs = (sapi: SAPI) => pipe(
+  select(selectors.selectedAtlas),
+  switchMap(atlas =>
+    forkJoin(
+      atlas.parcellations.map(parcWId => sapi.getParcDetail(atlas["@id"], parcWId["@id"]))
+    )
+  )
+)
+const allAvailSpacesParcs = (sapi: SAPI) => pipe(
+  select(selectors.selectedAtlas),
+  switchMap(atlas =>
+    forkJoin({
+      spaces: atlas.spaces.map(spcWId => sapi.getSpaceDetail(atlas["@id"], spcWId["@id"])),
+      parcellation: atlas.parcellations.map(parcWId => sapi.getParcDetail(atlas["@id"], parcWId["@id"])),
+    })
+  )
+)
+
+const nonDistinctATP = createSelector(
+  selectors.selectedAtlas,
+  selectors.selectedTemplate,
+  selectors.selectedParcellation,
+  (atlas, template, parcellation) => ({ atlas, template, parcellation })
+)
+
+const distinctATP = () => pipe(
+  select(nonDistinctATP),
+  distinctUntilChanged(
+    jsonEqual((o, n) => o?.["@id"] === n?.["@id"])
+  ),
+  map(val => val as { atlas: SapiAtlasModel, parcellation: SapiParcellationModel, template: SapiSpaceModel })
+)
+
+export const fromRootStore = {
+  allAvailSpaces,
+  allAvailParcs,
+  allAvailSpacesParcs,
+  distinctATP,
+}
diff --git a/src/state/const.ts b/src/state/const.ts
new file mode 100644
index 0000000000000000000000000000000000000000..60e44f40724c7a562efb1040af23b1f001adacf9
--- /dev/null
+++ b/src/state/const.ts
@@ -0,0 +1,13 @@
+import { annotation, atlasAppearance, atlasSelection, plugins, userInteraction, userInterface, userPreference } from "."
+
+export const nameSpace = `[state]`
+
+export type MainState = {
+  [userPreference.nameSpace]: userPreference.UserPreference
+  [atlasSelection.nameSpace]: atlasSelection.AtlasSelectionState
+  [userInterface.nameSpace]: userInterface.UiStore
+  [userInteraction.nameSpace]: userInteraction.UserInteraction
+  [annotation.nameSpace]: annotation.AnnotationState
+  [plugins.nameSpace]: plugins.PluginStore
+  [atlasAppearance.nameSpace]: atlasAppearance.AtlasAppearanceStore
+}
diff --git a/src/state/effects/viewerState.useEffect.spec.ts b/src/state/effects/viewerState.useEffect.spec.ts
deleted file mode 100644
index 37c7b0ee9f18297b0268a6459b74863e6f07d2b9..0000000000000000000000000000000000000000
--- a/src/state/effects/viewerState.useEffect.spec.ts
+++ /dev/null
@@ -1,738 +0,0 @@
-import { cvtNehubaConfigToNavigationObj, ViewerStateControllerUseEffect, defaultNavigationObject, defaultNehubaConfigObject } from './viewerState.useEffect'
-import { Observable, of, throwError } from 'rxjs'
-import { TestBed } from '@angular/core/testing'
-import { provideMockActions } from '@ngrx/effects/testing'
-import { MockStore, provideMockStore } from '@ngrx/store/testing'
-import { defaultRootState, generalActionError } from 'src/services/stateStore.service'
-import { Injectable } from '@angular/core'
-import { TemplateCoordinatesTransformation, ITemplateCoordXformResp } from 'src/services/templateCoordinatesTransformation.service'
-import { hot } from 'jasmine-marbles'
-import { AngularMaterialModule } from 'src/sharedModules'
-import { HttpClientModule } from '@angular/common/http'
-import { viewerStateFetchedTemplatesSelector, viewerStateNavigateToRegion, viewerStateNavigationStateSelector, viewerStateNewViewer, viewerStateSelectAtlas, viewerStateSelectTemplateWithName } from 'src/services/state/viewerState.store.helper'
-import { viewerStateFetchedAtlasesSelector, viewerStateGetSelectedAtlas, viewerStateSelectedParcellationSelector, viewerStateSelectedTemplateSelector } from 'src/services/state/viewerState/selectors'
-import { CONST } from 'common/constants'
-import { PureContantService } from 'src/util'
-import { viewerStateChangeNavigation } from 'src/services/state/viewerState/actions'
-
-let returnPosition = null
-const dummyParc1 = {
-  name: 'dummyParc1'
-}
-const dummyTmpl1 = {
-  '@id': 'dummyTmpl1-id',
-  name: 'dummyTmpl1',
-  parcellations: [dummyParc1],
-  nehubaConfig: {
-    dataset: {
-      initialNgState: {
-        ...defaultNehubaConfigObject
-      }
-    }
-  }
-}
-
-const dummyParc2 = {
-  name: 'dummyParc2'
-}
-const dummyTmpl2 = {
-  '@id': 'dummyTmpl2-id',
-  name: 'dummyTmpl2',
-  parcellations: [dummyParc2],
-  nehubaConfig: {
-    dataset: {
-      initialNgState: {
-        ...defaultNehubaConfigObject
-      }
-    }
-  }
-}
-
-@Injectable()
-class MockCoordXformService{
-  getPointCoordinatesForTemplate(src:string, tgt: string, pos: [number, number, number]): Observable<ITemplateCoordXformResp>{
-    return returnPosition
-      ? of({ status: 'completed', result: returnPosition } as ITemplateCoordXformResp)
-      : of({ status: 'error', statusText: 'Failing query' } as ITemplateCoordXformResp)
-  }
-}
-
-const initialState = JSON.parse(JSON.stringify( defaultRootState ))
-const mockFetchedTemplates = [
-  dummyTmpl2,
-  dummyTmpl1
-]
-initialState.viewerState.fetchedTemplates = mockFetchedTemplates
-initialState.viewerState.templateSelected = dummyTmpl2
-const currentNavigation = {
-  position: [4, 5, 6],
-  orientation: [0, 0, 0, 1],
-  perspectiveOrientation: [ 0, 0, 0, 1],
-  perspectiveZoom: 2e5,
-  zoom: 1e5
-}
-initialState.viewerState.navigation = currentNavigation
-
-class MockPureConstantService{
-  allFetchingReady$ = of(true)
-  initFetchTemplate$ = of([])
-
-  getRegionDetail(){
-    return of(null)
-  }
-}
-
-const mockPureConstantService = new MockPureConstantService()
-describe('> viewerState.useEffect.ts', () => {
-  describe('> ViewerStateControllerUseEffect', () => {
-    let actions$: Observable<any>
-    let spy: jasmine.Spy
-    let mockStore: MockStore
-    beforeEach(() => {
-
-      const mock = new MockCoordXformService()
-      spy = spyOn(mock, 'getPointCoordinatesForTemplate').and.callThrough()
-      returnPosition = null
-
-      TestBed.configureTestingModule({
-        imports: [
-          AngularMaterialModule,
-          HttpClientModule,
-        ],
-        providers: [
-          ViewerStateControllerUseEffect,
-          provideMockActions(() => actions$),
-          provideMockStore({ initialState }),
-          {
-            provide: TemplateCoordinatesTransformation,
-            useValue: mock
-          },
-          {
-            provide: PureContantService,
-            useValue: mockPureConstantService
-          }
-        ]
-      })
-
-      mockStore = TestBed.inject(MockStore)
-    })
-
-    describe('> selectTemplate$', () => {
-      beforeEach(() => {
-        mockStore.overrideSelector(viewerStateFetchedTemplatesSelector, mockFetchedTemplates)
-        mockStore.overrideSelector(viewerStateSelectedParcellationSelector, dummyParc1)
-        actions$ = hot(
-          'a',
-          {
-            a: viewerStateSelectTemplateWithName({
-                payload: {
-                  name: dummyTmpl1.name
-                }
-              })
-          }
-        )
-      })
-      describe('> when transiting from template A to template B', () => {
-        describe('> if the current navigation is correctly formed', () => {
-          it('> uses current navigation param', () => {
-
-            const viewerStateCtrlEffect = TestBed.inject(ViewerStateControllerUseEffect)
-            expect(
-              viewerStateCtrlEffect.selectTemplate$
-            ).toBeObservable(
-              hot(
-                'a',
-                {
-                  a: viewerStateNewViewer({
-                      selectTemplate: dummyTmpl1,
-                      selectParcellation: dummyTmpl1.parcellations[0],
-                    })
-                }
-              )
-            )
-            expect(spy).toHaveBeenCalledWith(
-              dummyTmpl2.name,
-              dummyTmpl1.name,
-              initialState.viewerState.navigation.position
-            )
-          })
-        })
-
-        describe('> if current navigation is malformed', () => {
-          it('> if current navigation is undefined, use nehubaConfig of last template', () => {
-
-            const mockStore = TestBed.inject(MockStore)
-            mockStore.overrideSelector(viewerStateNavigationStateSelector, null)
-            const viewerStateCtrlEffect = TestBed.inject(ViewerStateControllerUseEffect)
-
-            expect(
-              viewerStateCtrlEffect.selectTemplate$
-            ).toBeObservable(
-              hot(
-                'a',
-                {
-                  a: viewerStateNewViewer({
-                      selectTemplate: dummyTmpl1,
-                      selectParcellation: dummyTmpl1.parcellations[0],
-                    })
-                }
-              )
-            )
-            const { position } = cvtNehubaConfigToNavigationObj(dummyTmpl2.nehubaConfig.dataset.initialNgState)
-
-            expect(spy).toHaveBeenCalledWith(
-              dummyTmpl2.name,
-              dummyTmpl1.name,
-              position
-            )
-          })
-  
-          it('> if current navigation is empty object, use nehubaConfig of last template', () => {
-
-            const mockStore = TestBed.inject(MockStore)
-            mockStore.overrideSelector(viewerStateNavigationStateSelector, {})
-            const viewerStateCtrlEffect = TestBed.inject(ViewerStateControllerUseEffect)
-
-            expect(
-              viewerStateCtrlEffect.selectTemplate$
-            ).toBeObservable(
-              hot(
-                'a',
-                {
-                  a: viewerStateNewViewer({
-                      selectTemplate: dummyTmpl1,
-                      selectParcellation: dummyTmpl1.parcellations[0],
-                    })
-                }
-              )
-            )
-            const { position } = cvtNehubaConfigToNavigationObj(dummyTmpl2.nehubaConfig.dataset.initialNgState)
-
-            expect(spy).toHaveBeenCalledWith(
-              dummyTmpl2.name,
-              dummyTmpl1.name,
-              position
-            )
-          })
-        })
-  
-      })
-
-      it('> if coordXform returns error', () => {
-        const viewerStateCtrlEffect = TestBed.inject(ViewerStateControllerUseEffect)
-        expect(
-          viewerStateCtrlEffect.selectTemplate$
-        ).toBeObservable(
-          hot(
-            'a',
-            {
-              a: viewerStateNewViewer({
-                  selectTemplate: dummyTmpl1,
-                  selectParcellation: dummyTmpl1.parcellations[0],
-                })
-            }
-          )
-        )
-      })
-
-      it('> if coordXform complete', () => {
-        returnPosition = [ 1.11e6, 2.22e6, 3.33e6 ]
-
-        const viewerStateCtrlEffect = TestBed.inject(ViewerStateControllerUseEffect)
-        const updatedColin = JSON.parse( JSON.stringify( dummyTmpl1 ) )
-        const initialNgState = updatedColin.nehubaConfig.dataset.initialNgState
-        const updatedColinNavigation = updatedColin.nehubaConfig.dataset.initialNgState.navigation
-
-        const { zoom, orientation, perspectiveOrientation, position, perspectiveZoom } = currentNavigation
-
-        for (const idx of [0, 1, 2]) {
-          updatedColinNavigation.pose.position.voxelCoordinates[idx] = returnPosition[idx] / updatedColinNavigation.pose.position.voxelSize[idx]
-        }
-        updatedColinNavigation.zoomFactor = zoom
-        updatedColinNavigation.pose.orientation = orientation
-        initialNgState.perspectiveOrientation = perspectiveOrientation
-        initialNgState.perspectiveZoom = perspectiveZoom
-        
-        expect(
-          viewerStateCtrlEffect.selectTemplate$
-        ).toBeObservable(
-          hot(
-            'a',
-            {
-              a: viewerStateNewViewer({
-                  selectTemplate: updatedColin,
-                  selectParcellation: updatedColin.parcellations[0],
-                })
-            }
-          )
-        )
-      })
-
-    })
-  
-    describe('> navigateToRegion$', () => {
-      const setAction = region => {
-        actions$ = hot(
-          'a',
-          {
-            a: viewerStateNavigateToRegion({
-              payload: { region }
-            })
-          }
-        )
-      }
-      let mockStore: MockStore
-      beforeEach(() => {
-
-        mockStore = TestBed.inject(MockStore)
-          
-        mockStore.overrideSelector(viewerStateGetSelectedAtlas, { '@id': 'foo-bar-atlas'})
-        mockStore.overrideSelector(viewerStateSelectedTemplateSelector, { '@id': 'foo-bar-template'})
-        mockStore.overrideSelector(viewerStateSelectedParcellationSelector, { '@id': 'foo-bar-parcellation'})
-      })
-      describe('> if atlas, template, parc is not set', () => {
-        beforeEach(() => {
-          const region = {
-            name: 'foo bar'
-          }
-          setAction(region)
-        })
-        describe('> if atlas is unset', () => {
-          beforeEach(() => {
-            mockStore.overrideSelector(viewerStateGetSelectedAtlas, null)
-          })
-          it('> returns general error', () => {
-            const effect = TestBed.inject(ViewerStateControllerUseEffect)
-            expect(effect.navigateToRegion$).toBeObservable(
-              hot('a', {
-                a: generalActionError({
-                  message: 'Go to region: region / atlas / template / parcellation not defined.'
-                })
-              })
-            )
-          })
-        })
-        describe('> if template is unset', () => {
-          beforeEach(() => {
-            mockStore.overrideSelector(viewerStateSelectedTemplateSelector, null)
-          })
-          it('> returns general error', () => {
-            const effect = TestBed.inject(ViewerStateControllerUseEffect)
-            expect(effect.navigateToRegion$).toBeObservable(
-              hot('a', {
-                a: generalActionError({
-                  message: 'Go to region: region / atlas / template / parcellation not defined.'
-                })
-              })
-            )
-          })
-        })
-        describe('> if parc is unset', () => {
-          beforeEach(() => {
-            mockStore.overrideSelector(viewerStateSelectedParcellationSelector, null)
-          })
-          it('> returns general error', () => {
-            const effect = TestBed.inject(ViewerStateControllerUseEffect)
-            expect(effect.navigateToRegion$).toBeObservable(
-              hot('a', {
-                a: generalActionError({
-                  message: 'Go to region: region / atlas / template / parcellation not defined.'
-                })
-              })
-            )
-          })
-        })
-      })
-      describe('> if atlas, template, parc is set, but region unset', () => {
-        beforeEach(() => {
-          setAction(null)
-        })
-        it('> returns general error', () => {
-          const effect = TestBed.inject(ViewerStateControllerUseEffect)
-          expect(effect.navigateToRegion$).toBeObservable(
-            hot('a', {
-              a: generalActionError({
-                message: 'Go to region: region / atlas / template / parcellation not defined.'
-              })
-            })
-          )
-        })
-      })
-
-      describe('> if inputs are fine', () => {
-        let getRegionDetailSpy: jasmine.Spy
-        const region = {
-          name: 'foo bar'
-        }
-        beforeEach(() => {
-          getRegionDetailSpy = spyOn(mockPureConstantService, 'getRegionDetail').and.callThrough()
-          setAction(region)
-        })
-        afterEach(() => {
-          getRegionDetailSpy.calls.reset()
-        })
-
-        it('> getRegionDetailSpy is called', () => {
-          const ctrl = TestBed.inject(ViewerStateControllerUseEffect)
-
-          // necessary to trigger the emit
-          expect(
-            ctrl.navigateToRegion$
-          ).toBeObservable(
-            hot('a', {
-              a: generalActionError({
-                message: 'Fetching region detail error: Error: region does not have props defined!'
-              })
-            })
-          )
-
-          expect(getRegionDetailSpy).toHaveBeenCalled()
-        })
-
-        describe('> mal formed return', () => {
-          describe('> returns null', () => {
-            it('> generalactionerror', () => {
-              const ctrl = TestBed.inject(ViewerStateControllerUseEffect)
-              expect(
-                ctrl.navigateToRegion$
-              ).toBeObservable(
-                hot('a', {
-                  a: generalActionError({
-                    message: 'Fetching region detail error: Error: region does not have props defined!'
-                  })
-                })
-              )
-            })
-          })
-          describe('> general throw', () => {
-            const msg = 'oh no!'
-            beforeEach(() => {
-              getRegionDetailSpy.and.callFake(() => throwError(msg))
-            })
-
-            it('> generalactionerror', () => {
-              const ctrl = TestBed.inject(ViewerStateControllerUseEffect)
-              expect(
-                ctrl.navigateToRegion$
-              ).toBeObservable(
-                hot('a', {
-                  a: generalActionError({
-                    message: `Fetching region detail error: ${msg}`
-                  })
-                })
-              )
-            })
-
-          })
-          describe('> does not contain props attr', () => {
-
-            beforeEach(() => {
-              getRegionDetailSpy.and.callFake(() => of({
-                name: 'foo-bar'
-              }))
-            })
-
-            it('> generalactionerror', () => {
-              const ctrl = TestBed.inject(ViewerStateControllerUseEffect)
-              expect(
-                ctrl.navigateToRegion$
-              ).toBeObservable(
-                hot('a', {
-                  a: generalActionError({
-                    message: `Fetching region detail error: Error: region does not have props defined!`
-                  })
-                })
-              )
-            })
-          })
-
-          describe('> does not contain props.length === 0', () => {
-
-            beforeEach(() => {
-              getRegionDetailSpy.and.callFake(() => of({
-                name: 'foo-bar',
-                props: {}
-              }))
-            })
-
-            it('> generalactionerror', () => {
-              const ctrl = TestBed.inject(ViewerStateControllerUseEffect)
-              expect(
-                ctrl.navigateToRegion$
-              ).toBeObservable(
-                hot('a', {
-                  a: generalActionError({
-                    message: `Fetching region detail error: Error: region does not have props defined!`
-                  })
-                })
-              )
-            })
-          })
-        })
-
-        describe('> wellformed response', () => {
-          beforeEach(() => {
-
-            beforeEach(() => {
-              getRegionDetailSpy.and.callFake(() => of({
-                name: 'foo-bar',
-                props: {
-                  components: {
-                    centroid: [1,2,3]
-                  }
-                }
-              }))
-            })
-
-            it('> emits viewerStateChangeNavigation', () => {
-              const ctrl = TestBed.inject(ViewerStateControllerUseEffect)
-              expect(
-                ctrl.navigateToRegion$
-              ).toBeObservable(
-                hot('a', {
-                  a: viewerStateChangeNavigation({
-                    navigation: {
-                      position: [1e6,2e6,3e6],
-                      animation: {}
-                    }
-                  })
-                })
-              )
-            })
-          })
-        })
-      })
-    })
-  
-    describe('> onSelectAtlasSelectTmplParc$', () => {
-      let mockStore: MockStore
-      beforeEach(() => {
-        mockStore = TestBed.inject(MockStore)
-      })
-
-      it('> if atlas not found, return general error', () => {
-        mockStore.overrideSelector(viewerStateFetchedTemplatesSelector, [])
-        mockStore.overrideSelector(viewerStateFetchedAtlasesSelector, [])
-        actions$ = hot('a', {
-          a: viewerStateSelectAtlas({
-            atlas: {
-              ['@id']: 'foo-bar',
-            }
-          })
-        })
-        
-        const viewerSTateCtrlEffect = TestBed.inject(ViewerStateControllerUseEffect)
-        expect(
-          viewerSTateCtrlEffect.onSelectAtlasSelectTmplParc$
-        ).toBeObservable(
-          hot('a', {
-            a: generalActionError({
-              message: CONST.ATLAS_NOT_FOUND
-            })
-          })
-        )
-      })
-    
-      describe('> if atlas found', () => {
-        const mockParc1 = {
-          ['@id']: 'parc-1',
-          availableIn: [{
-            ['@id']: 'test-1'
-          }]
-        }
-        const mockParc0 = {
-          ['@id']: 'parc-0',
-          availableIn: [{
-            ['@id']: 'hello world'
-          }]
-        }
-        const mockTmplSpc = {
-          ['@id']: 'hello world',
-          availableIn: [ mockParc0 ]
-        }
-        const mockTmplSpc1 = {
-          ['@id']: 'test-1',
-          availableIn: [ mockParc1 ]
-        }
-
-        describe('> if template key val is not provided', () => {
-          describe('> will try to find the id of the first tmpl', () => {
-
-            it('> if fails, will return general error', () => {
-
-              mockStore.overrideSelector(viewerStateFetchedTemplatesSelector, [
-                mockTmplSpc1
-              ])
-              mockStore.overrideSelector(viewerStateFetchedAtlasesSelector, [{
-                ['@id']: 'foo-bar',
-                templateSpaces: [ mockTmplSpc ],
-                parcellations: [ mockParc0 ]
-              }])
-              actions$ = hot('a', {
-                a: viewerStateSelectAtlas({
-                  atlas: {
-                    ['@id']: 'foo-bar',
-                  }
-                })
-              })
-              
-              const viewerSTateCtrlEffect = TestBed.inject(ViewerStateControllerUseEffect)
-              expect(
-                viewerSTateCtrlEffect.onSelectAtlasSelectTmplParc$
-              ).toBeObservable(
-                hot('a', {
-                  a: generalActionError({
-                    message: CONST.TEMPLATE_NOT_FOUND
-                  })
-                })
-              )
-            })
-          
-            it('> if succeeds, will dispatch new viewer', () => {
-              const completeMocktmpl = {
-                ...mockTmplSpc1,
-                parcellations: [ mockParc1 ]
-              }
-              mockStore.overrideSelector(viewerStateFetchedTemplatesSelector, [
-                completeMocktmpl
-              ])
-              mockStore.overrideSelector(viewerStateFetchedAtlasesSelector, [{
-                ['@id']: 'foo-bar',
-                templateSpaces: [ mockTmplSpc1 ],
-                parcellations: [ mockParc1 ]
-              }])
-              actions$ = hot('a', {
-                a: viewerStateSelectAtlas({
-                  atlas: {
-                    ['@id']: 'foo-bar',
-                  }
-                })
-              })
-              
-              const viewerSTateCtrlEffect = TestBed.inject(ViewerStateControllerUseEffect)
-              expect(
-                viewerSTateCtrlEffect.onSelectAtlasSelectTmplParc$
-              ).toBeObservable(
-                hot('a', {
-                  a: viewerStateNewViewer({
-                    selectTemplate: completeMocktmpl,
-                    selectParcellation: mockParc1
-                  })
-                })
-              )
-            })
-      
-          })
-        })
-
-        describe('> if template key val is provided', () => {
-
-          const completeMockTmpl = {
-            ...mockTmplSpc,
-            parcellations: [ mockParc0 ]
-          }
-          const completeMocktmpl1 = {
-            ...mockTmplSpc1,
-            parcellations: [ mockParc1 ]
-          }
-          beforeEach(() => {
-
-            mockStore.overrideSelector(viewerStateFetchedTemplatesSelector, [
-              completeMockTmpl,
-              completeMocktmpl1,
-            ])
-            mockStore.overrideSelector(viewerStateFetchedAtlasesSelector, [{
-              ['@id']: 'foo-bar',
-              templateSpaces: [ mockTmplSpc, mockTmplSpc1 ],
-              parcellations: [ mockParc0, mockParc1 ]
-            }])
-          })
-          it('> will select template.@id', () => {
-
-            actions$ = hot('a', {
-              a: viewerStateSelectAtlas({
-                atlas: {
-                  ['@id']: 'foo-bar',
-                  template: {
-                    ['@id']: mockTmplSpc1['@id']
-                  }
-                }
-              })
-            })
-            
-            const viewerSTateCtrlEffect = TestBed.inject(ViewerStateControllerUseEffect)
-            expect(
-              viewerSTateCtrlEffect.onSelectAtlasSelectTmplParc$
-            ).toBeObservable(
-              hot('a', {
-                a: viewerStateNewViewer({
-                  selectTemplate: completeMocktmpl1,
-                  selectParcellation: mockParc1
-                })
-              })
-            )
-
-          })
-          
-          it('> if template.@id is not defined, will fallback to first template', () => {
-
-            actions$ = hot('a', {
-              a: viewerStateSelectAtlas({
-                atlas: {
-                  ['@id']: 'foo-bar',
-                  template: {
-                    
-                  } as any
-                }
-              })
-            })
-
-            const viewerSTateCtrlEffect = TestBed.inject(ViewerStateControllerUseEffect)
-            expect(
-              viewerSTateCtrlEffect.onSelectAtlasSelectTmplParc$
-            ).toBeObservable(
-              hot('a', {
-                a: viewerStateNewViewer({
-                  selectTemplate: completeMockTmpl,
-                  selectParcellation: mockParc0
-                })
-              })
-            )
-
-          })
-        })
-      })
-    })
-  })
-
-  describe('> cvtNehubaConfigToNavigationObj', () => {
-    describe('> returns default obj when input is malformed', () => {
-      it('> if no arg is provided', () => {
-
-        const obj = cvtNehubaConfigToNavigationObj()
-        expect(obj).toEqual(defaultNavigationObject)
-      })
-      it('> if null or undefined is provided', () => {
-
-        const obj = cvtNehubaConfigToNavigationObj(null)
-        expect(obj).toEqual(defaultNavigationObject)
-
-        const obj2 = cvtNehubaConfigToNavigationObj(undefined)
-        expect(obj2).toEqual(defaultNavigationObject)
-      })
-      it('> if malformed', () => {
-        
-        const obj = cvtNehubaConfigToNavigationObj(dummyTmpl2)
-        expect(obj).toEqual(defaultNavigationObject)
-
-        const obj2 = cvtNehubaConfigToNavigationObj({})
-        expect(obj2).toEqual(defaultNavigationObject)
-      })
-    })
-    it('> converts nehubaConfig object to navigation object', () => {
-      const obj = cvtNehubaConfigToNavigationObj(dummyTmpl2.nehubaConfig.dataset.initialNgState)
-      expect(obj).toEqual(defaultNavigationObject)
-    })
-  })
-
-})
diff --git a/src/state/effects/viewerState.useEffect.ts b/src/state/effects/viewerState.useEffect.ts
deleted file mode 100644
index 7c590f0c46922146b5c1b8471d219b24e5a335e5..0000000000000000000000000000000000000000
--- a/src/state/effects/viewerState.useEffect.ts
+++ /dev/null
@@ -1,466 +0,0 @@
-import { Injectable, OnDestroy } from "@angular/core";
-import { Actions, Effect, ofType } from "@ngrx/effects";
-import { Action, select, Store } from "@ngrx/store";
-import { Observable, Subscription, of, merge } from "rxjs";
-import { distinctUntilChanged, filter, map, shareReplay, withLatestFrom, switchMap, mapTo, startWith, catchError } from "rxjs/operators";
-import { FETCHED_TEMPLATE, IavRootStoreInterface, SELECT_PARCELLATION, SELECT_REGIONS, generalActionError } from "src/services/stateStore.service";
-import { TemplateCoordinatesTransformation } from "src/services/templateCoordinatesTransformation.service";
-import { CLEAR_STANDALONE_VOLUMES } from "src/services/state/viewerState.store";
-import { viewerStateToggleRegionSelect, viewerStateHelperSelectParcellationWithId, viewerStateSelectTemplateWithId, viewerStateNavigateToRegion, viewerStateSelectedTemplateSelector, viewerStateFetchedTemplatesSelector, viewerStateNewViewer, viewerStateSelectedParcellationSelector, viewerStateNavigationStateSelector, viewerStateSelectTemplateWithName, viewerStateSelectedRegionsSelector, viewerStateSelectAtlas } from "src/services/state/viewerState.store.helper";
-import { ngViewerSelectorClearViewEntries } from "src/services/state/ngViewerState/selectors";
-import { ngViewerActionClearView } from "src/services/state/ngViewerState/actions";
-import { PureContantService } from "src/util";
-import { CONST } from 'common/constants'
-import { viewerStateFetchedAtlasesSelector, viewerStateGetSelectedAtlas } from "src/services/state/viewerState/selectors";
-import { viewerStateChangeNavigation } from "src/services/state/viewerState/actions";
-import { cvtNavigationObjToNehubaConfig } from 'src/viewerModule/nehuba/util'
-import { getPosFromRegion } from "src/util/siibraApiConstants/fn";
-
-const defaultPerspectiveZoom = 1e6
-const defaultZoom = 1e6
-
-export const defaultNavigationObject = {
-  orientation: [0, 0, 0, 1],
-  perspectiveOrientation: [0.5, -0.5, -0.5, 0.5],
-  perspectiveZoom: defaultPerspectiveZoom,
-  zoom: defaultZoom,
-  position: [0, 0, 0],
-  positionReal: true
-}
-
-export const defaultNehubaConfigObject = {
-  perspectiveOrientation: [0.5, -0.5, -0.5, 0.5],
-  perspectiveZoom: 1e6,
-  navigation: {
-    pose: {
-      position: {
-        voxelCoordinates: [0, 0, 0],
-        voxelSize: [1,1,1]
-      },
-      orientation: [0, 0, 0, 1],
-    },
-    zoomFactor: defaultZoom
-  }
-}
-
-export function cvtNehubaConfigToNavigationObj(nehubaConfig?){
-  const {
-    navigation,
-    perspectiveOrientation = defaultNavigationObject.perspectiveOrientation,
-    perspectiveZoom = defaultNavigationObject.perspectiveZoom
-  } = nehubaConfig || {}
-  const { pose, zoomFactor = 1e6 } = navigation || {}
-  const { position, orientation = [0, 0, 0, 1] } = pose || {}
-  const { voxelSize = [1, 1, 1], voxelCoordinates = [0, 0, 0] } = position || {}
-
-  return {
-    orientation,
-    perspectiveOrientation: perspectiveOrientation,
-    perspectiveZoom: perspectiveZoom,
-    zoom: zoomFactor,
-    position: [0, 1, 2].map(idx => voxelSize[idx] * voxelCoordinates[idx]),
-    positionReal: true
-  }
-}
-
-@Injectable({
-  providedIn: 'root',
-})
-
-export class ViewerStateControllerUseEffect implements OnDestroy {
-
-  private subscriptions: Subscription[] = []
-
-  private selectedRegions$: Observable<any[]>
-
-  @Effect()
-  public init$ = this.pureService.initFetchTemplate$.pipe(
-    map(fetchedTemplate => {
-      return {
-        type: FETCHED_TEMPLATE,
-        fetchedTemplate,
-      }
-    }),
-  )
-
-  @Effect()
-  public onSelectAtlasSelectTmplParc$ = this.actions$.pipe(
-    ofType(viewerStateSelectAtlas.type),
-    switchMap(action => this.pureService.allFetchingReady$.pipe(
-      filter(v => !!v),
-      mapTo(action)
-    )),
-    withLatestFrom(
-      this.store$.pipe(
-        select(viewerStateFetchedTemplatesSelector),
-        startWith([])
-      ),
-      this.store$.pipe(
-        select(viewerStateFetchedAtlasesSelector),
-        startWith([])
-      )
-    ),
-    map(([action, fetchedTemplates, fetchedAtlases ])=> {
-
-      const { atlas: atlasObj } = action as any
-      const atlas = fetchedAtlases.find(a => a['@id'] === atlasObj['@id'])
-      if (!atlas) {
-        return generalActionError({
-          message: CONST.ATLAS_NOT_FOUND
-        })
-      }
-      /**
-       * selecting atlas means selecting the first available templateSpace
-       */
-      const targetTmplSpcId = atlasObj['template']?.['@id']
-      const templateTobeSelected = (
-        targetTmplSpcId
-        && atlas.templateSpaces.find(t => t['@id'] === targetTmplSpcId)
-      ) || atlas.templateSpaces[0]
-      
-      const templateSpaceId = templateTobeSelected['@id']
-      const atlasTmpl = atlas.templateSpaces.find(t => t['@id'] === templateSpaceId)
-
-      const templateSelected = fetchedTemplates.find(t => templateSpaceId === t['@id'])
-      if (!templateSelected) {
-        return generalActionError({
-          message: CONST.TEMPLATE_NOT_FOUND
-        })
-      }
-
-      const atlasParcs = atlasTmpl.availableIn
-        .map(availP => atlas.parcellations.find(p => availP['@id'] === p['@id']))
-        .filter(fullP => !!fullP)
-      const atlasParc = atlasParcs.find(p => {
-        if (!p.baseLayer) return false
-        if (p['@version']) {
-          return !p['@version']['@next']
-        }
-        return true
-      }) || templateSelected.parcellations[0]
-      const parcellationId = atlasParc && atlasParc['@id']
-      const parcellationSelected = parcellationId && templateSelected.parcellations.find(p => p['@id'] === parcellationId)
-      return viewerStateNewViewer({
-        selectTemplate: templateSelected,
-        selectParcellation: parcellationSelected
-      })
-    })
-  )
-
-
-  @Effect()
-  public selectParcellation$: Observable<any>
-
-  private selectTemplateIntent$: Observable<any> = merge(
-    this.actions$.pipe(
-      ofType(viewerStateSelectTemplateWithId.type),
-      map(({ payload, config }) => {
-        return {
-          templateId: payload['@id'],
-          parcellationId: config && config['selectParcellation'] && config['selectParcellation']['@id']
-        }
-      })
-    ),
-    this.actions$.pipe(
-      ofType(viewerStateSelectTemplateWithName),
-      withLatestFrom(this.store$.pipe(
-        select(viewerStateFetchedTemplatesSelector)
-      )),
-      map(([ action, fetchedTemplates ]) => {
-        const templateName = (action as any).payload.name
-        const foundTemplate = fetchedTemplates.find(t => t.name === templateName)
-        return foundTemplate && foundTemplate['@id']
-      }),
-      filter(v => !!v),
-      map(templateId => {
-        return { templateId, parcellationId: null }
-      })
-    )
-  )
-
-  @Effect()
-  public selectTemplate$: Observable<any> = this.selectTemplateIntent$.pipe(
-    withLatestFrom(
-      this.store$.pipe(
-        select(viewerStateFetchedTemplatesSelector)
-      ),
-      this.store$.pipe(
-        select(viewerStateSelectedParcellationSelector)
-      )
-    ),
-    map(([ { templateId, parcellationId }, fetchedTemplates, parcellationSelected ]) => {
-      /**
-       * find the correct template & parcellation from their IDs
-       */
-
-      /**
-       * for template, just look for the new id in fetched templates
-       */
-      const newTemplateTobeSelected = fetchedTemplates.find(t => t['@id'] === templateId)
-      if (!newTemplateTobeSelected) {
-        return {
-          selectTemplate: null,
-          selectParcellation: null,
-          errorMessage: `Selected templateId ${templateId} not found.`
-        }
-      }
-
-      /**
-       * for parcellation,
-       * if new parc id is defined, try to find the corresponding parcellation in the new template
-       * if above fails, try to find the corresponding parcellation of the currently selected parcellation
-       * if the above fails, select the first parcellation in the new template
-       */
-      const selectParcellationWithTemplate = (parcellationId && newTemplateTobeSelected['parcellations'].find(p => p['@id'] === parcellationId))
-        || (parcellationSelected && parcellationSelected['@id'] && newTemplateTobeSelected['parcellations'].find(p => p['@id'] === parcellationSelected['@id']))
-        || newTemplateTobeSelected.parcellations[0]
-
-      return {
-        selectTemplate: newTemplateTobeSelected,
-        selectParcellation: selectParcellationWithTemplate
-      }
-    }),
-    withLatestFrom(
-      this.store$.pipe(
-        select(viewerStateSelectedTemplateSelector),
-        startWith(null as any),
-      ),
-      this.store$.pipe(
-        select(viewerStateNavigationStateSelector),
-        startWith(null as any),
-      )
-    ),
-    switchMap(([{ selectTemplate, selectParcellation, errorMessage }, lastSelectedTemplate, navigation]) => {
-      /**
-       * if selectTemplate is undefined (cannot find template with id)
-       */
-      if (errorMessage) {
-        return of(generalActionError({
-          message: errorMessage || 'Switching template error.',
-        }))
-      }
-      /**
-       * if there were no template selected last
-       * simply return selectTemplate object
-       */
-      if (!lastSelectedTemplate) {
-        return of(viewerStateNewViewer({
-          selectParcellation,
-          selectTemplate,
-        }))
-      }
-
-      /**
-       * if there were template selected last, extract navigation info
-       */
-      const previousNavigation = (navigation && Object.keys(navigation).length > 0 && navigation) || cvtNehubaConfigToNavigationObj(lastSelectedTemplate.nehubaConfig?.dataset?.initialNgState)
-      return this.coordinatesTransformation.getPointCoordinatesForTemplate(lastSelectedTemplate.name, selectTemplate.name, previousNavigation.position).pipe(
-        map(({ status, result }) => {
-
-          /**
-           * if getPointCoordinatesForTemplate returns error, simply load the temp/parc
-           */
-          if (status === 'error') {
-            return viewerStateNewViewer({
-              selectParcellation,
-              selectTemplate,
-            })
-          }
-
-          /**
-           * otherwise, copy the nav state to templateSelected
-           * deepclone of json object is required, or it will mutate the fetchedTemplate
-           * setting navigation sometimes creates a race con, as creating nehubaViewer is not sync
-           */
-          const deepCopiedState = JSON.parse(JSON.stringify(selectTemplate))
-          const initialNgState = deepCopiedState.nehubaConfig.dataset.initialNgState
-
-          const newInitialNgState = cvtNavigationObjToNehubaConfig({
-            ...previousNavigation,
-            position: result
-          }, initialNgState)
-
-          /**
-           * mutation of initialNgState is expected here
-           */
-          deepCopiedState.nehubaConfig.dataset.initialNgState = {
-            ...initialNgState,
-            ...newInitialNgState
-          }
-
-          return viewerStateNewViewer({
-            selectTemplate: deepCopiedState,
-            selectParcellation,
-          })
-        })
-      )
-    })
-  )
-
-  @Effect()
-  public toggleRegionSelection$: Observable<any>
-
-  @Effect()
-  public navigateToRegion$: Observable<any>
-
-  @Effect()
-  public onTemplateSelectClearStandAloneVolumes$: Observable<any>
-
-  @Effect()
-  public onTemplateSelectUnsetAllClearQueues$: Observable<any> = this.store$.pipe(
-    select(viewerStateSelectedTemplateSelector),
-    withLatestFrom(this.store$.pipe(
-      select(ngViewerSelectorClearViewEntries)
-    )),
-    map(([_, clearViewQueue]) => {
-      const newVal = {}
-      for (const key of clearViewQueue) {
-        newVal[key] = false
-      }
-      return ngViewerActionClearView({
-        payload: newVal
-      })
-    })
-  )
-
-  constructor(
-    private actions$: Actions,
-    private store$: Store<IavRootStoreInterface>,
-    private pureService: PureContantService,
-    private coordinatesTransformation: TemplateCoordinatesTransformation
-  ) {
-    const viewerState$ = this.store$.pipe(
-      select('viewerState'),
-      shareReplay(1),
-    )
-
-    this.selectedRegions$ = viewerState$.pipe(
-      select('regionsSelected'),
-      distinctUntilChanged(),
-    )
-
-    this.onTemplateSelectClearStandAloneVolumes$ = this.store$.pipe(
-      select(viewerStateSelectedTemplateSelector),
-      distinctUntilChanged(),
-      mapTo({ type: CLEAR_STANDALONE_VOLUMES })
-    )
-
-    /**
-     * merge all sources of selecting parcellation into parcellation id
-     */
-    this.selectParcellation$ = merge(
-
-      /**
-       * listening on action
-       */
-
-      this.actions$.pipe(
-        ofType(viewerStateHelperSelectParcellationWithId.type),
-        map(({ payload }) => payload['@id'])
-      ),
-
-    ).pipe(
-      withLatestFrom(viewerState$.pipe(
-        select('templateSelected'),
-      )),
-      map(([id, templateSelected]) => {
-        const { parcellations: availableParcellations } = templateSelected
-        const newParcellation = availableParcellations.find(t => t['@id'] === id)
-        if (!newParcellation) {
-          return generalActionError({
-            message: 'Selected parcellation not found.'
-          })
-        }
-        return {
-          type: SELECT_PARCELLATION,
-          selectParcellation: newParcellation,
-        }
-      })
-    )
-
-    this.navigateToRegion$ = this.actions$.pipe(
-      ofType(viewerStateNavigateToRegion),
-      map(action => action.payload?.region),
-      withLatestFrom(
-        this.store$.pipe(
-          select(viewerStateGetSelectedAtlas)
-        ),
-        this.store$.pipe(
-          select(viewerStateSelectedTemplateSelector)
-        ),
-        this.store$.pipe(
-          select(viewerStateSelectedParcellationSelector)
-        )
-      ),
-      switchMap(([ region,  selectedAtlas, selectedTemplate, selectedParcellation ]) => {
-        if (!region || !selectedAtlas || !selectedTemplate || !selectedParcellation) {
-          return of(
-            generalActionError({
-              message: `Go to region: region / atlas / template / parcellation not defined.`
-            })
-          )
-        }
-        return this.pureService.getRegionDetail(selectedAtlas['@id'], selectedParcellation['@id'], selectedTemplate['@id'], region).pipe(
-          map(regDetail => {
-            const position = getPosFromRegion(regDetail)
-            if (!position) throw new Error(`region does not have props defined!`)
-            
-            return viewerStateChangeNavigation({
-              navigation: {
-                position,
-                animation: {},
-              }
-            })
-          }),
-          catchError((err) => of(
-            generalActionError({
-              message: `Fetching region detail error: ${err}`
-            })
-          ))
-        )
-      }),
-    )
-
-    this.toggleRegionSelection$ = this.actions$.pipe(
-      ofType(viewerStateToggleRegionSelect.type),
-      withLatestFrom(this.selectedRegions$),
-      map(([action, regionsSelected]) => {
-
-        const { payload = {} } = action as ViewerStateAction
-        const { region } = payload
-
-        /**
-         * if region does not have labelIndex (not tree leaf), for now, return error
-         */
-        if (!region.labelIndex) {
-          return generalActionError({
-            message: 'Currently, only regions at the lowest hierarchy can be selected.'
-          })
-        }
-
-        /**
-         * if the region is already selected, deselect it
-         * if the region is not yet selected, deselect any existing region, and select this region
-         */
-        const roiIsSelected = !!regionsSelected.find(r => r.name === region.name)
-        return {
-          type: SELECT_REGIONS,
-          selectRegions: roiIsSelected
-            ? []
-            : [ region ]
-        }
-      }),
-    )
-  }
-
-  public ngOnDestroy() {
-    while (this.subscriptions.length > 0) {
-      this.subscriptions.pop().unsubscribe()
-    }
-  }
-}
-
-interface ViewerStateAction extends Action {
-  payload: any
-  config: any
-}
diff --git a/src/state/index.ts b/src/state/index.ts
index 778709a1c7cdd5a85c71824a18b8c2f1aff1df07..3eba9c31857ad2db855c3c9c8e4c461c8fd488ee 100644
--- a/src/state/index.ts
+++ b/src/state/index.ts
@@ -1,5 +1,91 @@
+import { ActionReducer, StoreModule } from "@ngrx/store"
+
 export { StateModule } from "./state.module"
+
+import * as atlasSelection from "./atlasSelection"
+import * as annotation from "./annotations"
+import * as userInterface from "./userInterface"
+import * as atlasAppearance from "./atlasAppearance"
+import * as plugins from "./plugins"
+import * as userInteraction from "./userInteraction"
+import * as userPreference from "./userPreference"
+
 export {
-  ViewerStateControllerUseEffect,
-  cvtNehubaConfigToNavigationObj,
-} from "./effects/viewerState.useEffect"
\ No newline at end of file
+  atlasSelection,
+  annotation,
+  userInterface,
+  atlasAppearance,
+  plugins,
+  userInteraction,
+  userPreference,
+}
+
+export * as generalActions from "./actions"
+
+function debug(reducer: ActionReducer<any>): ActionReducer<any> {
+  return function(state, action) {
+    console.log('state', state);
+    console.log('action', action);
+ 
+    return reducer(state, action);
+  };
+}
+
+function generalApplyStateReducer(reducer: ActionReducer<MainState>): ActionReducer<MainState> {
+  return function(_state, action) {
+    let state = _state
+    if (action.type === generalApplyState.type) {
+      state = JSON.parse(
+        JSON.stringify(
+          (action as any).state
+        )
+      ) 
+    }
+    return reducer(state, action)
+  }
+}
+
+export const RootStoreModule = StoreModule.forRoot({
+  [userPreference.nameSpace]: userPreference.reducer,
+  [atlasSelection.nameSpace]: atlasSelection.reducer,
+  [userInterface.nameSpace]: userInterface.reducer,
+  [userInteraction.nameSpace]: userInteraction.reducer,
+  [annotation.nameSpace]: annotation.reducer,
+  [plugins.nameSpace]: plugins.reducer,
+  [atlasAppearance.nameSpace]: atlasAppearance.reducer,
+},{
+  metaReducers: [ 
+    generalApplyStateReducer,
+    // debug,
+  ]
+})
+
+/**
+ * 
+ * We have to use a function here. At import time, *.Effect(s) 
+ * would not yet be defined.
+ * 
+ * @returns Effects from state
+ */
+export function getStoreEffects() {
+  return [
+    plugins.Effects,
+    atlasSelection.Effect,
+    userInterface.Effects,
+  ]
+}
+
+import { MainState } from "./const"
+import { generalApplyState } from "./actions"
+
+export { MainState }
+
+export const defaultState: MainState = {
+  [userPreference.nameSpace]: userPreference.defaultState,
+  [atlasSelection.nameSpace]: atlasSelection.defaultState,
+  [userInterface.nameSpace]: userInterface.defaultState,
+  [userInteraction.nameSpace]: userInteraction.defaultState,
+  [annotation.nameSpace]: annotation.defaultState,
+  [plugins.nameSpace]: plugins.defaultState,
+  [atlasAppearance.nameSpace]: atlasAppearance.defaultState,
+}
diff --git a/src/state/plugins/actions.ts b/src/state/plugins/actions.ts
new file mode 100644
index 0000000000000000000000000000000000000000..759c7523082503c38eaed07b9ff92f34b1b3e775
--- /dev/null
+++ b/src/state/plugins/actions.ts
@@ -0,0 +1,9 @@
+import { createAction, props } from "@ngrx/store";
+import { nameSpace } from "./const"
+
+export const clearInitManifests = createAction(
+  `${nameSpace} clearInitManifests`,
+  props<{
+    nameSpace: string
+  }>()
+)
diff --git a/src/state/plugins/const.ts b/src/state/plugins/const.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7b004e658ddc8e803a00c892e32a82a6a57499be
--- /dev/null
+++ b/src/state/plugins/const.ts
@@ -0,0 +1,2 @@
+export const nameSpace = `[state.plugins]`
+export const INIT_MANIFEST_SRC = `__INIT_MANFEST_SRC__`
diff --git a/src/state/plugins/effects.spec.ts b/src/state/plugins/effects.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..98d4d78ebcba3ec7253691f5e4b5fcd89d3a4ff4
--- /dev/null
+++ b/src/state/plugins/effects.spec.ts
@@ -0,0 +1,130 @@
+import { fakeAsync, TestBed, tick } from "@angular/core/testing";
+import { HttpClientModule, HTTP_INTERCEPTORS, HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpResponse, HttpHeaders } from "@angular/common/http";
+import { Effects } from "./effects";
+import { Observable, of } from "rxjs";
+import { Action } from "@ngrx/store";
+import { provideMockActions } from "@ngrx/effects/testing";
+import { MockStore, provideMockStore } from "@ngrx/store/testing";
+import { Injectable } from "@angular/core";
+import { getRandomHex } from 'common/util'
+import { AngularMaterialModule } from "src/sharedModules";
+import { hot } from "jasmine-marbles";
+import * as actions from "./actions"
+import { INIT_MANIFEST_SRC } from "./const"
+import { DialogService } from "src/services/dialogService.service";
+import { selectors } from ".";
+import * as constants from "./const"
+
+const actions$: Observable<Action> = of({type: 'TEST'})
+
+const manifest = {
+  name: getRandomHex(),
+  templateURL: 'http://localhost:12345/template.html',
+  scriptURL: 'http://localhost:12345/script.js'
+}
+
+const template = getRandomHex()
+const script = getRandomHex()
+
+@Injectable()
+class HTTPInterceptorClass implements HttpInterceptor{
+  intercept(req: HttpRequest<any>, next: HttpHandler):Observable<HttpEvent<any>>{
+    if(req.url.indexOf('http://localhost:12345') >= 0) {
+      if (req.url.indexOf('manifest.json') >= 0) {
+
+        const headers = new HttpHeaders()
+        headers.set('content-type', 'application/json')
+        return of(new HttpResponse({
+          status: 200,
+          body: manifest,
+          headers
+        }))
+      }
+
+      if (req.url.indexOf('template.html') >= 0) {
+
+        const headers = new HttpHeaders()
+        headers.set('content-type', 'text/html')
+        return of(new HttpResponse({
+          status: 200,
+          body: template,
+          headers
+        }))
+      }
+
+      if (req.url.indexOf('script.js') >= 0) {
+
+        const headers = new HttpHeaders()
+        headers.set('content-type', 'application/javascript')
+        return of(new HttpResponse({
+          status: 200,
+          body: script,
+          headers
+        }))
+      }
+    } 
+    return next.handle(req)
+  }
+}
+
+@Injectable()
+class MockPluginService{
+  public launchNewWidget(arg) {
+    console.log('launch new widget')
+  }
+}
+
+// describe('pluginUseEffect.ts', () => {
+
+//   let spy: jasmine.Spy
+//   let mockStore: MockStore
+//   beforeEach(() => {
+//     TestBed.configureTestingModule({
+//       imports: [
+//         HttpClientModule,
+//         AngularMaterialModule
+//       ],
+//       providers: [
+//         Effects,
+//         provideMockActions(() => actions$),
+//         provideMockStore(),
+//         {
+//           provide: HTTP_INTERCEPTORS,
+//           useClass: HTTPInterceptorClass,
+//           multi: true
+//         },
+//         {
+//           provide: PluginServices,
+//           useClass: MockPluginService
+//         },{
+//           provide: DialogService,
+//           useValue: {
+//             getUserConfirm() {
+//               return Promise.resolve()
+//             }
+//           }
+//         }
+//       ]
+//     })
+//     mockStore = TestBed.inject(MockStore)
+//     mockStore.overrideSelector(selectors.initManfests, { [constants.INIT_MANIFEST_SRC]: "http://localhost:12345/manifest.json" })
+//     const pluginServices = TestBed.inject(PluginServices)
+//     spy = spyOn(pluginServices, 'launchNewWidget')
+//   })
+
+//   it('initManifests should fetch manifest.json', fakeAsync(() => {
+//     const effect = TestBed.inject(Effects)
+//     effect.initManLaunch.subscribe()
+//     expect(
+//       effect.initManClear
+//     ).toBeObservable(
+//       hot('a', {
+//         a: actions.clearInitManifests({
+//           nameSpace: INIT_MANIFEST_SRC
+//         })
+//       })
+//     )
+//     tick(16)
+//     expect(spy).toHaveBeenCalledWith(manifest)
+//   }))
+// })
diff --git a/src/state/plugins/effects.ts b/src/state/plugins/effects.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5f147b9b17f29011441b3c92fb7c92a992cca1a2
--- /dev/null
+++ b/src/state/plugins/effects.ts
@@ -0,0 +1,64 @@
+import { Injectable } from "@angular/core";
+import { createEffect } from "@ngrx/effects";
+import { select, Store } from "@ngrx/store";
+import { catchError, filter, map, mapTo, switchMap } from "rxjs/operators";
+import * as constants from "./const"
+import * as selectors from "./selectors"
+import * as actions from "./actions"
+import { DialogService } from "src/services/dialogService.service";
+import { NEVER, of } from "rxjs";
+import { PluginService } from "src/plugin/service";
+
+@Injectable()
+export class Effects{
+
+  initMan = this.store.pipe(
+    select(selectors.initManfests),
+    map(initMan => initMan[constants.INIT_MANIFEST_SRC]),
+    filter(val => val && val.length > 0),
+  )
+
+  private pendingList = new Set<string>()
+  private launchedList = new Set<string>()
+  private banList = new Set<string>()
+
+  initManLaunch = createEffect(() => this.initMan.pipe(
+    switchMap(val => of(...val)),
+    switchMap(
+      url => {
+        if (this.pendingList.has(url)) return NEVER
+        if (this.launchedList.has(url)) return NEVER
+        if (this.banList.has(url)) return NEVER
+        this.pendingList.add(url)
+        return this.dialogSvc
+          .getUserConfirm({
+            message: `This URL is trying to open a plugin from ${url}. Proceed?`
+          })
+          .then(() => {
+            this.launchedList.add(url)
+            return this.svc.launchPlugin(url)
+          })
+          .finally(() => {
+            this.pendingList.delete(url)
+          })
+      }
+    ),
+    catchError(() => of(null))
+  ), { dispatch: false })
+
+  initManClear = createEffect(() => this.initMan.pipe(
+    mapTo(
+      actions.clearInitManifests({
+        nameSpace: constants.INIT_MANIFEST_SRC
+      })
+    )
+  ))
+
+  constructor(
+    private store: Store,
+    private dialogSvc: DialogService,
+    private svc: PluginService,
+  ){
+    
+  }
+}
diff --git a/src/state/plugins/index.ts b/src/state/plugins/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d7324fb2780ab0dbe36ab4968208ecf5a40b45a6
--- /dev/null
+++ b/src/state/plugins/index.ts
@@ -0,0 +1,5 @@
+export * as selectors from "./selectors"
+export * as actions from "./actions"
+export { reducer, PluginStore, defaultState } from "./store"
+export { Effects } from "./effects"
+export { nameSpace, INIT_MANIFEST_SRC } from "./const"
\ No newline at end of file
diff --git a/src/state/plugins/selectors.ts b/src/state/plugins/selectors.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6b70d2e7d5dcc500739a02e53abcf938f2ccc094
--- /dev/null
+++ b/src/state/plugins/selectors.ts
@@ -0,0 +1,10 @@
+import { createSelector } from "@ngrx/store";
+import { PluginStore } from "./store"
+import { nameSpace } from "./const"
+
+const storeSelector = state => state[nameSpace] as PluginStore
+
+export const initManfests = createSelector(
+  storeSelector,
+  state => state.initManifests
+)
diff --git a/src/state/plugins/store.ts b/src/state/plugins/store.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7283a77a4f74431800af70c0360b0c8dfd2204f1
--- /dev/null
+++ b/src/state/plugins/store.ts
@@ -0,0 +1,30 @@
+import { createReducer, on } from "@ngrx/store";
+import * as actions from "./actions"
+
+export type PluginStore = {
+  initManifests: Record<string, string[]>
+}
+
+export const defaultState: PluginStore = {
+  initManifests: {}
+}
+
+export const reducer = createReducer(
+  defaultState,
+  on(
+    actions.clearInitManifests,
+    (state, { nameSpace }) => {
+      if (!state.initManifests[nameSpace]) return state
+      const newMan: Record<string, string[]> = {}
+      const { initManifests } = state
+      for (const key in initManifests) {
+        if (key === nameSpace) continue
+        newMan[key] = initManifests[key]
+      }
+      return {
+        ...state,
+        initManifests: newMan
+      }
+    }
+  ),
+)
diff --git a/src/state/userInteraction/actions.ts b/src/state/userInteraction/actions.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3df6c1b9780d6d0f9fde7e14e205a0d179445093
--- /dev/null
+++ b/src/state/userInteraction/actions.ts
@@ -0,0 +1,37 @@
+import { createAction, props } from "@ngrx/store"
+import { nameSpace } from "./const"
+import { SapiRegionModel, SapiFeatureModel, OpenMINDSCoordinatePoint } from "src/atlasComponents/sapi"
+
+export const mouseOverAnnotations = createAction(
+  `${nameSpace} mouseOverAnnotations`,
+  props<{
+    annotations: {
+      "@id": string
+    }[]
+  }>()
+)
+
+export const mouseoverRegions = createAction(
+  `${nameSpace} mouseoverRegions`,
+  props<{
+    regions: SapiRegionModel[]
+  }>()
+)
+
+export const mouseoverPosition = createAction(
+  `${nameSpace} mouseoverPosition`,
+  props<{
+    position: OpenMINDSCoordinatePoint
+  }>()
+)
+
+export const showFeature = createAction(
+  `${nameSpace} showFeature`,
+  props<{
+    feature: SapiFeatureModel
+  }>()
+)
+
+export const clearShownFeature = createAction(
+  `${nameSpace} clearShownFeature`,
+)
diff --git a/src/state/userInteraction/const.ts b/src/state/userInteraction/const.ts
new file mode 100644
index 0000000000000000000000000000000000000000..322454b4575be2ad44f6f7a251a46fc087db1e0c
--- /dev/null
+++ b/src/state/userInteraction/const.ts
@@ -0,0 +1 @@
+export const nameSpace = `[state.userInteraction]`
\ No newline at end of file
diff --git a/src/state/userInteraction/effects.ts b/src/state/userInteraction/effects.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5d4986ad5965117705e6d1aa869407fabdbd8a65
--- /dev/null
+++ b/src/state/userInteraction/effects.ts
@@ -0,0 +1,17 @@
+import { Injectable } from "@angular/core";
+import { Actions, createEffect, ofType } from "@ngrx/effects";
+import * as atlasSelectionActions from "../atlasSelection/actions"
+import * as userInterface from "../userInterface"
+import { mapTo } from "rxjs/operators";
+
+@Injectable()
+export class Effect {
+  onStandAloneVolumesExistCloseMatDrawer = createEffect(() => this.action.pipe(
+    ofType(atlasSelectionActions.clearStandAloneVolumes),
+    mapTo(userInterface.actions.closeSidePanel())
+  ))
+
+  constructor(private action: Actions){
+
+  }
+}
\ No newline at end of file
diff --git a/src/state/userInteraction/index.ts b/src/state/userInteraction/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..44d760341b10d264fcdda4ee28ad157b760a705b
--- /dev/null
+++ b/src/state/userInteraction/index.ts
@@ -0,0 +1,5 @@
+export { Effect } from "./effects"
+export { nameSpace } from "./const"
+export * as actions from "./actions"
+export * as selectors from "./selectors"
+export { reducer, UserInteraction, defaultState } from "./store"
\ No newline at end of file
diff --git a/src/state/userInteraction/selectors.ts b/src/state/userInteraction/selectors.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0f43a1a4c56fd0188d90c4fa4fb37f491daa35f3
--- /dev/null
+++ b/src/state/userInteraction/selectors.ts
@@ -0,0 +1,20 @@
+import { createSelector } from "@ngrx/store";
+import { nameSpace } from "./const"
+import { UserInteraction } from "./store";
+
+const selectStore = state => state[nameSpace] as UserInteraction
+
+export const mousingOverRegions = createSelector(
+  selectStore,
+  state => state.mouseoverRegions
+)
+
+export const selectedFeature = createSelector(
+  selectStore,
+  state => state.selectedFeature
+)
+
+export const mousingOverPosition = createSelector(
+  selectStore,
+  state => state.mouseoverPosition
+)
diff --git a/src/state/userInteraction/store.ts b/src/state/userInteraction/store.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ab75700289c279969ac2bba0eaafe3a6da3286e4
--- /dev/null
+++ b/src/state/userInteraction/store.ts
@@ -0,0 +1,55 @@
+import { createReducer, on } from "@ngrx/store";
+import { SapiRegionModel, SapiFeatureModel, OpenMINDSCoordinatePoint } from "src/atlasComponents/sapi";
+import * as actions from "./actions"
+
+export type UserInteraction = {
+  mouseoverRegions: SapiRegionModel[]
+  selectedFeature: SapiFeatureModel
+  mouseoverPosition: OpenMINDSCoordinatePoint
+}
+
+export const defaultState: UserInteraction = {
+  selectedFeature: null,
+  mouseoverRegions: [],
+  mouseoverPosition: null
+}
+
+export const reducer = createReducer(
+  defaultState,
+  on(
+    actions.mouseoverRegions,
+    (state, { regions }) => {
+      return {
+        ...state,
+        mouseoverRegions: regions
+      }
+    }
+  ),
+  on(
+    actions.showFeature,
+    (state, { feature }) => {
+      return {
+        ...state,
+        selectedFeature: feature
+      }
+    }
+  ),
+  on(
+    actions.clearShownFeature,
+    state => {
+      return {
+        ...state,
+        selectedFeature: null
+      }
+    }
+  ),
+  on(
+    actions.mouseoverPosition,
+    (state, { position }) => {
+      return {
+        ...state,
+        mouseoverPosition: position
+      }
+    }
+  )
+)
diff --git a/src/state/userInterface/actions.ts b/src/state/userInterface/actions.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e423f261e6ef1ee6ffb35a9f0464eda261153f35
--- /dev/null
+++ b/src/state/userInterface/actions.ts
@@ -0,0 +1,60 @@
+import { TemplateRef } from "@angular/core";
+import { MatBottomSheetConfig } from "@angular/material/bottom-sheet";
+import { MatSnackBarConfig } from "@angular/material/snack-bar";
+import { createAction, props } from "@ngrx/store";
+import { nameSpace, PanelMode } from "./const"
+
+
+export const openSidePanel = createAction(
+  `${nameSpace} openSidePanel`
+)
+
+export const closeSidePanel = createAction(
+  `${nameSpace} closeSidePanel`
+)
+
+export const expandSidePanelDetailView = createAction(
+  `${nameSpace} expandDetailView`
+)
+
+export const showBottomSheet = createAction(
+  `${nameSpace} showBottomSheet`,
+  props<{
+    template: TemplateRef<any>
+    config?: MatBottomSheetConfig
+  }>()
+)
+
+export const snackBarMessage = createAction(
+  `${nameSpace} snackBarMessage`,
+  props<{
+    message: string
+    config?: MatSnackBarConfig
+  }>()
+)
+
+
+export const setPanelMode = createAction(
+  `${nameSpace} setPanelMode`,
+  props<{
+    panelMode: PanelMode
+  }>()
+)
+
+export const cyclePanelMode = createAction(
+  `${nameSpace} cyclePanelMode`
+)
+
+export const toggleMaximiseView = createAction(
+  `${nameSpace} toggleMaximiseView`,
+  props<{
+    targetIndex: number
+  }>()
+)
+
+export const setPanelOrder = createAction(
+  `${nameSpace} setPanelOrder`,
+  props<{
+    order: string
+  }>()
+)
\ No newline at end of file
diff --git a/src/state/userInterface/const.ts b/src/state/userInterface/const.ts
new file mode 100644
index 0000000000000000000000000000000000000000..929102239aefb764442a329351c02542a293fe8b
--- /dev/null
+++ b/src/state/userInterface/const.ts
@@ -0,0 +1,5 @@
+export const nameSpace = `[state.ui]`
+export type PanelMode = 'FOUR_PANEL'
+| 'V_ONE_THREE'
+| 'H_ONE_THREE'
+| 'SINGLE_PANEL'
\ No newline at end of file
diff --git a/src/state/userInterface/effects.ts b/src/state/userInterface/effects.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6a213f60a78cb2d648badd77d56be1f4a1e1e65a
--- /dev/null
+++ b/src/state/userInterface/effects.ts
@@ -0,0 +1,118 @@
+import { Injectable } from "@angular/core";
+import { MatBottomSheet, MatBottomSheetRef } from "@angular/material/bottom-sheet";
+import { MatSnackBar } from "@angular/material/snack-bar";
+import { Actions, createEffect, ofType } from "@ngrx/effects";
+import { select, Store } from "@ngrx/store";
+import { of } from "rxjs";
+import { filter, map, mapTo, pairwise, startWith, switchMap, tap, withLatestFrom } from "rxjs/operators";
+import { generalActionError } from "../actions";
+import { userInterface } from "..";
+import { selectors } from "../atlasSelection"
+import * as actions from "./actions"
+
+@Injectable()
+export class Effects{
+
+  freshRegionSelect = this.store.pipe(
+    select(selectors.selectedRegions),
+    map(selReg => selReg.length),
+    startWith(0),
+    pairwise(),
+    filter(([prev, curr]) => prev === 0 && curr > 0),
+  )
+
+  onFreshRegionSelectSidePanelOpen = createEffect(() => this.freshRegionSelect.pipe(
+    mapTo(actions.openSidePanel()),
+  ))
+
+  onFreshRegionSelectSidePanelDetailExpand = createEffect(() => this.freshRegionSelect.pipe(
+    mapTo(actions.expandSidePanelDetailView())
+  ))
+
+  onGeneralError = createEffect(() => this.action.pipe(
+    ofType(generalActionError.type),
+    tap(payload => {
+      this.snackbar.open(
+        (payload as any)?.message || `Error: cannot complete your action`,
+        'Dismiss',
+        { duration: 5000 }
+      )
+    })
+  ), { dispatch: false })
+
+  onShowBottomSheet = createEffect(() => this.action.pipe(
+    ofType(actions.showBottomSheet),
+    tap(({ template, config }) => {
+
+      if (this.bottomSheetRef) {
+        this.bottomSheetRef.dismiss()
+      }
+      this.bottomSheetRef = this.bottomsheet.open(
+        template,
+        config
+      )
+      this.bottomSheetRef.afterDismissed().subscribe(() => this.bottomSheetRef = null)
+    })
+  ), { dispatch: false })
+
+  onSnackbarMessage = createEffect(() => this.action.pipe(
+    ofType(actions.snackBarMessage),
+    tap(({ message, config }) => {
+      const _config = config || { duration: 5000 }
+      this.snackbar.open(message, "Dismiss", _config)
+    })
+  ), { dispatch: false })
+
+  onMaximiseView = createEffect(() => this.action.pipe(
+    ofType(actions.toggleMaximiseView),
+    withLatestFrom(
+      this.store.pipe(
+        select(userInterface.selectors.panelMode),
+      )
+    ),
+    switchMap(([ { targetIndex }, panelMode ]) => {
+      const newMode: userInterface.PanelMode = panelMode === "FOUR_PANEL"
+        ? "SINGLE_PANEL"
+        : "FOUR_PANEL"
+      const newOrder = newMode === "FOUR_PANEL"
+        ? "0123"
+        : "0123".split("").map(v => ((Number(v) + targetIndex) % 4).toString()).join("")
+      return of(
+        userInterface.actions.setPanelMode({
+          panelMode: newMode
+        }),
+        userInterface.actions.setPanelOrder({
+          order: newOrder
+        })
+      )
+    })
+  ))
+
+  onCycleView = createEffect(() => this.action.pipe(
+    ofType(userInterface.actions.cyclePanelMode),
+    withLatestFrom(
+      this.store.pipe(
+        select(userInterface.selectors.panelMode)
+      ),
+      this.store.pipe(
+        select(userInterface.selectors.panelOrder)
+      ),
+    ),
+    filter(([_, panelMode, _1]) => panelMode === "SINGLE_PANEL"),
+    map(([_, _1, panelOrder]) => userInterface.actions.setPanelOrder({
+      order: [
+        ...panelOrder.split('').slice(1),
+        panelOrder[0]
+      ].join('')
+    }))
+  ))
+
+  private bottomSheetRef: MatBottomSheetRef
+  constructor(
+    private store: Store,
+    private action: Actions,
+    private bottomsheet: MatBottomSheet,
+    private snackbar: MatSnackBar,
+  ){
+  }
+}
diff --git a/src/state/userInterface/index.ts b/src/state/userInterface/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..382825ba33d4ae79a439f9e8cce8fc909c020bf5
--- /dev/null
+++ b/src/state/userInterface/index.ts
@@ -0,0 +1,5 @@
+export * as actions from "./actions"
+export * as selectors from "./selectors"
+export { nameSpace, PanelMode } from "./const"
+export { reducer, UiStore, defaultState } from "./store"
+export { Effects } from "./effects"
diff --git a/src/state/userInterface/selectors.ts b/src/state/userInterface/selectors.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b9b3001d1435f7545e07b7d64cfd63e8f545d0b8
--- /dev/null
+++ b/src/state/userInterface/selectors.ts
@@ -0,0 +1,15 @@
+import { createSelector } from "@ngrx/store";
+import { nameSpace } from "./const"
+import { UiStore } from "./store"
+
+const selectStore = state => state[nameSpace] as UiStore
+
+export const panelMode = createSelector(
+  selectStore,
+  state => state.panelMode
+)
+
+export const panelOrder = createSelector(
+  selectStore,
+  state => state.panelOrder
+)
diff --git a/src/state/userInterface/store.ts b/src/state/userInterface/store.ts
new file mode 100644
index 0000000000000000000000000000000000000000..cc854e820d61e94cbee9b5b95f988dbe9db0ec58
--- /dev/null
+++ b/src/state/userInterface/store.ts
@@ -0,0 +1,39 @@
+import { createReducer, on } from "@ngrx/store";
+import * as actions from "./actions"
+import { PanelMode } from "./const"
+
+export type UiStore = {
+  panelMode: PanelMode
+  panelOrder: string // permutation of 0123
+  octantRemoval: boolean
+  showDelineation: boolean
+}
+
+export const defaultState: UiStore = {
+  panelMode: 'FOUR_PANEL',
+  panelOrder: '0123',
+  octantRemoval: false,
+  showDelineation: true,
+}
+
+export const reducer = createReducer(
+  defaultState,
+  on(
+    actions.setPanelMode,
+    (state, { panelMode }) => {
+      return {
+        ...state,
+        panelMode
+      }
+    }
+  ),
+  on(
+    actions.setPanelOrder,
+    (state, { order }) => {
+      return {
+        ...state,
+        panelOrder: order
+      }
+    }
+  )
+)
diff --git a/src/state/userInterface/ui.ts b/src/state/userInterface/ui.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/state/userPreference/actions.ts b/src/state/userPreference/actions.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ee6f8f3062bde7c4c1e3648db385d55859134f2c
--- /dev/null
+++ b/src/state/userPreference/actions.ts
@@ -0,0 +1,39 @@
+import { createAction, props } from "@ngrx/store"
+import { nameSpace, CSP } from "./const"
+
+export const setAnimationFlag = createAction(
+  `${nameSpace} setAnimationFlag`,
+  props<{
+    flag: boolean
+  }>()
+)
+
+export const setGpuLimit = createAction(
+  `${nameSpace} setGpuLimit`,
+  props<{
+    limit: number
+  }>()
+)
+
+export const useMobileUi = createAction(
+  `${nameSpace} setUseMobileUi`,
+  props<{
+    flag: boolean
+  }>()
+)
+
+export const agreeCookie = createAction(
+  `${nameSpace} agreeCookie`
+)
+
+export const agreeKgTos = createAction(
+  `${nameSpace} agreeKgTos`
+)
+
+export const updateCsp = createAction(
+  `${nameSpace} updateCsp`,
+  props<{
+    name: string
+    csp: CSP
+  }>()
+)
\ No newline at end of file
diff --git a/src/state/userPreference/const.ts b/src/state/userPreference/const.ts
new file mode 100644
index 0000000000000000000000000000000000000000..cd96c117aaac67a50928ea673f60faac73c86b86
--- /dev/null
+++ b/src/state/userPreference/const.ts
@@ -0,0 +1,9 @@
+export const nameSpace = `[state.userPreference]`
+
+export const maxGpuLimit = 1e9
+export const minGpuLimit = 1e8
+
+export interface CSP{
+  'connect-src'?: string[]
+  'script-src'?: string[]
+}
diff --git a/src/state/userPreference/effects.ts b/src/state/userPreference/effects.ts
new file mode 100644
index 0000000000000000000000000000000000000000..29aaf3710126543ac635616b4c075939a5d5167a
--- /dev/null
+++ b/src/state/userPreference/effects.ts
@@ -0,0 +1,48 @@
+import { HttpClient } from "@angular/common/http";
+import { Injectable } from "@angular/core";
+import { Actions, createEffect, ofType } from "@ngrx/effects";
+import { map } from "rxjs/operators";
+import { COOKIE_VERSION, KG_TOS_VERSION, LOCAL_STORAGE_CONST } from "src/util/constants";
+import * as actions from "./actions"
+
+@Injectable()
+export class Effects{
+
+  onUseMobileUi = createEffect(() => this.actions$.pipe(
+    ofType(actions.useMobileUi),
+    map(({ flag }) => {
+      window.localStorage.setItem(LOCAL_STORAGE_CONST.MOBILE_UI, JSON.stringify(flag))
+    })
+  ), { dispatch: false })
+  
+  onSetGpuLimit = createEffect(() => this.actions$.pipe(
+    ofType(actions.setGpuLimit),
+    map(({ limit }) => {
+      localStorage.setItem(LOCAL_STORAGE_CONST.GPU_LIMIT, limit.toString())
+    })
+  ), { dispatch: false })
+
+  onAgreeCookie = createEffect(() => this.actions$.pipe(
+    ofType(actions.agreeCookie),
+    map(() => {
+      localStorage.setItem(LOCAL_STORAGE_CONST.AGREE_COOKIE, COOKIE_VERSION)
+    })
+  ), { dispatch: false })
+
+  onAgreeKgTos = createEffect(() => this.actions$.pipe(
+    ofType(actions.agreeKgTos),
+    map(() => {
+      localStorage.setItem(LOCAL_STORAGE_CONST.AGREE_KG_TOS, KG_TOS_VERSION)
+    })
+  ), { dispatch: false })
+
+  // TODO setup on startup get user csp
+  // this.http.get(`${this.constantSvc.backendUrl}user/pluginPermissions`)
+  // onStartUpGetCsp
+
+  constructor(
+    private actions$: Actions,
+    private http: HttpClient,
+  ){
+  }
+}
diff --git a/src/state/userPreference/index.ts b/src/state/userPreference/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1c7a493f7e1b35fc13e4ab3d552774ddad640b4f
--- /dev/null
+++ b/src/state/userPreference/index.ts
@@ -0,0 +1,4 @@
+export { UserPreference, reducer, defaultState } from "./store"
+export { nameSpace } from "./const"
+export * as actions from "./actions"
+export * as selectors from "./selectors"
diff --git a/src/state/userPreference/selectors.ts b/src/state/userPreference/selectors.ts
new file mode 100644
index 0000000000000000000000000000000000000000..608264fc6ce5d007e81b53fd1fadd25ae4a16dfb
--- /dev/null
+++ b/src/state/userPreference/selectors.ts
@@ -0,0 +1,35 @@
+import { createSelector } from "@ngrx/store"
+import { nameSpace } from "./const"
+import { UserPreference } from "./store"
+
+const storeSelector = store => store[nameSpace] as UserPreference
+
+export const useAnimation = createSelector(
+  storeSelector,
+  state => state.useAnimation
+)
+
+export const gpuLimit = createSelector(
+  storeSelector,
+  state => state.gpuLimit
+)
+
+export const useMobileUi = createSelector(
+  storeSelector,
+  state => state.useMobileUi
+)
+
+export const agreedToCookie = createSelector(
+  storeSelector,
+  store => store.agreeCookie
+)
+
+export const agreedToKgTos = createSelector(
+  storeSelector,
+  store => store.agreeKgTos
+)
+
+export const userCsp = createSelector(
+  storeSelector,
+  store => store.pluginCSP
+)
diff --git a/src/state/userPreference/store.ts b/src/state/userPreference/store.ts
new file mode 100644
index 0000000000000000000000000000000000000000..dbf0be95561efd8992d77a54e6c33db4f6f2c7a8
--- /dev/null
+++ b/src/state/userPreference/store.ts
@@ -0,0 +1,93 @@
+import { createReducer, on } from "@ngrx/store"
+import { COOKIE_VERSION, KG_TOS_VERSION, LOCAL_STORAGE_CONST } from "src/util/constants"
+import * as actions from "./actions"
+import { maxGpuLimit, CSP } from "./const"
+
+export const defaultGpuLimit = maxGpuLimit
+
+export type UserPreference = {
+  useMobileUi: boolean
+  gpuLimit: number
+  useAnimation: boolean
+  pluginCSP: Record<string, CSP>
+
+  agreeCookie: boolean
+  agreeKgTos: boolean
+}
+
+export const defaultState: UserPreference = {
+  useMobileUi: JSON.parse(localStorage.getItem(LOCAL_STORAGE_CONST.MOBILE_UI)),
+  gpuLimit: Number(localStorage.getItem(LOCAL_STORAGE_CONST.GPU_LIMIT)) || defaultGpuLimit,
+  useAnimation: !localStorage.getItem(LOCAL_STORAGE_CONST.ANIMATION),
+  pluginCSP: {},
+
+  agreeCookie: localStorage.getItem(LOCAL_STORAGE_CONST.AGREE_COOKIE) === COOKIE_VERSION,
+  agreeKgTos: localStorage.getItem(LOCAL_STORAGE_CONST.AGREE_KG_TOS) === KG_TOS_VERSION,
+}
+
+export const reducer = createReducer(
+  defaultState,
+  on(
+    actions.setAnimationFlag,
+    (state, { flag }) => {
+      if (flag) {
+        localStorage.removeItem(LOCAL_STORAGE_CONST.ANIMATION)
+      } else {
+        localStorage.setItem(LOCAL_STORAGE_CONST.ANIMATION, "false")
+      }
+      
+      return {
+        ...state,
+        useAnimation: flag
+      }
+    }
+  ),
+  on(
+    actions.setGpuLimit,
+    (state, { limit }) => {
+      return {
+        ...state,
+        gpuLimit: limit
+      }
+    }
+  ),
+  on(
+    actions.useMobileUi,
+    (state, { flag }) => {
+      return {
+        ...state,
+        useMobileUi: flag
+      }
+    }
+  ),
+  on(
+    actions.agreeCookie,
+    state => {
+      return {
+        ...state,
+        agreeCookie: true
+      }
+    }
+  ),
+  on(
+    actions.agreeKgTos,
+    state => {
+      return {
+        ...state,
+        agreeKgTos: true
+      }
+    }
+  ),
+  on(
+    actions.updateCsp,
+    (state, { name, csp }) => {
+      return {
+        ...state,
+        pluginCSP: {
+          ...state.pluginCSP,
+          [name]: csp
+        }
+      }
+    }
+  )
+)
diff --git a/src/theme.scss b/src/theme.scss
index 87c14ed3d10df20ac5ee476027f0071633be9ba2..8140e544fe37b55173fc22cc6b374c5e559ad2b4 100644
--- a/src/theme.scss
+++ b/src/theme.scss
@@ -1,5 +1,5 @@
 @use 'sass:map';
-@use '~@angular/material' as mat;
+@use '@angular/material' as mat;
 
 @include mat.core();
 
@@ -13,9 +13,20 @@
   $accent: map-get($color-config, accent);
   $warn: map-get($color-config, warn);
 
-  [iv-custom-comp],
-  .iv-custom-comp
+  [sxplr-custom-cmp],
+  .sxplr-custom-cmp
   {
+    color: mat.get-color-from-palette($foreground, text);
+    
+    &.hoverable
+    {
+      padding: 1rem 1.3rem;
+      &:hover
+      {
+        cursor: pointer;
+        background-color: mat.get-color-from-palette($background, 100);
+      }
+    }
 
     &[target="_blank"]
     {
diff --git a/src/ui/config/configCmp/config.component.ts b/src/ui/config/configCmp/config.component.ts
index 15f92277c707e22aeeb25cb64dec3e562f907030..494403b61842914fa93cfd377d8aa2ffe907ca5f 100644
--- a/src/ui/config/configCmp/config.component.ts
+++ b/src/ui/config/configCmp/config.component.ts
@@ -2,17 +2,11 @@ import { Component, OnDestroy, OnInit } from '@angular/core'
 import { select, Store } from '@ngrx/store';
 import { combineLatest, Observable, Subscription } from 'rxjs';
 import { debounceTime, distinctUntilChanged, map, startWith } from 'rxjs/operators';
-import { SUPPORTED_PANEL_MODES } from 'src/services/state/ngViewerState.store';
-import { ngViewerActionSetPanelOrder } from 'src/services/state/ngViewerState.store.helper';
-import { VIEWER_CONFIG_ACTION_TYPES, StateInterface as ViewerConfiguration } from 'src/services/state/viewerConfig.store'
-import { IavRootStoreInterface } from 'src/services/stateStore.service';
 import { isIdentityQuat } from 'src/viewerModule/nehuba/util';
-import {MatSlideToggleChange} from "@angular/material/slide-toggle";
-import {MatSliderChange} from "@angular/material/slider";
-import { PureContantService } from 'src/util';
-import { ngViewerActionSwitchPanelMode } from 'src/services/state/ngViewerState/actions';
-import { ngViewerSelectorPanelMode, ngViewerSelectorPanelOrder } from 'src/services/state/ngViewerState/selectors';
-import { viewerStateSelectorNavigation } from 'src/services/state/viewerState/selectors';
+import { MatSlideToggleChange } from "@angular/material/slide-toggle";
+import { MatSliderChange } from "@angular/material/slider";
+import { atlasSelection, userPreference, userInterface } from 'src/state';
+import { environment } from "src/environments/environment"
 
 const GPU_TOOLTIP = `Higher GPU usage can cause crashes on lower end machines`
 const ANIMATION_TOOLTIP = `Animation can cause slowdowns in lower end machines`
@@ -33,14 +27,25 @@ export class ConfigComponent implements OnInit, OnDestroy {
   public GPU_TOOLTIP = GPU_TOOLTIP
   public ANIMATION_TOOLTIP = ANIMATION_TOOLTIP
   public MOBILE_UI_TOOLTIP = MOBILE_UI_TOOLTIP
-  public supportedPanelModes = SUPPORTED_PANEL_MODES
+
+  public experimentalFlag = environment.EXPERIMENTAL_FEATURE_FLAG
+
+  public panelModes: Record<string, userInterface.PanelMode> = {
+    FOUR_PANEL: "FOUR_PANEL",
+    H_ONE_THREE: "H_ONE_THREE",
+    SINGLE_PANEL: "SINGLE_PANEL",
+    V_ONE_THREE: "V_ONE_THREE",
+  }
+
 
   /**
    * in MB
    */
   public gpuLimit$: Observable<number>
 
-  public useMobileUI$: Observable<boolean>
+  public useMobileUI$: Observable<boolean> = this.store.pipe(
+    select(userPreference.selectors.useMobileUi)
+  )
   public animationFlag$: Observable<boolean>
   private subscriptions: Subscription[] = []
 
@@ -56,35 +61,28 @@ export class ConfigComponent implements OnInit, OnDestroy {
   private viewerObliqueRotated$: Observable<boolean>
 
   constructor(
-    private store: Store<IavRootStoreInterface>,
-    private pureConstantService: PureContantService,
+    private store: Store<any>,
   ) {
 
-    this.useMobileUI$ = this.pureConstantService.useTouchUI$
-
     this.gpuLimit$ = this.store.pipe(
-      select('viewerConfigState'),
-      map((config: ViewerConfiguration) => config.gpuLimit),
-      distinctUntilChanged(),
+      select(userPreference.selectors.gpuLimit),
       map(v => v / 1e6),
     )
 
     this.animationFlag$ = this.store.pipe(
-      select('viewerConfigState'),
-      map((config: ViewerConfiguration) => config.animation),
+      select(userPreference.selectors.useAnimation)
     )
 
     this.panelMode$ = this.store.pipe(
-      select(ngViewerSelectorPanelMode),
-      startWith(SUPPORTED_PANEL_MODES[0]),
+      select(userInterface.selectors.panelMode)
     )
 
     this.panelOrder$ = this.store.pipe(
-      select(ngViewerSelectorPanelOrder),
+      select(userInterface.selectors.panelOrder),
     )
 
     this.viewerObliqueRotated$ = this.store.pipe(
-      select(viewerStateSelectorNavigation),
+      select(atlasSelection.selectors.navigation),
       map(navigation => (navigation && navigation.orientation) || [0, 0, 0, 1]),
       debounceTime(100),
       map(isIdentityQuat),
@@ -115,36 +113,34 @@ export class ConfigComponent implements OnInit, OnDestroy {
 
   public toggleMobileUI(ev: MatSlideToggleChange) {
     const { checked } = ev
-    this.store.dispatch({
-      type: VIEWER_CONFIG_ACTION_TYPES.SET_MOBILE_UI,
-      payload: {
-        useMobileUI: checked,
-      },
-    })
+    this.store.dispatch(
+      userPreference.actions.useMobileUi({
+        flag: checked
+      })
+    )
   }
 
   public toggleAnimationFlag(ev: MatSlideToggleChange ) {
     const { checked } = ev
-    this.store.dispatch({
-      type: VIEWER_CONFIG_ACTION_TYPES.UPDATE_CONFIG,
-      config: {
-        animation: checked,
-      },
-    })
+    this.store.dispatch(
+      userPreference.actions.setAnimationFlag({
+        flag: checked
+      })
+    )
   }
 
   public handleMatSliderChange(ev: MatSliderChange) {
-    this.store.dispatch({
-      type: VIEWER_CONFIG_ACTION_TYPES.UPDATE_CONFIG,
-      config: {
-        gpuLimit: ev.value * 1e6,
-      },
-    })
+    this.store.dispatch(
+      userPreference.actions.setGpuLimit({
+        limit: ev.value * 1e6
+      })
+    )
   }
-  public usePanelMode(panelMode: string) {
+  public usePanelMode(panelMode: userInterface.PanelMode) {
+
     this.store.dispatch(
-      ngViewerActionSwitchPanelMode({
-        payload: { panelMode }
+      userInterface.actions.setPanelMode({
+        panelMode
       })
     )
   }
@@ -160,8 +156,8 @@ export class ConfigComponent implements OnInit, OnDestroy {
 
     [arr[idx1], arr[idx2]] = [arr[idx2], arr[idx1]]
     this.store.dispatch(
-      ngViewerActionSetPanelOrder({
-        payload: { panelOrder: arr.join('') }
+      userInterface.actions.setPanelOrder({
+        order: arr.join('')
       })
     )
   }
diff --git a/src/ui/config/configCmp/config.stories.ts b/src/ui/config/configCmp/config.stories.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f1aa0a63fa156ec53b5d48e120d2278840896279
--- /dev/null
+++ b/src/ui/config/configCmp/config.stories.ts
@@ -0,0 +1,46 @@
+import { CommonModule } from "@angular/common"
+import { HttpClientModule } from "@angular/common/http"
+import { Meta, moduleMetadata, Story } from "@storybook/angular"
+import { provideDarkTheme } from "src/atlasComponents/sapi/stories.base"
+import { ConfigModule } from "../module"
+import { ConfigComponent } from "./config.component"
+import { userPreference, userInterface, atlasSelection } from "src/state"
+import { StoreModule } from "@ngrx/store"
+
+export default {
+  component: ConfigComponent,
+  decorators: [
+    moduleMetadata({
+      imports: [
+        CommonModule,
+        HttpClientModule,
+        ConfigModule,
+        StoreModule.forRoot({
+          [userPreference.nameSpace]: userPreference.reducer,
+          [userInterface.nameSpace]: userInterface.reducer,
+          [atlasSelection.nameSpace]: atlasSelection.reducer,
+        })
+      ],
+      providers: [
+        ...provideDarkTheme,
+      ],
+      declarations: []
+    })
+  ],
+} as Meta
+
+const Template: Story<ConfigComponent> = (args: ConfigComponent, { loaded }) => {
+  const { experimentalFlag } = args
+  return ({
+    props: {
+      experimentalFlag
+    },
+  })
+}
+
+export const Default = Template.bind({})
+Default.args = {
+  experimentalFlag: true
+}
+Default.loaders = [
+]
\ No newline at end of file
diff --git a/src/ui/config/configCmp/config.style.css b/src/ui/config/configCmp/config.style.css
index 907391412ce3868cca03f2b38599f2f551238085..9fccc534f7886d65ce63f92ca16c8ecf8fc89c42 100644
--- a/src/ui/config/configCmp/config.style.css
+++ b/src/ui/config/configCmp/config.style.css
@@ -11,4 +11,16 @@
 .onDragOver
 {
   background-color: rgba(128,128,128,0.2);
+}
+
+.chunky
+{
+  width: 100%;
+  height: 100%;
+}
+
+.uncollapsable
+{
+  width: 10em;
+  height: 7em;
 }
\ No newline at end of file
diff --git a/src/ui/config/configCmp/config.template.html b/src/ui/config/configCmp/config.template.html
index deb625baac125ea4ff0a47915adf349144d31ae1..eab836a149b2f949cd154ea2e49fe572a90dca6b 100644
--- a/src/ui/config/configCmp/config.template.html
+++ b/src/ui/config/configCmp/config.template.html
@@ -1,15 +1,67 @@
 <mat-tab-group>
+
+  <!-- hard ware -->
+  <mat-tab label="Hardware">
+    <!-- wrapper + margin control -->
+    <div class="sxplr-m-4">
+
+      <!-- use mobile UI -->
+      <div class="d-flex mb-2 align-items-center">
+        <mat-slide-toggle
+          [checked]="useMobileUI$ | async"
+          (change)="toggleMobileUI($event)">
+          Enable Mobile UI
+        </mat-slide-toggle>
+        <small iav-stop="click mousedown mouseup" [matTooltip]="MOBILE_UI_TOOLTIP" class="ml-2 fas fa-question"></small>
+      </div>
+
+      <!-- animation toggle -->
+      <div class="d-flex mb-2 align-items-center">
+        <mat-slide-toggle
+          [checked]="animationFlag$ | async"
+          (change)="toggleAnimationFlag($event)">
+          Enable Animation
+        </mat-slide-toggle>
+        <small iav-stop="click mousedown mouseup" [matTooltip]="ANIMATION_TOOLTIP" class="ml-2 fas fa-question"></small>
+      </div>
+
+      <!-- GPU limit -->
+      <div class="d-flex flex-row align-items-center justify-content start">
+        <label
+          class="sxplr-m-0 d-inline-block flex-grow-0 flex-shrink-0"
+          for="gpuLimitSlider">
+          GPU Limit
+          <small iav-stop="click mousedown mouseup" [matTooltip]="GPU_TOOLTIP" class="ml-2 fas fa-question"></small>
+        </label>
+        <mat-slider
+          class="flex-grow-1 flex-shrink-1 ml-2 mr-2"
+          id="gpuLimitSlider"
+          name="gpuLimitSlider"
+          thumbLabel="true"
+          min="100"
+          max="1000"
+          [step]="stepSize"
+          (change)="handleMatSliderChange($event)"
+          [value]="gpuLimit$ | async">
+        </mat-slider>
+        <span class="d-inline-block flex-grow-0 flex-shrink-0 w-10em">
+          {{ gpuLimit$ | async }} MB
+        </span>
+      </div>
+    </div>
+  </mat-tab>
+
   <!-- viewer preference -->
-  <mat-tab *ngIf="false" label="Viewer Preference">
-    
-    <div class="m-2">
+  <mat-tab *ngIf="experimentalFlag" label="Viewer Preference">
+
+    <div class="sxplr-custom-cmp text sxplr-m-2">
       <div class="mat-h2">
         Rearrange Viewports
       </div>
       <div class="mat-h4 text-muted">
         Click and drag to rearrange viewport positions
       </div>
-      <current-layout class="d-flex w-20em h-15em p-2">
+      <current-layout class="d-flex w-20em h-15em sxplr-p-2">
         <div
           matRipple
           (dragstart)="handleDragStart($event)"
@@ -17,11 +69,11 @@
           (dragleave)="handleDragLeave($event)"
           (dragend)="handleDragend($event)"
           (drop)="handleDrop($event)"
-          class="w-100 h-100 config-transition"
+          class="chunky config-transition"
           cell-i>
           <div
             [attr.panel-order]="0"
-            class="config-transition w-100 h-100 d-flex align-items-center justify-content-center border"
+            class="config-transition chunky d-flex align-items-center justify-content-center border"
             draggable="true">
             {{ (panelTexts$ | async)[0] }}
           </div>
@@ -33,11 +85,11 @@
           (dragleave)="handleDragLeave($event)"
           (dragend)="handleDragend($event)"
           (drop)="handleDrop($event)"
-          class="w-100 h-100 config-transition"
+          class="chunky config-transition"
           cell-ii>
           <div
             [attr.panel-order]="1"
-            class="config-transition w-100 h-100 d-flex align-items-center justify-content-center border"
+            class="config-transition chunky d-flex align-items-center justify-content-center border"
             draggable="true">
             {{ (panelTexts$ | async)[1] }}
           </div>
@@ -49,11 +101,11 @@
           (dragleave)="handleDragLeave($event)"
           (dragend)="handleDragend($event)"
           (drop)="handleDrop($event)"
-          class="w-100 h-100 config-transition"
+          class="chunky config-transition"
           cell-iii>
           <div
             [attr.panel-order]="2"
-            class="config-transition w-100 h-100 d-flex align-items-center justify-content-center border"
+            class="config-transition chunky d-flex align-items-center justify-content-center border"
             draggable="true">
             {{ (panelTexts$ | async)[2] }}
           </div>
@@ -65,146 +117,112 @@
           (dragleave)="handleDragLeave($event)"
           (dragend)="handleDragend($event)"
           (drop)="handleDrop($event)"
-          class="w-100 h-100 config-transition"
+          class="chunky config-transition"
           cell-iv>
           <div
             [attr.panel-order]="3"
-            class="config-transition w-100 h-100 d-flex align-items-center justify-content-center border"
+            class="config-transition chunky d-flex align-items-center justify-content-center border"
             draggable="true">
             {{ (panelTexts$ | async)[3] }}
           </div>
         </div>
       </current-layout>
 
-      <div class="mat-body text-muted font-italic">
+      <div class="sxplr-custom-cmp text text-muted font-italic">
         Plane designation refers to default orientation (without oblique rotation).
       </div>
     </div>
 
     <!-- scroll window -->
 
-    <div class="m-2">
+    <div class="sxplr-m-2 sxplr-custom-cmp text">
       <div class="mat-h2">
         Select a viewports configuration
       </div>
     </div>
 
-    <div class="d-flex flex-row flex-nowrap p-2">
+    <div class="d-flex flex-row flex-nowrap sxplr-p-2">
+      BLA?
+      <!-- main template -->
+      <ng-template #panelModeBtnTmpl
+        let-panelMode="panelMode"
+        let-previewTmpl="previewTmpl">
+        <button
+          class="sxplr-m-2 sxplr-p-2"
+          mat-flat-button
+          (click)="usePanelMode(panelMode)"
+          [color]="(panelMode$ | async) === panelMode ? 'primary' : null">
+
+          <div class="uncollapsable">
+
+            <ng-template [ngTemplateOutlet]="previewTmpl">
+            </ng-template>
+          </div>
+        </button>
+      </ng-template>
 
       <!-- Four Panel Card -->
-      <button
-        class="m-2 p-2"
-        mat-flat-button
-        (click)="usePanelMode(supportedPanelModes[0])"
-        [color]="(panelMode$ | async) === supportedPanelModes[0] ? 'primary' : null">
-        <layout-four-panel class="d-block w-10em h-7em">
-          <div class="border w-100 h-100" cell-i></div>
-          <div class="border w-100 h-100" cell-ii></div>
-          <div class="border w-100 h-100" cell-iii></div>
-          <div class="border w-100 h-100" cell-iv></div>
+      <ng-template #layoutFourPanelTmpl>
+        <layout-four-panel class="d-block chunky">
+          <div class="sxplr-border chunky" cell-i></div>
+          <div class="sxplr-border chunky" cell-ii></div>
+          <div class="sxplr-border chunky" cell-iii></div>
+          <div class="sxplr-border chunky" cell-iv></div>
         </layout-four-panel>
-      </button>
+      </ng-template>
+      <ng-template [ngTemplateOutlet]="panelModeBtnTmpl"
+        [ngTemplateOutletContext]="{
+          panelMode: panelModes.FOUR_PANEL,
+          previewTmpl: layoutFourPanelTmpl
+        }">
+      </ng-template>
 
       <!-- temporarily disabling 1-3 layout -->
 
       <!-- horizontal 1 3 card -->
       <!-- <button
-        class="m-2 p-2"
+        class="sxplr-m-2 sxplr-p-2"
         mat-flat-button
-        (click)="usePanelMode(supportedPanelModes[1])"
-        [color]="(panelMode$ | async) === supportedPanelModes[1] ? 'primary' : null">
+        (click)="usePanelMode(panelModes.H_ONE_THREE)"
+        [color]="(panelMode$ | async) === panelModes.H_ONE_THREE ? 'primary' : null">
         <layout-horizontal-one-three class="d-block w-10em h-7em">
-          <div class="border w-100 h-100" cell-i></div>
-          <div class="border w-100 h-100" cell-ii></div>
-          <div class="border w-100 h-100" cell-iii></div>
-          <div class="border w-100 h-100" cell-iv></div>
+          <div class="border chunky" cell-i></div>
+          <div class="border chunky" cell-ii></div>
+          <div class="border chunky" cell-iii></div>
+          <div class="border chunky" cell-iv></div>
         </layout-horizontal-one-three>
       </button> -->
-  
+
       <!-- vertical 1 3 card -->
       <!-- <button
-        class="m-2 p-2"
+        class="sxplr-m-2 sxplr-p-2"
         mat-flat-button
-        (click)="usePanelMode(supportedPanelModes[2])"
-        [color]="(panelMode$ | async) === supportedPanelModes[2] ? 'primary' : null">
+        (click)="usePanelMode(panelModes.V_ONE_THREE)"
+        [color]="(panelMode$ | async) === panelModes.V_ONE_THREE ? 'primary' : null">
         <layout-vertical-one-three class="d-block w-10em h-7em">
-          <div class="border w-100 h-100" cell-i></div>
-          <div class="border w-100 h-100" cell-ii></div>
-          <div class="border w-100 h-100" cell-iii></div>
-          <div class="border w-100 h-100" cell-iv></div>
+          <div class="border chunky" cell-i></div>
+          <div class="border chunky" cell-ii></div>
+          <div class="border chunky" cell-iii></div>
+          <div class="border chunky" cell-iv></div>
         </layout-vertical-one-three>
       </button> -->
 
       <!-- single -->
-      <button
-        class="m-2 p-2"
-        mat-flat-button
-        (click)="usePanelMode(supportedPanelModes[3])"
-        [color]="(panelMode$ | async) === supportedPanelModes[3] ? 'primary' : null">
+      <ng-template #singlePanelTmpl>
         <layout-single-panel class="d-block w-10em h-7em">
-          <div class="border w-100 h-100" cell-i></div>
-          <div class="border w-100 h-100" cell-ii></div>
-          <div class="border w-100 h-100" cell-iii></div>
-          <div class="border w-100 h-100" cell-iv></div>
+          <div class="border chunky" cell-i></div>
+          <div class="border chunky" cell-ii></div>
+          <div class="border chunky" cell-iii></div>
+          <div class="border chunky" cell-iv></div>
         </layout-single-panel>
-      </button>
-    </div>
-  </mat-tab>
-
-  <!-- hard ware -->
-  <mat-tab label="Hardware">
-    <!-- wrapper + margin control -->
-    <div class="m-4">
-
-      <!-- use mobile UI -->
-      <div class="d-flex mb-2 align-items-center">
-        <mat-slide-toggle
-          [checked]="useMobileUI$ | async"
-          (change)="toggleMobileUI($event)">
-          Enable Mobile UI
-        </mat-slide-toggle>
-        <small iav-stop="click mousedown mouseup" [matTooltip]="MOBILE_UI_TOOLTIP" class="ml-2 fas fa-question"></small>
-      </div>
-
-      <!-- animation toggle -->
-      <div class="d-flex mb-2 align-items-center">
-        <mat-slide-toggle
-          [checked]="animationFlag$ | async"
-          (change)="toggleAnimationFlag($event)">
-          Enable Animation
-        </mat-slide-toggle>
-        <small iav-stop="click mousedown mouseup" [matTooltip]="ANIMATION_TOOLTIP" class="ml-2 fas fa-question"></small>
-      </div>
-
-      <!-- GPU limit -->
-      <div class="d-flex flex-row align-items-center justify-content start">
-        <label
-          class="m-0 d-inline-block flex-grow-0 flex-shrink-0"
-          for="gpuLimitSlider">
-          GPU Limit
-          <small iav-stop="click mousedown mouseup" [matTooltip]="GPU_TOOLTIP" class="ml-2 fas fa-question"></small>
-        </label>
-        <mat-slider
-          class="flex-grow-1 flex-shrink-1 ml-2 mr-2"
-          id="gpuLimitSlider"
-          name="gpuLimitSlider"
-          thumbLabel="true"
-          min="100"
-          max="1000"
-          [step]="stepSize"
-          (change)="handleMatSliderChange($event)"
-          [value]="gpuLimit$ | async">
-        </mat-slider>
-        <span class="d-inline-block flex-grow-0 flex-shrink-0 w-10em">
-          {{ gpuLimit$ | async }} MB
-        </span>
-      </div>
+      </ng-template>
+      <ng-template [ngTemplateOutlet]="panelModeBtnTmpl"
+        [ngTemplateOutletContext]="{
+          panelMode: panelModes.SINGLE_PANEL,
+          previewTmpl: singlePanelTmpl
+        }">
+      </ng-template>
     </div>
   </mat-tab>
-
-  <!-- plugin csp -->
-  <mat-tab label="Plugin Permission">
-    <plugin-csp-controller></plugin-csp-controller>
-  </mat-tab>
 </mat-tab-group>
 
diff --git a/src/ui/config/module.ts b/src/ui/config/module.ts
index f52b5ffb9e5aded977503089cee9f9e5f3e06d10..55fcded2c2c01aa17ce1bdb3420dc2e6c9634e5e 100644
--- a/src/ui/config/module.ts
+++ b/src/ui/config/module.ts
@@ -9,7 +9,7 @@ import { ConfigComponent } from "./configCmp/config.component";
   imports: [
     CommonModule,
     AngularMaterialModule,
-    PluginModule,
+    // PluginModule,
     LayoutModule,
   ],
   declarations: [
diff --git a/src/ui/dialogInfo/const.ts b/src/ui/dialogInfo/const.ts
new file mode 100644
index 0000000000000000000000000000000000000000..09aa80f6f979e24b8cd407ec54fcbdfa4c8d3154
--- /dev/null
+++ b/src/ui/dialogInfo/const.ts
@@ -0,0 +1 @@
+import { InjectionToken } from "@angular/core";
diff --git a/src/ui/dialogInfo/dialog.directive.ts b/src/ui/dialogInfo/dialog.directive.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1b30c4e59ede96787318fe0f8582a2ee85317072
--- /dev/null
+++ b/src/ui/dialogInfo/dialog.directive.ts
@@ -0,0 +1,58 @@
+import { Directive, HostListener, Input, TemplateRef } from "@angular/core";
+import { MatDialog, MatDialogConfig } from "@angular/material/dialog";
+import { MatSnackBar } from "@angular/material/snack-bar";
+
+type DialogSize = 's' | 'm' | 'l' | 'xl'
+
+const sizeDict: Record<DialogSize, Partial<MatDialogConfig>> = {
+  's': {
+    width: '25vw',
+    height: '25vh'
+  },
+  'm': {
+    width: '50vw',
+    height: '50vh'
+  },
+  'l': {
+    width: '75vw',
+    height: '75vh'
+  },
+  'xl': {
+    width: '90vw',
+    height: '90vh'
+  }
+}
+
+@Directive({
+  selector: `[sxplr-dialog]`,
+  exportAs: 'sxplrDialog',
+})
+
+export class DialogDirective{
+
+  @Input('sxplr-dialog')
+  templateRef: TemplateRef<unknown>
+
+  @Input('sxplr-dialog-size')
+  size: DialogSize = 'm'
+
+  @Input('sxplr-dialog-data')
+  data: unknown
+
+  constructor(
+    private matDialog: MatDialog,
+    private snackbar: MatSnackBar,
+  ){
+  }
+
+  @HostListener('click')
+  onClick(){
+    if (!this.templateRef) {
+      return this.snackbar.open(`Cannot show dialog. sxplr-dialog template not provided`)
+    }
+    this.matDialog.open(this.templateRef, {
+      data: this.data,
+      ...sizeDict[this.size]
+    })
+  }
+}
\ No newline at end of file
diff --git a/src/ui/dialogInfo/index.ts b/src/ui/dialogInfo/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..354dde0cbdafd34674a303cfa52d0dd45aa3487f
--- /dev/null
+++ b/src/ui/dialogInfo/index.ts
@@ -0,0 +1 @@
+export { DialogDirective } from "./dialog.directive"
\ No newline at end of file
diff --git a/src/ui/dialogInfo/module.ts b/src/ui/dialogInfo/module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..de09c12a621f36ca70b46354e7eda5a040802b70
--- /dev/null
+++ b/src/ui/dialogInfo/module.ts
@@ -0,0 +1,19 @@
+import { NgModule } from "@angular/core";
+import { MatDialogModule } from "@angular/material/dialog";
+import { MatSnackBarModule } from "@angular/material/snack-bar";
+import { DialogDirective } from "./dialog.directive"
+
+@NgModule({
+  imports: [
+    MatSnackBarModule,
+    MatDialogModule,
+  ],
+  declarations: [
+    DialogDirective,
+  ],
+  exports: [
+    DialogDirective,
+  ],
+})
+
+export class DialogModule{}
diff --git a/src/ui/help/about/about.component.ts b/src/ui/help/about/about.component.ts
index 77dd5c431ba9ec42fc8911ead646806abeda0832..08c4e1999e5de2ead41f81f49d28957dc3c0d747 100644
--- a/src/ui/help/about/about.component.ts
+++ b/src/ui/help/about/about.component.ts
@@ -1,5 +1,4 @@
 import { Component } from '@angular/core'
-import { PureContantService } from 'src/util';
 
 @Component({
   selector: 'iav-about',
@@ -10,14 +9,9 @@ import { PureContantService } from 'src/util';
 })
 
 export class AboutCmp {
-  public contactEmailHref: string = `mailto:${this.constantService.supportEmailAddress}?Subject=[InteractiveAtlasViewer]%20Queries`
-  public supportEmailAddress: string = this.constantService.supportEmailAddress
+  public supportEmailAddress: string = `support@ebrains.eu`
+  public contactEmailHref: string = `mailto:${this.supportEmailAddress}?Subject=[siibra-explorer]%20Queries`
 
-  public userDoc: string = this.constantService.docUrl
-  public repoUrl = this.constantService.repoUrl
-
-  constructor(
-    private constantService: PureContantService,
-  ) {
-  }
+  public userDoc = `https://siibra-explorer.readthedocs.io/en/latest/`
+  public repoUrl = `https://github.com/FZJ-INM1-BDA/siibra-explorer`
 }
diff --git a/src/ui/help/helpOnePager/helpOnePager.component.spec.ts b/src/ui/help/helpOnePager/helpOnePager.component.spec.ts
index 08f17969c3656fb5d3d73ca8a6adf6270d605eae..ea3ceeba5935b89bb6de1e7e017cdec927fac15e 100644
--- a/src/ui/help/helpOnePager/helpOnePager.component.spec.ts
+++ b/src/ui/help/helpOnePager/helpOnePager.component.spec.ts
@@ -3,7 +3,6 @@ import { TestBed } from '@angular/core/testing'
 import { ComponentsModule } from 'src/components'
 import { AngularMaterialModule } from 'src/sharedModules'
 import { QuickTourModule } from 'src/ui/quickTour'
-import { PureContantService } from 'src/util'
 import { UtilModule } from 'src/util/util.module'
 import { HelpOnePager } from './helpOnePager.component'
 
@@ -24,12 +23,6 @@ describe('> helpOnePager.component.ts', () => {
         declarations: [
           HelpOnePager,
         ],
-        providers: [
-          {
-            provide: PureContantService,
-            useValue: {}
-          }
-        ]
       }).compileComponents()
     })
     it('> should render a table', () => {
diff --git a/src/ui/help/helpOnePager/helpOnePager.component.ts b/src/ui/help/helpOnePager/helpOnePager.component.ts
index f7afe35c9306a196b0c4bc0e20f1e9ee447258c7..fb4d54137af64cc245c56e30beeaea1564417311 100644
--- a/src/ui/help/helpOnePager/helpOnePager.component.ts
+++ b/src/ui/help/helpOnePager/helpOnePager.component.ts
@@ -1,6 +1,5 @@
 import { MatDialog } from '@angular/material/dialog';
-import { Component, Optional } from "@angular/core";
-import { PureContantService } from "src/util";
+import { Component } from "@angular/core";
 import { ARIA_LABELS } from 'common/constants'
 import { HowToCite } from '../howToCite/howToCite.component';
 
@@ -21,13 +20,9 @@ export class HelpOnePager{
   public extQuickStarter: string
   public userDoc: string
   constructor(
-    @Optional() pConstService: PureContantService,
     private dialog: MatDialog,
   ){
     this.extQuickStarter = `quickstart.html`
-    if (pConstService) {
-      this.userDoc = pConstService.docUrl
-    }
   }
 
   howToCite(){
diff --git a/src/ui/help/helpOnePager/helpOnePager.template.html b/src/ui/help/helpOnePager/helpOnePager.template.html
index af0ae2e65b7bd8a6047e026165ad922f98662267..c0789dd0ca3933ad2f1c47c99644ceee28b35a4f 100644
--- a/src/ui/help/helpOnePager/helpOnePager.template.html
+++ b/src/ui/help/helpOnePager/helpOnePager.template.html
@@ -5,7 +5,7 @@
   <a *ngIf="extQuickStarter"
     [href]="extQuickStarter"
     target="_blank"
-    class="position-absolute top-0 right-0"
+    class="position-absolute tosxplr-p-0 right-0"
     [matTooltip]="ARIA_LABELS.OPEN_IN_NEW_WINDOW">
     <button mat-icon-button
       color="primary">
diff --git a/src/ui/help/howToCite/howToCite.style.css b/src/ui/help/howToCite/howToCite.style.css
index be8e9d87a469b5adc6608260adcbf1780f45206b..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644
--- a/src/ui/help/howToCite/howToCite.style.css
+++ b/src/ui/help/howToCite/howToCite.style.css
@@ -1,4 +0,0 @@
-:host >>> img
-{
-  width: 100%;
-}
\ No newline at end of file
diff --git a/src/ui/layerbrowser/index.ts b/src/ui/layerbrowser/index.ts
deleted file mode 100644
index e1c19e45596aa009b1c8a86a22b48628dceb991e..0000000000000000000000000000000000000000
--- a/src/ui/layerbrowser/index.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-export { LayerBrowserModule } from './layerBrowser.module'
-
-
-export interface INgLayerInterface {
-  name: string
-  visible: boolean
-  source: string
-  type: string // image | segmentation | etc ...
-  transform?: [[number, number, number, number], [number, number, number, number], [number, number, number, number], [number, number, number, number]] | null
-  // colormap : string
-}
diff --git a/src/ui/layerbrowser/layerBrowser.module.ts b/src/ui/layerbrowser/layerBrowser.module.ts
deleted file mode 100644
index a634859a0ce3ed07d2d57b2471cc481bbe89e27b..0000000000000000000000000000000000000000
--- a/src/ui/layerbrowser/layerBrowser.module.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-import {
-  GetInitialLayerOpacityPipe,
-  LayerBrowser,
-  LockedLayerBtnClsPipe
-} from './layerBrowserComponent/layerbrowser.component'
-import { NgModule } from '@angular/core';
-import { CommonModule } from '@angular/common';
-import { AngularMaterialModule } from 'src/sharedModules';
-import { LayerDetailComponent } from './layerDetail/layerDetail.component';
-import { FormsModule } from '@angular/forms';
-import { UtilModule } from 'src/util';
-
-@NgModule({
-  imports: [
-    CommonModule,
-    FormsModule,
-    AngularMaterialModule,
-    UtilModule,
-  ],
-  declarations: [
-    LayerBrowser,
-    LayerDetailComponent,
-
-    GetInitialLayerOpacityPipe,
-    LockedLayerBtnClsPipe,
-  ],
-  exports: [
-    GetInitialLayerOpacityPipe,
-    LayerBrowser,
-    LockedLayerBtnClsPipe
-  ]
-})
-
-export class LayerBrowserModule{}
\ No newline at end of file
diff --git a/src/ui/layerbrowser/layerBrowserComponent/layerbrowser.component.ts b/src/ui/layerbrowser/layerBrowserComponent/layerbrowser.component.ts
deleted file mode 100644
index 14d933124d8e2921b72e2f34996adcbaf40a9520..0000000000000000000000000000000000000000
--- a/src/ui/layerbrowser/layerBrowserComponent/layerbrowser.component.ts
+++ /dev/null
@@ -1,276 +0,0 @@
-import { ChangeDetectionStrategy, Component,  EventEmitter, Input, OnDestroy, OnInit, Output, Pipe, PipeTransform } from "@angular/core";
-import { select, Store } from "@ngrx/store";
-import { combineLatest, Observable, Subscription } from "rxjs";
-import { debounceTime, distinctUntilChanged, map, shareReplay, startWith } from "rxjs/operators";
-import { MatSliderChange } from "@angular/material/slider";
-
-import { getViewer } from "src/util/fn";
-import { PureContantService } from "src/util";
-import { ngViewerActionRemoveNgLayer, ngViewerActionForceShowSegment } from "src/services/state/ngViewerState/actions";
-import { getNgIds } from 'src/util/fn'
-import { LoggingService } from "src/logging";
-import { ARIA_LABELS } from 'common/constants'
-
-import { INgLayerInterface } from "../index";
-
-const SHOW_LAYER_NAMES = [
-  'PLI Fiber Orientation Red Channel',
-  'PLI Fiber Orientation Green Channel',
-  'PLI Fiber Orientation Blue Channel',
-  'Blockface Image',
-  'PLI Transmittance',
-  'T2w MRI',
-  'MRI Labels',
-  'VOI_1 (area V1)',
-  'VOI_2 (area V2)'
-]
-
-@Component({
-  selector : 'layer-browser',
-  templateUrl : './layerbrowser.template.html',
-  styleUrls : [
-    './layerbrowser.style.css',
-  ],
-  changeDetection: ChangeDetectionStrategy.OnPush
-})
-
-export class LayerBrowser implements OnInit, OnDestroy {
-
-  public TOGGLE_SHOW_LAYER_CONTROL_ARIA_LABEL = ARIA_LABELS.TOGGLE_SHOW_LAYER_CONTROL
-
-  @Output() public nonBaseLayersChanged: EventEmitter<INgLayerInterface[]> = new EventEmitter()
-
-  /**
-   * TODO make untangle nglayernames and its dependency on ng
-   */
-  public loadedNgLayers$: Observable<INgLayerInterface[]>
-  public lockedLayers: string[] = []
-
-  public nonBaseNgLayers$: Observable<INgLayerInterface[]>
-
-  public forceShowSegmentCurrentState: boolean | null = null
-  public forceShowSegment$: Observable<boolean|null>
-
-  public ngLayers$: Observable<string[]>
-  public advancedMode: boolean = false
-
-  private subscriptions: Subscription[] = []
-  private disposeHandler: any
-
-  @Input()
-  public showPlaceholder: boolean = true
-
-  public darktheme$: Observable<boolean>
-
-  private customNgLayers: string[] = ['spatial landmark layer']
-
-  constructor(
-    private store: Store<any>,
-    private pureConstantSvc: PureContantService,
-    private log: LoggingService,
-  ) {
-    this.ngLayers$ = store.pipe(
-      select('viewerState'),
-      select('templateSelected'),
-      map(templateSelected => {
-        if (!templateSelected) { return [] }
-        if (this.advancedMode) { return [] }
-
-        const { ngId , otherNgIds = []} = templateSelected
-
-        return [
-          ngId,
-          ...this.customNgLayers,
-          ...otherNgIds,
-          ...templateSelected.parcellations.reduce((acc, curr) => {
-            return acc.concat([
-              curr.ngId,
-              ...getNgIds(curr.regions),
-            ])
-          }, []),
-        ]
-      }),
-      /**
-       * get unique array
-       */
-      map(nonUniqueArray => Array.from(new Set(nonUniqueArray))),
-      /**
-       * remove falsy values
-       */
-      map(arr => arr.filter(v => !!v)),
-    )
-
-    this.loadedNgLayers$ = this.store.pipe(
-      select('viewerState'),
-      select('loadedNgLayers'),
-    )
-
-    this.nonBaseNgLayers$ = combineLatest(
-      this.ngLayers$,
-      this.loadedNgLayers$,
-    ).pipe(
-      map(([baseNgLayerNames, loadedNgLayers]) => {
-        const baseNameSet = new Set(baseNgLayerNames)
-        return loadedNgLayers.filter(l => SHOW_LAYER_NAMES.includes(l.name))
-      }),
-      distinctUntilChanged()
-    )
-
-    this.forceShowSegment$ = this.store.pipe(
-      select('ngViewerState'),
-      select('forceShowSegment'),
-      startWith(false)
-    )
-
-    this.darktheme$ = this.pureConstantSvc.darktheme$.pipe(
-      shareReplay(1),
-    )
-
-  }
-
-  public ngOnInit() {
-    this.subscriptions.push(
-      this.nonBaseNgLayers$.pipe(
-        // on switching template, non base layer will fire
-        // debounce to ensure that the non base layer is indeed an extra layer
-        debounceTime(160),
-      ).subscribe(layers => this.nonBaseLayersChanged.emit(layers)),
-    )
-    this.subscriptions.push(
-      this.forceShowSegment$.subscribe(state => this.forceShowSegmentCurrentState = state),
-    )
-
-  }
-
-  public ngOnDestroy() {
-    this.subscriptions.forEach(s => s.unsubscribe())
-  }
-
-  public classVisible(layer: any): boolean {
-    return typeof layer.visible === 'undefined'
-      ? true
-      : layer.visible
-  }
-
-  public checkLocked(ngLayer: INgLayerInterface): boolean {
-    if (!this.lockedLayers) {
-      /* locked layer undefined. always return false */
-      return false
-    } else {
-      return this.lockedLayers.findIndex(l => l === ngLayer.name) >= 0
-    }
-  }
-
-  get viewer() {
-    return getViewer()
-  }
-
-  public toggleVisibility(layer: any) {
-    const layerName = layer.name
-    if (!layerName) {
-      this.log.error('layer name not defined', layer)
-      return
-    }
-    const ngLayer = this.viewer.layerManager.getLayerByName(layerName)
-    if (!ngLayer) {
-      this.log.error('ngLayer could not be found', layerName, this.viewer.layerManager.managedLayers)
-    }
-    ngLayer.setVisible(!ngLayer.visible)
-  }
-
-  public toggleForceShowSegment(ngLayer: any) {
-    if (!ngLayer || ngLayer.type !== 'segmentation') {
-      /* toggle only on segmentation layer */
-      return
-    }
-
-    /**
-     * TODO perhaps useEffects ?
-     */
-    this.store.dispatch(
-      ngViewerActionForceShowSegment({
-        forceShowSegment : this.forceShowSegmentCurrentState === null
-          ? true
-          : this.forceShowSegmentCurrentState === true
-            ? false
-            : null,
-      })
-    )
-  }
-
-  public removeLayer(layer: any) {
-    if (this.checkLocked(layer)) {
-      this.log.warn('this layer is locked and cannot be removed')
-      return
-    }
-
-    this.store.dispatch(
-      ngViewerActionRemoveNgLayer({
-        layer
-      })
-    )
-  }
-
-  public changeOpacity(layerName: string, event: MatSliderChange){
-    const { value } = event
-    const l = this.viewer.layerManager.getLayerByName(layerName)
-    if (!l) return
-
-    if (typeof l.layer.opacity === 'object') {
-      l.layer.opacity.value = value
-    } else if (typeof l.layer.displayState === 'object') {
-      l.layer.displayState.selectedAlpha.value = value
-    } else {
-      this.log.warn({
-        msg: `layer does not belong anywhere`,
-        layerName,
-        layer: l
-      })
-    }
-  }
-
-  /**
-   * TODO use observable and pipe to make this more perf
-   */
-  public segmentationTooltip() {
-    return `toggle segments visibility:
-    ${this.forceShowSegmentCurrentState === true ? 'always show' : this.forceShowSegmentCurrentState === false ? 'always hide' : 'auto'}`
-  }
-
-  get segmentationAdditionalClass() {
-    return this.forceShowSegmentCurrentState === null
-      ? 'blue'
-      : this.forceShowSegmentCurrentState === true
-        ? 'normal'
-        : this.forceShowSegmentCurrentState === false
-          ? 'muted'
-          : 'red'
-  }
-
-  public matTooltipPosition: string = 'below'
-}
-
-@Pipe({
-  name: 'lockedLayerBtnClsPipe',
-})
-
-export class LockedLayerBtnClsPipe implements PipeTransform {
-  public transform(ngLayer: INgLayerInterface, lockedLayers?: string[]): boolean {
-    return (lockedLayers && new Set(lockedLayers).has(ngLayer.name)) || false
-  }
-}
-
-@Pipe({
-  name: 'getInitialLayerOpacityPipe'
-})
-
-export class GetInitialLayerOpacityPipe implements PipeTransform{
-  public transform(viewer: any, layerName: string): number{
-    if (!viewer) return 0
-    const l = viewer.layerManager.getLayerByName(layerName)
-    if (!l || !l.layer) return 0
-    if (typeof l.layer.opacity === 'object') return l.layer.opacity.value
-    else if (typeof l.layer.displayState === 'object') return l.layer.displayState.selectedAlpha.value
-    else return 0
-  }
-}
diff --git a/src/ui/layerbrowser/layerBrowserComponent/layerbrowser.style.css b/src/ui/layerbrowser/layerBrowserComponent/layerbrowser.style.css
deleted file mode 100644
index b38ff0ea019b33d7e8797092569c7cc865408ea2..0000000000000000000000000000000000000000
--- a/src/ui/layerbrowser/layerBrowserComponent/layerbrowser.style.css
+++ /dev/null
@@ -1,11 +0,0 @@
-:host
-{
-  padding: 0 0.2em;
-  display: flex;
-  flex-direction: column-reverse;
-}
-
-.noLayerPlaceHolder
-{
-  padding: 0.5em 1em;
-}
diff --git a/src/ui/layerbrowser/layerBrowserComponent/layerbrowser.template.html b/src/ui/layerbrowser/layerBrowserComponent/layerbrowser.template.html
deleted file mode 100644
index 977a88beab32894a90f0f284452f1a29177b35ab..0000000000000000000000000000000000000000
--- a/src/ui/layerbrowser/layerBrowserComponent/layerbrowser.template.html
+++ /dev/null
@@ -1,112 +0,0 @@
-<!-- n.b. using mousedown for event trigger -->
-<!-- Chrome & FF exhibit different behaviours when using click/mouseup as a event handler -->
-<!-- in Chrome, it will complain that expression changed after change detection -->
-<!-- in FF, the element changes, and focusout event is never fired properly -->
-
-<ng-container *ngIf="nonBaseNgLayers$ | async as nonBaseNgLayers; else noLayerPlaceHolder">
-  <mat-accordion *ngIf="nonBaseNgLayers.length > 0; else noLayerPlaceHolder"
-    [multi]="true"
-    displayMode="flat">
-    <mat-expansion-panel
-      [disabled]="true"
-      *ngFor="let ngLayer of nonBaseNgLayers"
-      class="layer-expansion-unit"
-      #expansionPanel>
-      <mat-expansion-panel-header>
-        <div class="align-items-center d-flex flex-nowrap pr-4 w-100">
-
-          <button mat-icon-button
-            [matTooltip]="ngLayer.name | getFilenamePipe">
-            <i class="fas fa-info"></i>
-          </button>
-
-          <!-- toggle opacity -->
-          <div matTooltip="opacity">
-
-            <mat-slider
-              [disabled]="!ngLayer.visible"
-              min="0"
-              max="1"
-              (input)="changeOpacity(ngLayer.name, $event)"
-              [value]="viewer | getInitialLayerOpacityPipe: ngLayer.name"
-              step="0.01">
-
-            </mat-slider>
-          </div>
-
-          <!-- toggle visibility -->
-
-          <button
-            [matTooltipPosition]="matTooltipPosition"
-            [matTooltip]="(ngLayer | lockedLayerBtnClsPipe : lockedLayers) ? 'base layer cannot be hidden' : 'toggle visibility'"
-            (mousedown)="toggleVisibility(ngLayer)"
-            mat-icon-button
-            [disabled]="ngLayer | lockedLayerBtnClsPipe : lockedLayers"
-            [color]="ngLayer.visible ? 'primary' : null">
-            <i [ngClass]="(ngLayer | lockedLayerBtnClsPipe : lockedLayers) ? 'fas fa-lock muted' : ngLayer.visible ? 'far fa-eye' : 'far fa-eye-slash'">
-            </i>
-          </button>
-
-          <!-- advanced mode only: toggle force show segmentation -->
-          <button
-            *ngIf="advancedMode"
-            [matTooltipPosition]="matTooltipPosition"
-            [matTooltip]="ngLayer.type === 'segmentation' ? segmentationTooltip() : 'only segmentation layer can hide/show segments'"
-            (mousedown)="toggleForceShowSegment(ngLayer)"
-            mat-icon-button>
-            <i
-              class="fas"
-              [ngClass]="ngLayer.type === 'segmentation' ? ('fa-th-large ' + segmentationAdditionalClass) : 'fa-lock muted' ">
-
-            </i>
-          </button>
-
-          <!-- remove layer -->
-          <button
-            color="warn"
-            mat-icon-button
-            (mousedown)="removeLayer(ngLayer)"
-            [disabled]="ngLayer | lockedLayerBtnClsPipe : lockedLayers"
-            [matTooltip]="(ngLayer | lockedLayerBtnClsPipe : lockedLayers) ? 'base layers cannot be removed' : 'remove layer'">
-            <i [class]="(ngLayer | lockedLayerBtnClsPipe : lockedLayers) ? 'fas fa-lock muted' : 'fas fa-trash'">
-            </i>
-          </button>
-
-          <!-- layer description -->
-          <mat-label
-            [matTooltipPosition]="matTooltipPosition"
-            [matTooltip]="ngLayer.name | getFilenamePipe"
-            [class]="((darktheme$ | async) ? 'text-light' : 'text-dark') + ' text-truncate flex-grow-1 flex-shrink-1'">
-            {{ ngLayer.name | getFilenamePipe }}
-          </mat-label>
-
-          <button mat-icon-button
-            [attr.aria-label]="TOGGLE_SHOW_LAYER_CONTROL_ARIA_LABEL"
-            (click)="expansionPanel.toggle()">
-            <ng-container *ngIf="expansionPanel.expanded; else btnIconAlt">
-              <i class="fas fa-chevron-up"></i>
-            </ng-container>
-
-            <ng-template #btnIconAlt>
-              <i class="fas fa-chevron-down"></i>
-            </ng-template>
-          </button>
-
-        </div>
-      </mat-expansion-panel-header>
-
-      <ng-template matExpansionPanelContent>
-        <layer-detail-cmp [layerName]="ngLayer.name">
-        </layer-detail-cmp>
-      </ng-template>
-
-    </mat-expansion-panel>
-  </mat-accordion>
-</ng-container>
-
-<!-- fall back when no layers are showing -->
-<ng-template #noLayerPlaceHolder>
-  <small *ngIf="showPlaceholder" class="noLayerPlaceHolder text-muted">
-    No additional layers added.
-  </small>
-</ng-template>
diff --git a/src/ui/layerbrowser/layerDetail/layerDetail.component.spec.ts b/src/ui/layerbrowser/layerDetail/layerDetail.component.spec.ts
deleted file mode 100644
index 575f538b1b828342c9f290f37aab9bf2c2209556..0000000000000000000000000000000000000000
--- a/src/ui/layerbrowser/layerDetail/layerDetail.component.spec.ts
+++ /dev/null
@@ -1,300 +0,0 @@
-import { LayerDetailComponent, VIEWER_INJECTION_TOKEN } from './layerDetail.component'
-import { async, TestBed } from '@angular/core/testing'
-import { NgLayersService } from '../ngLayerService.service'
-import { By } from '@angular/platform-browser'
-import * as CONSTANT from 'src/util/constants'
-import { AngularMaterialModule } from 'src/sharedModules'
-import { CommonModule } from '@angular/common'
-import { FormsModule, ReactiveFormsModule } from '@angular/forms'
-
-const getSpies = (service: NgLayersService) => {
-  const lowThMapGetSpy = spyOn(service.lowThresholdMap, 'get').and.callThrough()
-  const highThMapGetSpy = spyOn(service.highThresholdMap, 'get').and.callThrough()
-  const brightnessMapGetSpy = spyOn(service.brightnessMap, 'get').and.callThrough()
-  const contractMapGetSpy = spyOn(service.contrastMap, 'get').and.callThrough()
-  const removeBgMapGetSpy = spyOn(service.removeBgMap, 'get').and.callThrough()
-
-  const lowThMapSetSpy = spyOn(service.lowThresholdMap, 'set').and.callThrough()
-  const highThMapSetSpy = spyOn(service.highThresholdMap, 'set').and.callThrough()
-  const brightnessMapSetSpy = spyOn(service.brightnessMap, 'set').and.callThrough()
-  const contrastMapSetSpy = spyOn(service.contrastMap, 'set').and.callThrough()
-  const removeBgMapSetSpy = spyOn(service.removeBgMap, 'set').and.callThrough()
-
-  return {
-    lowThMapGetSpy,
-    highThMapGetSpy,
-    brightnessMapGetSpy,
-    contractMapGetSpy,
-    removeBgMapGetSpy,
-    lowThMapSetSpy,
-    highThMapSetSpy,
-    brightnessMapSetSpy,
-    contrastMapSetSpy,
-    removeBgMapSetSpy,
-  }
-}
-
-const getCtrl = () => {
-  const lowThSlider = By.css('mat-slider[aria-label="Set lower threshold"]')
-  const highThSlider = By.css('mat-slider[aria-label="Set higher threshold"]')
-  const brightnessSlider = By.css('mat-slider[aria-label="Set brightness"]')
-  const contrastSlider = By.css('mat-slider[aria-label="Set contrast"]')
-  const removeBgSlideToggle = By.css('mat-slide-toggle[aria-label="Remove background"]')
-  return {
-    lowThSlider,
-    highThSlider,
-    brightnessSlider,
-    contrastSlider,
-    removeBgSlideToggle,
-  }
-}
-
-const getSliderChangeTest = ctrlName => describe(`testing: ${ctrlName}`, () => {
-
-  it('on change, calls window', () => {
-    const service = TestBed.inject(NgLayersService)
-    const spies = getSpies(service)
-    
-    const fixture = TestBed.createComponent(LayerDetailComponent)
-    const layerName = `hello-kitty`
-    fixture.componentInstance.layerName = layerName
-    const triggerChSpy = spyOn(fixture.componentInstance, 'triggerChange')
-    const ctrls = getCtrl()
-    
-    const sLower = fixture.debugElement.query( ctrls[`${ctrlName}Slider`] )
-    sLower.componentInstance.input.emit({ value: 0.5 })
-    expect(spies[`${ctrlName}MapSetSpy`]).toHaveBeenCalledWith(layerName, 0.5)
-    expect(triggerChSpy).toHaveBeenCalled()
-  })
-})
-
-const fragmentMainSpy = {
-  value: `test value`,
-  restoreState: () => {}
-}
-
-const defaultViewer = {
-  layerManager: {
-    getLayerByName: jasmine.createSpy('getLayerByName').and.returnValue({layer: {fragmentMain: fragmentMainSpy}})
-  }
-}
-
-describe('> layerDetail.component.ts', () => {
-  describe('> LayerDetailComponent', () => {
-
-    beforeEach(async(() => {
-      TestBed.configureTestingModule({
-        declarations: [
-          LayerDetailComponent
-        ],
-        imports: [
-          AngularMaterialModule,
-          CommonModule,
-          FormsModule,
-          ReactiveFormsModule,
-        ],
-        providers: [
-          NgLayersService,
-          {
-            provide: VIEWER_INJECTION_TOKEN,
-            useValue: defaultViewer
-          }
-        ]
-      }).compileComponents()
-    }))
-
-    describe('> basic funcitonalities', () => {
-
-      it('> it should be created', () => {
-        const fixture = TestBed.createComponent(LayerDetailComponent)
-        const element = fixture.debugElement.componentInstance
-        expect(element).toBeTruthy()
-      })
-  
-      it('> on bind input, if input is truthy, calls get on layerService maps', () => {
-        TestBed.overrideProvider(VIEWER_INJECTION_TOKEN, {
-          useValue: {}
-        })
-        const service = TestBed.inject(NgLayersService)
-        const {
-          brightnessMapGetSpy,
-          contractMapGetSpy,
-          highThMapGetSpy,
-          lowThMapGetSpy,
-          removeBgMapGetSpy
-        } = getSpies(service)
-  
-        const layerName = `hello-kitty`
-        const fixture = TestBed.createComponent(LayerDetailComponent)
-        fixture.componentInstance.layerName = layerName
-        fixture.componentInstance.ngOnChanges()
-        fixture.detectChanges()
-        expect(brightnessMapGetSpy).toHaveBeenCalledWith(layerName)
-        expect(contractMapGetSpy).toHaveBeenCalledWith(layerName)
-        expect(highThMapGetSpy).toHaveBeenCalledWith(layerName)
-        expect(lowThMapGetSpy).toHaveBeenCalledWith(layerName)
-        expect(removeBgMapGetSpy).toHaveBeenCalledWith(layerName)
-      })
-  
-      it('> on bind input, if input is falsy, does not call layerService map get', () => {
-        const service = TestBed.inject(NgLayersService)
-        const {
-          brightnessMapGetSpy,
-          contractMapGetSpy,
-          highThMapGetSpy,
-          lowThMapGetSpy,
-          removeBgMapGetSpy
-        } = getSpies(service)
-  
-        const layerName = null
-        const fixture = TestBed.createComponent(LayerDetailComponent)
-        fixture.componentInstance.layerName = layerName
-        fixture.componentInstance.ngOnChanges()
-        fixture.detectChanges()
-        expect(brightnessMapGetSpy).not.toHaveBeenCalled()
-        expect(contractMapGetSpy).not.toHaveBeenCalled()
-        expect(highThMapGetSpy).not.toHaveBeenCalled()
-        expect(lowThMapGetSpy).not.toHaveBeenCalled()
-        expect(removeBgMapGetSpy).not.toHaveBeenCalled()
-      })
-  
-    })
-
-    const testingSlidersCtrl = [
-      'lowTh',
-      'highTh',
-      'brightness',
-      'contrast',
-    ]
-
-    for (const sliderCtrl of testingSlidersCtrl ) {
-      getSliderChangeTest(sliderCtrl)
-    }
-
-    describe('testing: removeBG toggle', () => {
-      it('on change, calls window', () => {
-
-        const service = TestBed.inject(NgLayersService)
-        const { removeBgMapSetSpy } = getSpies(service)
-        
-        const fixture = TestBed.createComponent(LayerDetailComponent)
-        const triggerChSpy = spyOn(fixture.componentInstance, 'triggerChange')
-        const layerName = `hello-kitty`
-        fixture.componentInstance.layerName = layerName
-
-        const { removeBgSlideToggle } = getCtrl()
-        const bgToggle = fixture.debugElement.query( removeBgSlideToggle )
-        bgToggle.componentInstance.change.emit({ checked: true })
-        expect(removeBgMapSetSpy).toHaveBeenCalledWith('hello-kitty', true)
-        expect(triggerChSpy).toHaveBeenCalled()
-
-        removeBgMapSetSpy.calls.reset()
-        triggerChSpy.calls.reset()
-        expect(removeBgMapSetSpy).not.toHaveBeenCalled()
-        expect(triggerChSpy).not.toHaveBeenCalled()
-
-        bgToggle.componentInstance.change.emit({ checked: false })
-
-        expect(removeBgMapSetSpy).toHaveBeenCalledWith('hello-kitty', false)
-        expect(triggerChSpy).toHaveBeenCalled()
-      })
-    })
-
-    describe('triggerChange', () => {
-      it('should throw if viewer is not defined', () => {
-        TestBed.overrideProvider(VIEWER_INJECTION_TOKEN, {
-          useValue: null
-        })
-        const fixutre = TestBed.createComponent(LayerDetailComponent)
-        expect(function(){
-          fixutre.componentInstance.triggerChange()
-        }).toThrowError('viewer is not defined')
-      })
-
-      it('should throw if layer is not found', () => {
-        const fakeGetLayerByName = jasmine.createSpy().and.returnValue(undefined)
-        const fakeNgInstance = {
-          layerManager: {
-            getLayerByName: fakeGetLayerByName
-          }
-        }
-
-        TestBed.overrideProvider(VIEWER_INJECTION_TOKEN, {
-          useValue: fakeNgInstance
-        })
-
-        const fixutre = TestBed.createComponent(LayerDetailComponent)
-        const layerName = `test-kitty`
-
-        fixutre.componentInstance.layerName = layerName
-
-        expect(function(){
-          fixutre.componentInstance.triggerChange()
-        }).toThrowError(`layer with name: ${layerName}, not found.`)
-      })
-
-      it('should throw if layer.layer.fragmentMain is undefined', () => {
-        const layerName = `test-kitty`
-
-        const fakeLayer = {
-          hello: 'world'
-        }
-        const fakeGetLayerByName = jasmine.createSpy().and.returnValue(fakeLayer)
-        const fakeNgInstance = {
-          layerManager: {
-            getLayerByName: fakeGetLayerByName
-          }
-        }
-
-        TestBed.overrideProvider(VIEWER_INJECTION_TOKEN, {
-          useValue: fakeNgInstance
-        })
-
-        const fixutre = TestBed.createComponent(LayerDetailComponent)
-
-        fixutre.componentInstance.layerName = layerName
-
-        expect(function(){
-          fixutre.componentInstance.triggerChange()
-        }).toThrowError(`layer.fragmentMain is not defined... is this an image layer?`)
-      })
-
-      it('should call getShader and restoreState if all goes right', () => {
-
-        const replacementShader = `blabla ahder`
-        const getShaderSpy = jasmine.createSpy('getShader').and.returnValue(replacementShader)
-        spyOnProperty(CONSTANT, 'getShader').and.returnValue(getShaderSpy)
-        
-        const layerName = `test-kitty`
-
-        const fakeRestoreState = jasmine.createSpy('fakeGetLayerByName')
-        const fakeLayer = {
-          layer: {
-            fragmentMain: {
-              restoreState: fakeRestoreState
-            }
-          }
-        }
-        const fakeGetLayerByName = jasmine.createSpy('fakeGetLayerByName').and.returnValue(fakeLayer)
-        const fakeNgInstance = {
-          layerManager: {
-            getLayerByName: fakeGetLayerByName
-          }
-        }
-        TestBed.overrideProvider(VIEWER_INJECTION_TOKEN, {
-          useValue: fakeNgInstance
-        })
-        
-        const fixutre = TestBed.createComponent(LayerDetailComponent)
-        fixutre.componentInstance.layerName = layerName
-        fixutre.detectChanges()
-
-        fixutre.componentInstance.triggerChange()
-        
-        expect(fakeGetLayerByName).toHaveBeenCalledWith(layerName)
-        expect(getShaderSpy).toHaveBeenCalled()
-        expect(fakeRestoreState).toHaveBeenCalledWith(replacementShader)
-      })
-    })
-  })
-})
\ No newline at end of file
diff --git a/src/ui/layerbrowser/layerDetail/layerDetail.component.ts b/src/ui/layerbrowser/layerDetail/layerDetail.component.ts
deleted file mode 100644
index 414bab57e2f2ef11e8232a7405e8b206d5707cda..0000000000000000000000000000000000000000
--- a/src/ui/layerbrowser/layerDetail/layerDetail.component.ts
+++ /dev/null
@@ -1,100 +0,0 @@
-import { Component, Input, OnChanges, ChangeDetectionStrategy, Optional, Inject } from "@angular/core";
-import { NgLayersService } from "../ngLayerService.service";
-import { MatSliderChange } from "@angular/material/slider";
-import { MatSlideToggleChange } from "@angular/material/slide-toggle";
-import { getShader } from "src/util/constants";
-
-export const VIEWER_INJECTION_TOKEN = `VIEWER_INJECTION_TOKEN`
-
-@Component({
-  selector: 'layer-detail-cmp',
-  templateUrl: './layerDetail.template.html',
-  changeDetection: ChangeDetectionStrategy.OnPush
-})
-
-export class LayerDetailComponent implements OnChanges{
-  @Input() 
-  layerName: string
-
-  private colormap = null 
-
-  constructor(
-    private layersService: NgLayersService,
-    @Optional() @Inject(VIEWER_INJECTION_TOKEN) private injectedViewer
-  ){
-
-  }
-
-  ngOnChanges(){
-    if (!this.layerName) return
-
-    this.lowThreshold = this.layersService.lowThresholdMap.get(this.layerName) || this.lowThreshold
-    this.highThreshold = this.layersService.highThresholdMap.get(this.layerName) || this.highThreshold
-    this.brightness = this.layersService.brightnessMap.get(this.layerName) || this.brightness
-    this.contrast = this.layersService.contrastMap.get(this.layerName) || this.contrast
-    this.removeBg = this.layersService.removeBgMap.get(this.layerName) || this.removeBg
-    this.colormap = this.layersService.colorMapMap.get(this.layerName) || this.colormap
-  }
-
-  public lowThreshold: number = 0
-  public highThreshold: number = 1
-  public brightness: number = 0
-  public contrast: number = 0
-  public removeBg: boolean = false
-
-  handleChange(mode: 'low' | 'high' | 'brightness' | 'contrast', event: MatSliderChange){
-    switch(mode) {
-    case 'low':
-      this.layersService.lowThresholdMap.set(this.layerName, event.value)
-      this.lowThreshold = event.value
-      break;
-    case 'high':
-      this.layersService.highThresholdMap.set(this.layerName, event.value)
-      this.highThreshold = event.value
-      break;
-    case 'brightness':
-      this.layersService.brightnessMap.set(this.layerName, event.value)
-      this.brightness = event.value
-      break;
-    case 'contrast':
-      this.layersService.contrastMap.set(this.layerName, event.value)
-      this.contrast = event.value
-      break;
-    default: return
-    }
-    this.triggerChange()
-  }
-
-  handleToggleBg(event: MatSlideToggleChange){
-    this.layersService.removeBgMap.set(this.layerName, event.checked)
-    this.removeBg = event.checked
-    this.triggerChange()
-  }
-
-  triggerChange(){
-    const { lowThreshold, highThreshold, brightness, contrast, removeBg, colormap } = this
-    const shader = getShader({
-      lowThreshold,
-      highThreshold, 
-      colormap,
-      brightness,
-      contrast,
-      removeBg
-    })
-    this.fragmentMain.restoreState(shader)
-  }
-
-  private get viewer(){
-    return this.injectedViewer || (window as any).viewer
-  }
-
-  private get fragmentMain(){
-
-    if (!this.viewer) throw new Error(`viewer is not defined`)
-    const layer = this.viewer.layerManager.getLayerByName(this.layerName)
-    if (!layer) throw new Error(`layer with name: ${this.layerName}, not found.`)
-    if (! (layer.layer?.fragmentMain?.restoreState) ) throw new Error(`layer.fragmentMain is not defined... is this an image layer?`)
-
-    return layer.layer.fragmentMain
-  }
-}
diff --git a/src/ui/layerbrowser/layerDetail/layerDetail.template.html b/src/ui/layerbrowser/layerDetail/layerDetail.template.html
deleted file mode 100644
index 376b875c3ffd924a5a69166d2555104cae0c0390..0000000000000000000000000000000000000000
--- a/src/ui/layerbrowser/layerDetail/layerDetail.template.html
+++ /dev/null
@@ -1,79 +0,0 @@
-<div class="d-flex flex-column">
-  <div>
-    <mat-label>
-      Low threshold
-    </mat-label>
-    <mat-slider
-      aria-label="Set lower threshold"
-      (input)="handleChange('low', $event)"
-      [value]="lowThreshold"
-      min="0"
-      max="1"
-      step="0.001">
-    </mat-slider>
-    <mat-label>
-      {{ lowThreshold }}
-    </mat-label>
-  </div>
-
-  <div>
-    <mat-label>
-      High threshold
-    </mat-label>
-    <mat-slider
-      aria-label="Set higher threshold"
-      (input)="handleChange('high', $event)"
-      [value]="highThreshold"
-      min="0"
-      max="1"
-      step="0.001">
-    </mat-slider>  
-    <mat-label>
-      {{ highThreshold }}
-    </mat-label>
-  </div>
-  <div>
-    <mat-label>
-      Brightness
-    </mat-label>
-    <mat-slider
-      aria-label="Set brightness"
-      (input)="handleChange('brightness', $event)"
-      [value]="brightness"
-      min="-1"
-      max="1"
-      step="0.01">
-    </mat-slider>  
-    <mat-label>
-      {{ brightness }}
-    </mat-label>
-  </div>
-  <div>
-    <mat-label>
-      Contrast
-    </mat-label>
-    <mat-slider
-      aria-label="Set contrast"
-      (input)="handleChange('contrast', $event)"
-      [value]="contrast"
-      min="-1"
-      max="1"
-      step="0.01">
-    </mat-slider>  
-    <mat-label>
-      {{ contrast }}
-    </mat-label>
-  </div>
-
-  <div>
-    <mat-label>
-      Remove background
-    </mat-label>
-    <mat-slide-toggle
-      aria-label="Remove background"
-      (change)="handleToggleBg($event)"
-      [(ngModel)]="removeBg">
-
-    </mat-slide-toggle>
-  </div>
-</div>
\ No newline at end of file
diff --git a/src/ui/layerbrowser/ngLayerService.service.ts b/src/ui/layerbrowser/ngLayerService.service.ts
deleted file mode 100644
index 35bd949ba6885423862692dacdac94e35dc96a7b..0000000000000000000000000000000000000000
--- a/src/ui/layerbrowser/ngLayerService.service.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { Injectable } from "@angular/core";
-import { EnumColorMapName } from "src/util/colorMaps";
-
-@Injectable({
-  providedIn: 'root'
-})
-
-export class NgLayersService{
-  public lowThresholdMap: Map<string, number> = new Map()
-  public highThresholdMap: Map<string, number> = new Map()
-  public brightnessMap: Map<string, number> = new Map()
-  public contrastMap: Map<string, number> = new Map()
-  public removeBgMap: Map<string, boolean> = new Map()
-  public colorMapMap: Map<string, EnumColorMapName> = new Map()
-}
diff --git a/src/ui/logoContainer/logoContainer.component.ts b/src/ui/logoContainer/logoContainer.component.ts
index 105e0726251a75522bc2d747767c0fe7b3eb50c2..3cc1e6d947db7c42e80c251d7c0de1e67bbc4339 100644
--- a/src/ui/logoContainer/logoContainer.component.ts
+++ b/src/ui/logoContainer/logoContainer.component.ts
@@ -1,7 +1,7 @@
-import { Component } from "@angular/core";
-import { PureContantService } from "src/util";
-import { Subscription } from "rxjs";
+import { Component, Inject } from "@angular/core";
+import { Observable, Subscription } from "rxjs";
 import { distinctUntilChanged } from "rxjs/operators";
+import { DARKTHEME } from "src/util/injectionTokens";
 
 const imageDark = 'assets/logo/ebrains-logo-dark.svg'
 const imageLight = 'assets/logo/ebrains-logo-light.svg'
@@ -24,10 +24,10 @@ export class LogoContainer {
 
   private subscriptions: Subscription[] = []
   constructor(
-    private pureConstantService: PureContantService
+    @Inject(DARKTHEME) darktheme$: Observable<boolean>
   ){
     this.subscriptions.push(
-      pureConstantService.darktheme$.pipe(
+      darktheme$.pipe(
         distinctUntilChanged()
       ).subscribe(flag => {
         this.containerStyle = {
diff --git a/src/ui/nehubaContainer/2dLandmarks/flatLm/flatLm.component.ts b/src/ui/nehubaContainer/2dLandmarks/flatLm/flatLm.component.ts
deleted file mode 100644
index e425aca205c81b4e644508a769f280e65c6abcc0..0000000000000000000000000000000000000000
--- a/src/ui/nehubaContainer/2dLandmarks/flatLm/flatLm.component.ts
+++ /dev/null
@@ -1,50 +0,0 @@
-import { ChangeDetectionStrategy, ChangeDetectorRef, Component, HostBinding, OnChanges, SimpleChanges } from "@angular/core";
-import { LandmarkUnitBase } from "../landmark.base";
-
-@Component({
-  selector: 'landmark-2d-flat-cmp',
-  templateUrl: './flatLm.template.html',
-  styleUrls: [
-    './flatLm.style.css'
-  ],
-  changeDetection: ChangeDetectionStrategy.OnPush
-})
-
-export class FlatLMCmp extends LandmarkUnitBase implements OnChanges{
-
-  @HostBinding('style.transform')
-  transform = `translate(0px, 0px)`
-
-  @HostBinding('style.opacity')
-  opacity = 1.0
-
-  @HostBinding('style.text-shadow')
-  textShadow = null
-
-  private scale: number = 1
-
-  constructor(private cdr: ChangeDetectorRef){
-    super()
-    this.cdr.detach()
-  }
-
-  ngOnChanges(){
-    
-    const zModifier = Math.tanh(this.positionZ/10)
-    if (this.positionZ >= 0) {
-      const shadowLength =  zModifier * 4
-      this.textShadow = `0 ${shadowLength}px ${shadowLength}px black`
-      this.opacity = 1.0
-    } else {
-      this.textShadow = null
-      /**
-       * assert(zModifier < 0)
-       */
-      this.opacity = 1.0 + (zModifier * 0.8)
-    }
-
-    this.transform = `translate(${this.positionX}px, ${this.positionY - (zModifier >= 0 ? zModifier * 4 : 0) }px)`
-  }
-}
-
-export const NORMAL_COLOR: number[] = [201,54,38]
diff --git a/src/ui/nehubaContainer/2dLandmarks/flatLm/flatLm.style.css b/src/ui/nehubaContainer/2dLandmarks/flatLm/flatLm.style.css
deleted file mode 100644
index 462b4c7efcef86358db18579f078dc9043c37fab..0000000000000000000000000000000000000000
--- a/src/ui/nehubaContainer/2dLandmarks/flatLm/flatLm.style.css
+++ /dev/null
@@ -1,10 +0,0 @@
-:host
-{
-  position: absolute;
-  color: rgba(201, 54, 38, 1.0)
-}
-
-:host > div
-{
-  transform: translate(-50%, -50%);
-}
diff --git a/src/ui/nehubaContainer/2dLandmarks/flatLm/flatLm.template.html b/src/ui/nehubaContainer/2dLandmarks/flatLm/flatLm.template.html
deleted file mode 100644
index a28b3899ac23b46084447e6104158475ca30f932..0000000000000000000000000000000000000000
--- a/src/ui/nehubaContainer/2dLandmarks/flatLm/flatLm.template.html
+++ /dev/null
@@ -1,3 +0,0 @@
-<div class="pe-all">
-  <i class="fas fa-circle"></i>
-</div>
diff --git a/src/ui/nehubaContainer/2dLandmarks/landmark.base.ts b/src/ui/nehubaContainer/2dLandmarks/landmark.base.ts
deleted file mode 100644
index 953c75ed49d956b42023822fec53daea65adcec7..0000000000000000000000000000000000000000
--- a/src/ui/nehubaContainer/2dLandmarks/landmark.base.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import { Directive, Input } from "@angular/core"
-
-@Directive()
-export class LandmarkUnitBase{
-  @Input() public positionX: number = 0
-  @Input() public positionY: number = 0
-  @Input() public positionZ: number = 0
-
-  @Input() public color: number[] = [255, 255, 255]
-}
diff --git a/src/ui/nehubaContainer/2dLandmarks/module.ts b/src/ui/nehubaContainer/2dLandmarks/module.ts
deleted file mode 100644
index cef4ec49316a4723d47a2ebce00ebe43dad6020b..0000000000000000000000000000000000000000
--- a/src/ui/nehubaContainer/2dLandmarks/module.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import { CommonModule } from "@angular/common";
-import { NgModule } from "@angular/core";
-import { FlatLMCmp } from "./flatLm/flatLm.component";
-import { SafeStylePipe } from "./safeStyle.pipe";
-
-@NgModule({
-  imports: [
-    CommonModule,
-  ],
-  declarations: [
-    FlatLMCmp,
-
-    /**
-     * pipes
-     */
-    SafeStylePipe,
-  ],
-  exports: [
-    FlatLMCmp,
-  ]
-})
-
-export class Landmark2DModule{}
diff --git a/src/ui/nehubaContainer/2dLandmarks/safeStyle.pipe.ts b/src/ui/nehubaContainer/2dLandmarks/safeStyle.pipe.ts
deleted file mode 100644
index 7901ca1cc515310cb40f495d9ffe7f520f81a5e9..0000000000000000000000000000000000000000
--- a/src/ui/nehubaContainer/2dLandmarks/safeStyle.pipe.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import { Pipe, PipeTransform } from "@angular/core";
-import { DomSanitizer, SafeStyle } from "@angular/platform-browser";
-
-@Pipe({
-  name : 'safeStyle',
-})
-
-export class SafeStylePipe implements PipeTransform {
-  constructor(private sanitizer: DomSanitizer) {
-
-  }
-
-  public transform(style: string): SafeStyle {
-    return this.sanitizer.bypassSecurityTrustStyle(style)
-  }
-}
diff --git a/src/ui/nehubaContainer/pipes/mobileControlNubStyle.pipe.ts b/src/ui/nehubaContainer/pipes/mobileControlNubStyle.pipe.ts
deleted file mode 100644
index 1f70f6b658cb27001bab3415c77aa0ec90d5be70..0000000000000000000000000000000000000000
--- a/src/ui/nehubaContainer/pipes/mobileControlNubStyle.pipe.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-import { Pipe, PipeTransform } from "@angular/core";
-import { PANELS } from 'src/services/state/ngViewerState.store.helper'
-
-
-@Pipe({
-  name: 'mobileControlNubStylePipe',
-})
-
-export class MobileControlNubStylePipe implements PipeTransform {
-  public transform(panelMode: string): any {
-    switch (panelMode) {
-    case PANELS.SINGLE_PANEL:
-      return {
-        top: '80%',
-        left: '95%',
-      }
-    case PANELS.V_ONE_THREE:
-    case PANELS.H_ONE_THREE:
-      return {
-        top: '66.66%',
-        left: '66.66%',
-      }
-    case PANELS.FOUR_PANEL:
-    default:
-      return {
-        top: '50%',
-        left: '50%',
-      }
-    }
-  }
-}
diff --git a/src/ui/quickTour/module.ts b/src/ui/quickTour/module.ts
index b599aeca491397515b1e271453878144e0a4f187..4f303b92d556132acf2a46afd452a9269a424779 100644
--- a/src/ui/quickTour/module.ts
+++ b/src/ui/quickTour/module.ts
@@ -21,7 +21,7 @@ import {StartTourDialogDialog} from "src/ui/quickTour/startTourDialog/startTourD
     WindowResizeModule,
     ComponentsModule,
   ],
-  declarations:[
+  declarations: [
     QuickTourThis,
     QuickTourComponent,
     QuickTourDirective,
@@ -32,7 +32,7 @@ import {StartTourDialogDialog} from "src/ui/quickTour/startTourDialog/startTourD
     QuickTourDirective,
     QuickTourThis,
   ],
-  providers:[
+  providers: [
     {
       provide: OverlayContainer,
       useClass: FullscreenOverlayContainer
@@ -42,7 +42,6 @@ import {StartTourDialogDialog} from "src/ui/quickTour/startTourDialog/startTourD
       provide: QUICK_TOUR_CMP_INJTKN,
       useValue: QuickTourComponent
     }
-  ],
-  entryComponents: [ StartTourDialogDialog ]
+  ]
 })
 export class QuickTourModule{}
diff --git a/src/ui/ui.module.ts b/src/ui/ui.module.ts
index c33043639f957bbe8640db5e3c5104d682c14958..a657e32cc68916a0b63a485f226dbdd7a05e948e 100644
--- a/src/ui/ui.module.ts
+++ b/src/ui/ui.module.ts
@@ -1,39 +1,26 @@
 import { NgModule } from "@angular/core";
 import { ComponentsModule } from "src/components/components.module";
-
 import { FormsModule, ReactiveFormsModule } from "@angular/forms";
 import { LayoutModule } from "src/layouts/layout.module";
-
 import { ScrollingModule } from "@angular/cdk/scrolling"
 import { HttpClientModule } from "@angular/common/http";
 import { AngularMaterialModule } from 'src/sharedModules'
 import { UtilModule } from "src/util";
 import { DownloadDirective } from "../util/directives/download.directive";
-
-import { LogoContainer } from "./logoContainer/logoContainer.component";
 import { MobileOverlay } from "./nehubaContainer/mobileOverlay/mobileOverlay.component";
-import { MobileControlNubStylePipe } from "./nehubaContainer/pipes/mobileControlNubStyle.pipe";
-
 import { HumanReadableFileSizePipe } from "src/util/pipes/humanReadableFileSize.pipe";
-
 import { ReorderPanelIndexPipe } from "./nehubaContainer/reorderPanelIndex.pipe";
-
 import { FixedMouseContextualContainerDirective } from "src/util/directives/FixedMouseContextualContainerDirective.directive";
-
 import { ShareModule } from "src/share";
 import { AuthModule } from "src/auth";
 import { ActionDialog } from "./actionDialog/actionDialog.component";
 import { APPEND_SCRIPT_TOKEN, appendScriptFactory } from "src/util/constants";
 import { DOCUMENT } from "@angular/common";
 import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
-import { RegionalFeaturesModule } from "../atlasComponents/regionalFeatures";
-import { Landmark2DModule } from "./nehubaContainer/2dLandmarks/module";
 import { HANDLE_SCREENSHOT_PROMISE, TypeHandleScrnShotPromise } from "../screenshot";
-import { ParcellationRegionModule } from "src/atlasComponents/parcellationRegion";
-import { AtlasCmpParcellationModule } from "src/atlasComponents/parcellation";
 
 @NgModule({
-  imports : [
+  imports: [
     BrowserAnimationsModule,
     HttpClientModule,
     FormsModule,
@@ -45,24 +32,13 @@ import { AtlasCmpParcellationModule } from "src/atlasComponents/parcellation";
     AngularMaterialModule,
     ShareModule,
     AuthModule,
-    RegionalFeaturesModule,
-    Landmark2DModule,
-    ParcellationRegionModule,
-    AtlasCmpParcellationModule,
   ],
-  declarations : [
-    
-    LogoContainer,
+  declarations: [
     MobileOverlay,
-
     ActionDialog,
-
     /* pipes */
-    MobileControlNubStylePipe,
-
     HumanReadableFileSizePipe,
     ReorderPanelIndexPipe,
-
     /* directive */
     DownloadDirective,
     FixedMouseContextualContainerDirective,
@@ -71,7 +47,7 @@ import { AtlasCmpParcellationModule } from "src/atlasComponents/parcellation";
     {
       provide: APPEND_SCRIPT_TOKEN,
       useFactory: appendScriptFactory,
-      deps: [ DOCUMENT ]
+      deps: [DOCUMENT]
     },
     {
       provide: HANDLE_SCREENSHOT_PROMISE,
@@ -98,7 +74,6 @@ import { AtlasCmpParcellationModule } from "src/atlasComponents/parcellation";
           const context = subCanvas.getContext('2d')
           context.drawImage(
             canvas,
-
             /**
              * from
              */
@@ -106,7 +81,6 @@ import { AtlasCmpParcellationModule } from "src/atlasComponents/parcellation";
             y,
             width,
             height,
-
             /**
              * to
              */
@@ -115,7 +89,6 @@ import { AtlasCmpParcellationModule } from "src/atlasComponents/parcellation";
             width,
             height
           )
-
           subCanvas.toBlob(blob => {
             const url = URL.createObjectURL(blob)
             rs({
@@ -127,18 +100,9 @@ import { AtlasCmpParcellationModule } from "src/atlasComponents/parcellation";
       }) as TypeHandleScrnShotPromise
     }
   ],
-  entryComponents : [
-
-    /* dynamically created components needs to be declared here */
-    
-    ActionDialog,
-  ],
-  exports : [
+  exports: [
     // NehubaContainer,
-    
-    LogoContainer,
     MobileOverlay,
-    
     // StatusCardComponent,
     FixedMouseContextualContainerDirective,
   ]
diff --git a/src/util/array.ts b/src/util/array.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2c9cfeea640639773c0249db3f64406a6a67dac4
--- /dev/null
+++ b/src/util/array.ts
@@ -0,0 +1,13 @@
+const defaultCmFn = <T>(o: T, n: T) => o === n
+export function arrayEqual<T>(compareFn: (o1: T, o2: T) => boolean = defaultCmFn, order = false) {
+  return function(array1: T[], array2: T[]){
+    if (order) {
+      for (const idx in array1) {
+        if (!compareFn(array1[idx], array2[idx])) return false
+      }
+      return true
+    }
+    return !!array1.every(it1 => array2.find(it2 => compareFn(it1, it2)))
+      && !!array2.every(it2 => array1.find(it1 => compareFn(it1, it2)))
+  }
+}
diff --git a/src/util/constants.ts b/src/util/constants.ts
index 84ee596993c519223fc8f28487af1a0cb8372f4d..5c4e7185743a1dca35e0523f076385b797fc21b5 100644
--- a/src/util/constants.ts
+++ b/src/util/constants.ts
@@ -3,8 +3,7 @@ import { environment } from 'src/environments/environment'
 
 export const LOCAL_STORAGE_CONST = {
   GPU_LIMIT: 'fzj.xg.iv.GPU_LIMIT',
-  ANIMATION: 'fzj.xg.iv.ANIMATION_FLAG',
-  SAVED_REGION_SELECTIONS: 'fzj.xg.iv.SAVED_REGION_SELECTIONS',
+  ANIMATION: 'fzj.xg.iv.DISABLE_ANIMATION_FLAG',
   MOBILE_UI: 'fzj.xg.iv.MOBILE_UI',
   AGREE_COOKIE: 'fzj.xg.iv.AGREE_COOKIE',
   AGREE_KG_TOS: 'fzj.xg.iv.AGREE_KG_TOS',
@@ -15,7 +14,6 @@ export const LOCAL_STORAGE_CONST = {
 
 export const COOKIE_VERSION = '0.3.0'
 export const KG_TOS_VERSION = '0.3.0'
-export const DS_PREVIEW_URL = environment.DATASET_PREVIEW_URL
 export const BACKENDURL = (() => {
   const { BACKEND_URL } = environment
   if (!BACKEND_URL) return ``
@@ -84,7 +82,6 @@ export const getShader = ({
   removeBg = false
 } = {}): string => {
   const { header, main, premain } = mapKeyColorMap.get(colormap) || (() => {
-    console.warn(`colormap ${colormap} not found. Using default colormap instead`)
     return mapKeyColorMap.get(EnumColorMapName.GREYSCALE)
   })()
 
diff --git a/src/util/directives/floatingContainer.directive.ts b/src/util/directives/floatingContainer.directive.ts
deleted file mode 100644
index 4ff9eb3b102668138f6a0ab8ed3eeacf02296fbf..0000000000000000000000000000000000000000
--- a/src/util/directives/floatingContainer.directive.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { Directive, ViewContainerRef } from "@angular/core";
-import { WidgetServices } from "src/widget";
-
-@Directive({
-  selector: '[floatingContainerDirective]',
-})
-
-export class FloatingContainerDirective {
-  constructor(
-    widgetService: WidgetServices,
-    viewContainerRef: ViewContainerRef,
-  ) {
-    widgetService.floatingContainer = viewContainerRef
-  }
-}
diff --git a/src/util/fn.spec.ts b/src/util/fn.spec.ts
index 61bb2fd8cb23665bc85aacb76b298f026d2f89aa..efd5e418d29f6d46df2442bb8d02cb1a63b14840 100644
--- a/src/util/fn.spec.ts
+++ b/src/util/fn.spec.ts
@@ -3,78 +3,10 @@ import {} from 'jasmine'
 import { hot } from 'jasmine-marbles'
 import { Observable, of } from 'rxjs'
 import { switchMap } from 'rxjs/operators'
-import { isSame, getGetRegionFromLabelIndexId, switchMapWaitFor, bufferUntil } from './fn'
+import { switchMapWaitFor, bufferUntil } from './fn'
 
 describe(`> util/fn.ts`, () => {
 
-  describe('> #getGetRegionFromLabelIndexId', () => {
-    
-    const COLIN_JULICHBRAIN_LAYER_NAME = `COLIN_V25_LEFT_NG_SPLIT_HEMISPHERE`
-    const LABEL_INDEX = 12
-    const dummyParc = {
-      regions: [
-        {
-          name: 'foo-bar',
-          children: [
-            {
-              name: 'foo-bar-region-ba',
-              ngId: `${COLIN_JULICHBRAIN_LAYER_NAME}-ba`,
-              labelIndex: LABEL_INDEX
-            },
-            {
-              name: 'foo-bar-region+1',
-              ngId: COLIN_JULICHBRAIN_LAYER_NAME,
-              labelIndex: LABEL_INDEX + 1
-            },
-            {
-              name: 'foo-bar-region',
-              ngId: COLIN_JULICHBRAIN_LAYER_NAME,
-              labelIndex: LABEL_INDEX
-            }
-          ]
-        }
-      ]
-    }
-    it('translateds hoc1 from labelIndex to region', () => {
-
-      const getRegionFromlabelIndexId = getGetRegionFromLabelIndexId({
-        parcellation: {
-          ...dummyParc,
-          updated: true,
-        },
-      })
-      const fetchedRegion = getRegionFromlabelIndexId({ labelIndexId: `${COLIN_JULICHBRAIN_LAYER_NAME}#${LABEL_INDEX}` })
-      expect(fetchedRegion).toBeTruthy()
-      expect(fetchedRegion.name).toEqual('foo-bar-region')
-      
-    })
-  })
-  describe(`> #isSame`, () => {
-    it('should return true with null, null', () => {
-      expect(isSame(null, null)).toBe(true)
-    })
-
-    it('should return true with string', () => {
-      expect(isSame('test', 'test')).toBe(true)
-    })
-
-    it(`should return true with numbers`, () => {
-      expect(isSame(12, 12)).toBe(true)
-    })
-
-    it('should return true with obj with name attribute', () => {
-
-      const obj = {
-        name: 'hello',
-      }
-      const obj2 = {
-        name: 'hello',
-        world: 'world',
-      }
-      expect(isSame(obj, obj2)).toBe(true)
-      expect(obj).not.toEqual(obj2)
-    })
-  })
 
   describe('> #switchMapWaitFor', () => {
     const val = 'hello world'
diff --git a/src/util/fn.ts b/src/util/fn.ts
index 479958c477576d2e34b7cc8f1be6243048024992..e5ef8ac6bde4b72b67c2fb76f7b94edde8123476 100644
--- a/src/util/fn.ts
+++ b/src/util/fn.ts
@@ -1,12 +1,6 @@
-import { deserialiseParcRegionId } from 'common/util'
 import { interval, Observable, of } from 'rxjs'
 import { filter, mapTo, take } from 'rxjs/operators'
 
-export function isSame(o, n) {
-  if (!o) { return !n }
-  return o === n || (o && n && o.name === n.name)
-}
-
 export function getViewer() {
   return (window as any).viewer
 }
@@ -45,26 +39,10 @@ const recursiveFlatten = (region, {ngId}) => {
   )
 }
 
-export function recursiveFindRegionWithLabelIndexId({ regions, labelIndexId, inheritedNgId = 'root' }: {regions: any[], labelIndexId: string, inheritedNgId: string}) {
-  const { ngId, labelIndex } = deserialiseParcRegionId( labelIndexId )
-  const fr1 = regions.map(r => recursiveFlatten(r, { ngId: inheritedNgId }))
-  const fr2 = fr1.reduce((acc, curr) => acc.concat(...curr), [])
-  const found = fr2.find(r => r.ngId === ngId && Number(r.labelIndex) === Number(labelIndex))
-  if (found) { return found }
-  return null
-}
-
 export function getUuid(){
   return crypto.getRandomValues(new Uint32Array(1))[0].toString(16)
 }
 
-export const getGetRegionFromLabelIndexId = ({ parcellation }) => {
-  const { ngId: defaultNgId, regions } = parcellation
-  // if (!updated) throw new Error(`parcellation not yet updated`)
-  return ({ labelIndexId }) =>
-    recursiveFindRegionWithLabelIndexId({ regions, labelIndexId, inheritedNgId: defaultNgId })
-}
-
 type TPrimitive = string | number
 
 const include = <T extends TPrimitive>(el: T, arr: T[]) => arr.indexOf(el) >= 0
@@ -131,8 +109,7 @@ export class QuickHash {
     if (opts?.length) this.length = opts.length
   }
 
-  @CachedFunction()
-  getHash(str: string){
+  static GetHash(str: string) {
     let hash = 0
     for (const char of str) {
       const charCode = char.charCodeAt(0)
@@ -141,6 +118,11 @@ export class QuickHash {
     }
     return hash.toString(16).slice(1)
   }
+
+  @CachedFunction()
+  getHash(str: string){
+    return QuickHash.GetHash(str)
+  }
 }
 
 // fsaverage uses threesurfer, which, whilst do not use ngId, uses 'left' and 'right' as keys 
diff --git a/src/util/generator.ts b/src/util/generator.ts
index 9c9d14a9064fb6d113d7c648ae59c69575314c69..fcf39012ca428114ebe93974a5aa1951db8bef15 100644
--- a/src/util/generator.ts
+++ b/src/util/generator.ts
@@ -1,7 +1,7 @@
 export function* timedValues(ms: number = 500, mode: string = 'linear') {
   const startTime = Date.now()
 
-  const getValue = (fraction) => {
+  const getValue = (fraction: number) => {
     switch (mode) {
     case 'linear':
     default:
diff --git a/src/util/index.ts b/src/util/index.ts
index 21b7d4d11094b30d4e750237e94ebc32a516cdce..37bf9c0b4ee949075bad7c12aaa2c66d9eaff0e2 100644
--- a/src/util/index.ts
+++ b/src/util/index.ts
@@ -1,5 +1,4 @@
 export { UtilModule } from './util.module'
-export { PureContantService } from './pureConstant.service'
 export { CLICK_INTERCEPTOR_INJECTOR, ClickInterceptor, CONTEXT_MENU_ITEM_INJECTOR, TClickInterceptorConfig, TContextMenu } from './injectionTokens'
 export {
   DoublyLinkedList,
diff --git a/src/util/injectionTokens.ts b/src/util/injectionTokens.ts
index f8720f5d6ce4ca2e19c473799afb695bee18be05..feb547176fb2e37b7fe5222e716d1cf8c257a5ce 100644
--- a/src/util/injectionTokens.ts
+++ b/src/util/injectionTokens.ts
@@ -1,4 +1,5 @@
 import { InjectionToken } from "@angular/core";
+import { Observable } from "rxjs";
 
 export const CLICK_INTERCEPTOR_INJECTOR = new InjectionToken<ClickInterceptor>('CLICK_INTERCEPTOR_INJECTOR')
 
@@ -17,3 +18,5 @@ export type TContextMenu<T> = {
   register: (fn: T) => void
   deregister: (fn: (fn: T) => void) => void
 }
+
+export const DARKTHEME = new InjectionToken<Observable<boolean>>('DARKTHEME')
diff --git a/src/util/interfaces.ts b/src/util/interfaces.ts
index e5c2fbd9686a3f1036a5e34959c2e697e9f8d1a4..6c057cb1ebe6acb7381f58864a0511cb3c1dc061 100644
--- a/src/util/interfaces.ts
+++ b/src/util/interfaces.ts
@@ -16,13 +16,15 @@ export interface IHasFullId{
 
 export type TOverwriteShowDatasetDialog = (arg: any) => void
 
-export const OVERWRITE_SHOW_DATASET_DIALOG_TOKEN = new InjectionToken<TOverwriteShowDatasetDialog>('OVERWRITE_SHOW_DATASET_DIALOG_TOKEN')
-
 export type TRegionOfInterest = { ['fullId']: string }
 
-export const REGION_OF_INTEREST = new InjectionToken<Observable<TRegionOfInterest>>('RegionOfInterest')
 export const CANCELLABLE_DIALOG = new InjectionToken('CANCELLABLE_DIALOG')
 
+export type CANCELLABLE_DIALOG_OPTS = Partial<{
+  userCancelCallback: () => void
+  ariaLabel: string
+}>
+
 export type TTemplateImage = {
   name: string
   '@id': string
diff --git a/src/util/json.ts b/src/util/json.ts
new file mode 100644
index 0000000000000000000000000000000000000000..dc6125ad3d4c6de031608d32213ffda341a4fecd
--- /dev/null
+++ b/src/util/json.ts
@@ -0,0 +1,19 @@
+import { arrayEqual } from "./array"
+
+const defaultCmFn = <T>(o: T, n: T) => o === n
+export function jsonEqual<T>(compareFn: (o1: T, o2: T) => boolean = defaultCmFn) {
+  
+  return function(obj1: Record<string, T>, obj2: Record<string, T>){
+
+    /**
+     * first check if keys equal
+     */
+    if (!arrayEqual()(Object.keys(obj1), Object.keys(obj2))) {
+      return false
+    }
+    for (const key in obj1) {
+      if (!compareFn(obj1[key], obj2[key])) return false
+    }
+    return true
+  }
+}
diff --git a/src/util/pipes/nmToMm.pipe.ts b/src/util/pipes/nmToMm.pipe.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f156d6c88dc8fbfc45fb8ad7ce01fdf23a404d69
--- /dev/null
+++ b/src/util/pipes/nmToMm.pipe.ts
@@ -0,0 +1,12 @@
+import { Pipe, PipeTransform } from "@angular/core";
+
+@Pipe({
+  name: 'nmToMm',
+  pure: true
+})
+
+export class NmToMm implements PipeTransform{
+  public transform(nums: number[]): number[] {
+    return nums.map(num => (num / 1e6))
+  }
+}
diff --git a/src/util/priority.ts b/src/util/priority.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ab703f76b14e1a299fa2ae017d81038a43c1c858
--- /dev/null
+++ b/src/util/priority.ts
@@ -0,0 +1,176 @@
+import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse } from "@angular/common/http"
+import { Injectable } from "@angular/core"
+import { interval, merge, Observable, of, Subject, timer } from "rxjs"
+import { catchError, filter, finalize, map, switchMapTo, take, takeWhile } from "rxjs/operators"
+
+export const PRIORITY_HEADER = 'x-sxplr-http-priority'
+
+type ResultBase = {
+  urlWithParams: string
+}
+
+type Result<T> = {
+  result: HttpResponse<T>
+} & ResultBase
+
+type ErrorResult = {
+  error: Error
+} & ResultBase
+
+type Queue = {
+  urlWithParams: string
+  priority: number
+  req: HttpRequest<unknown>
+  next: HttpHandler
+}
+
+@Injectable({
+  providedIn: 'root'
+})
+export class PriorityHttpInterceptor implements HttpInterceptor{
+
+  private retry = 5
+  private disablePriority = false
+
+  private priorityQueue: Queue[] = []
+
+  private currentJob: Set<string> = new Set()
+  private archive: Map<string, HttpResponse<unknown>> = new Map()
+  private queue$: Subject<Queue> = new Subject()
+  private result$: Subject<Result<unknown>> = new Subject()
+  private error$: Subject<ErrorResult> = new Subject()
+
+  private forceCheck$ = new Subject()
+
+  private counter = 0
+  private max = 6
+
+  constructor(){
+    this.forceCheck$.pipe(
+      switchMapTo(
+        merge(
+          timer(0),
+          interval(16)
+        ).pipe(
+          filter(() => this.counter < this.max),
+          takeWhile(() => this.priorityQueue.length > 0)
+        )
+      )
+    ).subscribe(() => {
+      const job = this.priorityQueue.pop()
+      if (!job) return
+      this.currentJob.add(job.urlWithParams)
+      this.queue$.next(job)
+    })
+
+    this.queue$.subscribe(({ next, req, urlWithParams }) => {
+      this.counter ++
+      let retry = this.retry
+      next.handle(req).pipe(
+        finalize(() => {
+          this.counter --
+        }),
+        catchError((err, obs) => {
+          if (retry >= 0) {
+            retry --
+            return obs
+          }
+          return of(new Error(err))
+        }),
+      ).subscribe(val => {
+        if (val instanceof Error) {
+          this.error$.next({
+            urlWithParams,
+            error: val
+          })
+        }
+        if (val instanceof HttpResponse) {
+          this.archive.set(urlWithParams, val)
+          this.result$.next({
+            urlWithParams,
+            result: val
+          })
+        }
+      })
+    })
+  }
+
+  updatePriority(urlWithParams: string, newPriority: number) {
+    
+    if (this.currentJob.has(urlWithParams)) return
+
+    const foundIdx = this.priorityQueue.findIndex(v => v.urlWithParams === urlWithParams)
+    if (foundIdx < 0) return false
+    const [ item ] = this.priorityQueue.splice(foundIdx, 1)
+    item.priority = newPriority
+
+    this.insert(item)
+    this.forceCheck$.next(true)
+    return true
+  }
+
+  private insert(obj: Queue) {
+    const { priority, urlWithParams, req } = obj
+    
+    if (this.archive.has(urlWithParams)) return
+    if (this.currentJob.has(urlWithParams)) return
+
+    obj.req = req.clone({
+      headers: req.headers.delete(PRIORITY_HEADER)
+    })
+
+    const existing = this.priorityQueue.find(q => q.urlWithParams === urlWithParams)
+    if (existing) {
+      if (existing.priority < priority) {
+        this.updatePriority(urlWithParams, priority)
+      }
+      return
+    }
+    const foundIdx = this.priorityQueue.findIndex(q => q.priority <= priority)
+    const useIndex = foundIdx >= 0 ? foundIdx : this.priorityQueue.length
+    this.priorityQueue.splice(useIndex, 0, obj)
+  }
+
+  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
+    /**
+     * Do not use priority for requests other than get.
+     * Since the way in which serialization occurs is via path and query param...
+     * body is not used.
+     */
+    if (this.disablePriority || req.method !== 'GET') {
+      return next.handle(req)
+    }
+
+    const { urlWithParams } = req
+    if (this.archive.has(urlWithParams)) {
+      return of(
+        this.archive.get(urlWithParams).clone()
+      )
+    }
+    
+    const priority = Number(req.headers.get(PRIORITY_HEADER) || 0)
+    const objToInsert: Queue = {
+      priority,
+      req,
+      next,
+      urlWithParams
+    }
+
+    this.insert(objToInsert)
+    this.forceCheck$.next(true)
+
+    return merge(
+      this.result$,
+      this.error$,
+    ).pipe(
+      filter(v => v.urlWithParams === urlWithParams),
+      take(1),
+      map(v => {
+        if (v instanceof Error) {
+          throw v
+        }
+        return (v as Result<unknown>).result
+      })
+    )
+  }
+}
diff --git a/src/util/pureConstant.service.spec.ts b/src/util/pureConstant.service.spec.ts
deleted file mode 100644
index fa9ccf741bd443baf767d929d5786a08ecd190b7..0000000000000000000000000000000000000000
--- a/src/util/pureConstant.service.spec.ts
+++ /dev/null
@@ -1,85 +0,0 @@
-import { HttpClientTestingModule, HttpTestingController } from "@angular/common/http/testing"
-import { TestBed } from "@angular/core/testing"
-import { MatSnackBarModule } from "@angular/material/snack-bar"
-import { MockStore, provideMockStore } from "@ngrx/store/testing"
-import { BS_ENDPOINT } from "src/atlasComponents/regionalFeatures/bsFeatures"
-import { AtlasWorkerService } from "src/atlasViewer/atlasViewer.workerService.service"
-import { viewerStateFetchedAtlasesSelector, viewerStateFetchedTemplatesSelector } from "src/services/state/viewerState/selectors"
-import { PureContantService, SIIBRA_API_VERSION_HEADER_KEY, SIIBRA_API_VERSION } from "./pureConstant.service"
-import { TAtlas } from "./siibraApiConstants/types"
-
-const MOCK_BS_ENDPOINT = `http://localhost:1234`
-
-describe('> pureConstant.service.ts', () => {
-  describe('> PureContantService', () => {
-    let httpController: HttpTestingController
-    beforeEach(() => {
-      TestBed.configureTestingModule({
-        imports:[
-          HttpClientTestingModule,
-          MatSnackBarModule,
-        ],
-        providers: [
-          provideMockStore(),
-          {
-            provide: AtlasWorkerService,
-            useValue: {
-              worker: null
-            }
-          },
-          {
-            provide: BS_ENDPOINT,
-            useValue: MOCK_BS_ENDPOINT
-          }
-        ]
-      })
-
-      const mockStore = TestBed.inject(MockStore)
-      mockStore.overrideSelector(viewerStateFetchedTemplatesSelector, [])
-      mockStore.overrideSelector(viewerStateFetchedAtlasesSelector, [])
-      httpController = TestBed.inject(HttpTestingController)
-    })
-
-    afterEach(() => {
-      httpController.verify()
-    })
-
-    it('> can be init', () => {
-      const service = TestBed.inject(PureContantService)
-      const exp = httpController.expectOne(`${MOCK_BS_ENDPOINT}/atlases`)
-      exp.flush([])
-      expect(service).toBeTruthy()
-    })
-    describe('> allFetchingReady$', () => {
-      const mockAtlas: TAtlas = {
-        id: 'mockatlas id',
-        name: 'mockatlas name',
-        links: {
-          parcellations: {
-            href: `${MOCK_BS_ENDPOINT}/mockatlas-parcellation-href`
-          },
-          spaces: {
-            href: `${MOCK_BS_ENDPOINT}/atlas-spaces`
-          }
-        }
-      }
-      it('> can be init, and configuration emits allFetchingReady$', () => {
-        const service = TestBed.inject(PureContantService)
-        const exp = httpController.expectOne(`${MOCK_BS_ENDPOINT}/atlases`)
-        exp.flush([mockAtlas], {
-          headers: {
-            [SIIBRA_API_VERSION_HEADER_KEY]: SIIBRA_API_VERSION
-          }
-        })
-        service.allFetchingReady$.subscribe()
-
-        const expT1 = httpController.expectOne(`${MOCK_BS_ENDPOINT}/atlases/${encodeURIComponent(mockAtlas.id)}/spaces`)
-        expT1.flush([])
-
-        const expP1 = httpController.expectOne(`${MOCK_BS_ENDPOINT}/atlases/${encodeURIComponent(mockAtlas.id)}/parcellations`)
-        expP1.flush([])
-      })
-  
-    })
-  })
-})
diff --git a/src/util/pureConstant.service.ts b/src/util/pureConstant.service.ts
deleted file mode 100644
index c5f2aa4644e33a8a19d31fa0ef3e814acd625740..0000000000000000000000000000000000000000
--- a/src/util/pureConstant.service.ts
+++ /dev/null
@@ -1,853 +0,0 @@
-import { Inject, Injectable, OnDestroy } from "@angular/core";
-import { Store, select } from "@ngrx/store";
-import { Observable, Subscription, of, forkJoin, combineLatest, from } from "rxjs";
-import { viewerConfigSelectorUseMobileUi } from "src/services/state/viewerConfig.store.helper";
-import { shareReplay, tap, scan, catchError, filter, switchMap, map, distinctUntilChanged, mapTo } from "rxjs/operators";
-import { HttpClient } from "@angular/common/http";
-import { viewerStateFetchedTemplatesSelector, viewerStateSetFetchedAtlases } from "src/services/state/viewerState.store.helper";
-import { LoggingService } from "src/logging";
-import { viewerStateFetchedAtlasesSelector, viewerStateSelectedTemplateSelector } from "src/services/state/viewerState/selectors";
-import { BS_ENDPOINT, BACKENDURL } from "src/util/constants";
-import { flattenReducer } from 'common/util'
-import { IVolumeTypeDetail, TAtlas, TId, TParc, TRegionDetail, TRegionSummary, TSpaceFull, TSpaceSummary, TVolumeSrc } from "./siibraApiConstants/types";
-import { MultiDimMap, recursiveMutate, mutateDeepMerge } from "./fn";
-import { patchRegions } from './patchPureConstants'
-import { environment } from "src/environments/environment";
-import { MatSnackBar } from "@angular/material/snack-bar";
-import { TTemplateImage } from "./interfaces";
-
-export const SIIBRA_API_VERSION_HEADER_KEY='x-siibra-api-version'
-export const SIIBRA_API_VERSION = '0.1.13'
-
-const validVolumeType = new Set([
-  'neuroglancer/precomputed',
-  'neuroglancer/precompmesh',
-  'threesurfer/gii',
-  'threesurfer/gii-label',
-])
-
-function getNgId(atlasId: string, tmplId: string, parcId: string, regionKey: string){
-  const proxyId = MultiDimMap.GetProxyKeyMatch(atlasId, tmplId, parcId, regionKey)
-  if (proxyId) return proxyId
-  return '_' + MultiDimMap.GetKey(atlasId, tmplId, parcId, regionKey)
-}
-
-function parseId(id: TId){
-  if (typeof id === 'string') return id
-  return `${id.kg.kgSchema}/${id.kg.kgId}`
-}
-
-type THasId = {
-  ['@id']: string
-  name: string
-}
-
-type TIAVAtlas = {
-  templateSpaces: ({ availableIn: THasId[] } & THasId)[]
-  parcellations: ({
-    availableIn: THasId[]
-    baseLayer: boolean
-    '@version': {
-      name: string
-      '@next': string
-      '@previous': string
-      '@this': string
-    }
-  } & THasId)[]
-} & THasId
-
-type TNehubaConfig = Record<string, {
-  source: string
-  transform: number[][]
-  type: 'segmentation' | 'image'
-}>
-
-type TViewerConfig = TNehubaConfig
-
-/**
- * key value pair of
- * atlasId -> templateId -> viewerConfig
- */
-type TAtlasTmplViewerConfig = Record<string, Record<string, TViewerConfig>>
-
-export const spaceMiscInfoMap = new Map([
-  ['minds/core/referencespace/v1.0.0/a1655b99-82f1-420f-a3c2-fe80fd4c8588', {
-    name: 'bigbrain',
-    scale: 1,
-  }],
-  ['minds/core/referencespace/v1.0.0/dafcffc5-4826-4bf1-8ff6-46b8a31ff8e2', {
-    name: 'icbm2009c',
-    scale: 1,
-  }],
-  ['minds/core/referencespace/v1.0.0/7f39f7be-445b-47c0-9791-e971c0b6d992', {
-    name: 'colin27',
-    scale: 1,
-  }],
-  ['minds/core/referencespace/v1.0.0/265d32a0-3d84-40a5-926f-bf89f68212b9', {
-    name: 'allen-mouse',
-    scale: 0.1,
-  }],
-  ['minds/core/referencespace/v1.0.0/d5717c4a-0fa1-46e6-918c-b8003069ade8', {
-    name: 'waxholm',
-    scale: 0.1,
-  }],
-])
-
-function getNehubaConfig(space: TSpaceFull) {
-
-  const darkTheme = space.src_volume_type === 'mri'
-  const { scale } = spaceMiscInfoMap.get(space.id) || { scale: 1 }
-  const backgrd = darkTheme
-    ? [0,0,0,1]
-    : [1,1,1,1]
-
-  const rmPsp = darkTheme
-    ? {"mode":"<","color":[0.1,0.1,0.1,1]}
-    :{"color":[1,1,1,1],"mode":"=="}
-  const drawSubstrates = darkTheme
-    ? {"color":[0.5,0.5,1,0.2]}
-    : {"color":[0,0,0.5,0.15]}
-  const drawZoomLevels = darkTheme
-    ? {"cutOff":150000 * scale }
-    : {"cutOff":200000 * scale,"color":[0.5,0,0,0.15] }
-
-  // enable surface parcellation
-  // otherwise, on segmentation selection, the unselected meshes will also be invisible
-  const surfaceParcellation = space.id === 'minds/core/referencespace/v1.0.0/7f39f7be-445b-47c0-9791-e971c0b6d992'
-  return {
-    "configName": "",
-    "globals": {
-      "hideNullImageValues": true,
-      "useNehubaLayout": {
-        "keepDefaultLayouts": false
-      },
-      "useNehubaMeshLayer": true,
-      "rightClickWithCtrlGlobal": false,
-      "zoomWithoutCtrlGlobal": false,
-      "useCustomSegmentColors": true
-    },
-    "zoomWithoutCtrl": true,
-    "hideNeuroglancerUI": true,
-    "rightClickWithCtrl": true,
-    "rotateAtViewCentre": true,
-    "enableMeshLoadingControl": true,
-    "zoomAtViewCentre": true,
-    // "restrictUserNavigation": true,
-    "dataset": {
-      "imageBackground": backgrd,
-      "initialNgState": {
-        "showDefaultAnnotations": false,
-        "layers": {},
-        "navigation": {
-          "zoomFactor": 350000 * scale,
-        },
-        "perspectiveOrientation": [
-          0.3140767216682434,
-          -0.7418519854545593,
-          0.4988985061645508,
-          -0.3195493221282959
-        ],
-        "perspectiveZoom": 1922235.5293810747 * scale
-      }
-    },
-    "layout": {
-      "useNehubaPerspective": {
-        "perspectiveSlicesBackground": backgrd,
-        "removePerspectiveSlicesBackground": rmPsp,
-        "perspectiveBackground": backgrd,
-        "fixedZoomPerspectiveSlices": {
-          "sliceViewportWidth": 300,
-          "sliceViewportHeight": 300,
-          "sliceZoom": 563818.3562426177 * scale,
-          "sliceViewportSizeMultiplier": 2
-        },
-        "mesh": {
-          "backFaceColor": backgrd,
-          "removeBasedOnNavigation": true,
-          "flipRemovedOctant": true,
-          surfaceParcellation
-        },
-        "centerToOrigin": true,
-        "drawSubstrates": drawSubstrates,
-        "drawZoomLevels": drawZoomLevels,
-        "restrictZoomLevel": {
-          "minZoom": 1200000 * scale,
-          "maxZoom": 3500000 * scale
-        }
-      }
-    }
-  }
-  
-}
-
-@Injectable({
-  providedIn: 'root'
-})
-
-export class PureContantService implements OnDestroy{
-  
-  private subscriptions: Subscription[] = []
-  public repoUrl = `https://github.com/FZJ-INM1-BDA/siibra-explorer`
-  public supportEmailAddress = `support@ebrains.eu`
-  public docUrl = `https://siibra-explorer.readthedocs.io/`
-
-  public showHelpSupportText: string = `Did you encounter an issue?
-Send us an email: <a target = "_blank" href = "mailto:${this.supportEmailAddress}">${this.supportEmailAddress}</a>
-
-Raise/track issues at github repo: <a target = "_blank" href = "${this.repoUrl}">${this.repoUrl}</a>
-`
-
-  public useTouchUI$: Observable<boolean>
-  public darktheme$: Observable<boolean>
-
-  public totalAtlasesLength: number
-
-  public allFetchingReady$: Observable<boolean>
-
-  private atlasParcSpcRegionMap = new MultiDimMap()
-
-  private _backendUrl = (BACKENDURL && `${BACKENDURL}/`.replace(/\/\/$/, '/')) || `${window.location.origin}${window.location.pathname}`
-  get backendUrl() {
-    console.warn(`something is using backendUrl`)
-    return this._backendUrl
-  }
-
-  public getRegionDetail(atlasId: string, parcId: string, spaceId: string, region: any) {
-    return this.http.get<TRegionDetail>(
-      `${this.bsEndpoint}/atlases/${encodeURIComponent(atlasId)}/parcellations/${encodeURIComponent(parcId)}/regions/${encodeURIComponent(region.name)}`,
-      {
-        params: {
-          'space_id': spaceId
-        },
-        responseType: 'json'
-      }
-    )
-  }
-
-  private patchRegions$ = forkJoin(
-    patchRegions.map(patch => from(patch))
-  ).pipe(
-    shareReplay(1)
-  )
-
-  private getRegions(atlasId: string, parcId: string, spaceId: string){
-    return this.http.get<TRegionSummary[]>(
-      `${this.bsEndpoint}/atlases/${encodeURIComponent(atlasId)}/parcellations/${encodeURIComponent(parcId)}/regions`,
-      {
-        params: {
-          'space_id': spaceId
-        },
-        responseType: 'json'
-      }
-    ).pipe(
-      switchMap(regions => this.patchRegions$.pipe(
-        map(patchRegions => {
-          for (const p of patchRegions) {
-            if (
-              p.targetParcellation !== '*'
-              && Array.isArray(p.targetParcellation)
-              && p.targetParcellation.every(p => p["@id"] !== parcId)
-            ) {
-              continue
-            }
-            if (
-              p.targetSpace !== '*'
-              && Array.isArray(p.targetSpace)
-              && p.targetSpace.every(sp => sp['@id'] !== spaceId)
-            ) {
-              continue
-            }
-
-            recursiveMutate(
-              regions,
-              r => r.children || [],
-              region => {
-
-                if (p["@type"] === 'julich/siibra/append-region/v0.0.1') {
-                  if (p.parent['name'] === region.name) {
-                    if (!region.children) region.children = []
-                    region.children.push(
-                      p.payload as TRegionSummary
-                    )
-                  }
-                }
-                if (p['@type'] === 'julich/siibra/patch-region/v0.0.1') {
-                  if (p.target['name'] === region.name) {
-                    mutateDeepMerge(
-                      region,
-                      p.payload
-                    )
-                  }
-                }
-              },
-              true
-            )
-          }
-          return regions
-        })
-      ))
-    )
-  }
-
-  private getParcs(atlasId: string){
-    return this.http.get<TParc[]>(
-      `${this.bsEndpoint}/atlases/${encodeURIComponent(atlasId)}/parcellations`,
-      { responseType: 'json' }
-    )
-  }
-
-  private httpCallCache = new Map<string, Observable<any>>()
-
-  private getParcDetail(atlasId: string, parcId: string) {
-    return this.http.get<TParc>(
-      `${this.bsEndpoint}/atlases/${encodeURIComponent(atlasId)}/parcellations/${encodeURIComponent(parcId)}`,
-      { responseType: 'json' }
-    )
-  }
-
-  private getSpaces(atlasId: string){
-    return this.http.get<TSpaceSummary[]>(
-      `${this.bsEndpoint}/atlases/${encodeURIComponent(atlasId)}/spaces`,
-      { responseType: 'json' }
-    )
-  }
-
-  private getSpaceDetail(atlasId: string, spaceId: string) {
-    return this.http.get<TSpaceFull>(
-      `${this.bsEndpoint}/atlases/${encodeURIComponent(atlasId)}/spaces/${encodeURIComponent(spaceId)}`,
-      { responseType: 'json' }
-    )
-  }
-
-  private getSpacesAndParc(atlasId: string): Observable<{ templateSpaces: TSpaceFull[], parcellations: TParc[] }> {
-    const cacheKey = `getSpacesAndParc::${atlasId}`
-    if (this.httpCallCache.has(cacheKey)) return this.httpCallCache.get(cacheKey)
-    
-    const spaces$ = this.getSpaces(atlasId).pipe(
-      switchMap(spaces => spaces.length > 0
-        ? forkJoin(
-          spaces.map(space => this.getSpaceDetail(atlasId, parseId(space.id)))
-        )
-        : of([]))
-    )
-    const parcs$ = this.getParcs(atlasId).pipe(
-      // need not to get full parc data. first level gets all data
-      // switchMap(parcs => forkJoin(
-      //   parcs.map(parc => this.getParcDetail(atlasId, parseId(parc.id)))
-      // ))
-    )
-    const returnObs = forkJoin([
-      spaces$,
-      parcs$,
-    ]).pipe(
-      map(([ templateSpaces, parcellations ]) => {
-        /**
-         * select only parcellations that contain renderable volume(s)
-         */
-        const filteredParcellations = parcellations.filter(p => {
-          return p._dataset_specs.some(spec => spec["@type"] === 'fzj/tmp/volume_type/v0.0.1' && validVolumeType.has(spec.volume_type))
-        })
-
-        /**
-         * remove parcellation versions that are marked as deprecated
-         * and assign prev/next id accordingly
-         */
-        for (const p of filteredParcellations) {
-          if (!p.version) continue
-          if (p.version.deprecated) {
-            const prevId = p.version.prev
-            const nextId = p.version.next
-
-            const prev = prevId && filteredParcellations.find(p => parseId(p.id) === prevId)
-            const next = nextId && filteredParcellations.find(p => parseId(p.id) === nextId)
-
-            const newPrevId = prev && parseId(prev.id)
-            const newNextId = next && parseId(next.id)
-
-            if (!!prev.version) {
-              prev.version.next = newNextId
-            }
-
-            if (!!next.version) {
-              next.version.prev = newPrevId
-            }
-          }
-        }
-        const removeDeprecatedParc = filteredParcellations.filter(p => {
-          if (!p.version) return true
-          return !(p.version.deprecated)
-        })
-
-        return {
-          templateSpaces,
-          parcellations: removeDeprecatedParc
-        }
-      }),
-      shareReplay(1)
-    )
-    this.httpCallCache.set(cacheKey, returnObs)
-    return returnObs
-  }
-
-  constructor(
-    private store: Store<any>,
-    private http: HttpClient,
-    private log: LoggingService,
-    private snackbar: MatSnackBar,
-    @Inject(BS_ENDPOINT) private bsEndpoint: string,
-  ){
-    this.darktheme$ = this.store.pipe(
-      select(viewerStateSelectedTemplateSelector),
-      map(tmpl => tmpl?.useTheme === 'dark')
-    )
-
-    this.useTouchUI$ = this.store.pipe(
-      select(viewerConfigSelectorUseMobileUi),
-      shareReplay(1)
-    )
-
-    this.subscriptions.push(
-      this.fetchedAtlases$.subscribe(fetchedAtlases => 
-        this.store.dispatch(
-          viewerStateSetFetchedAtlases({ fetchedAtlases })
-        )
-      )
-    )
-
-    this.allFetchingReady$ = combineLatest([
-      this.initFetchTemplate$.pipe(
-        filter(v => !!v),
-        map(arr => arr.length),
-      ),
-      this.store.pipe(
-        select(viewerStateFetchedTemplatesSelector),
-        map(arr => arr.length),
-      ),
-      this.store.pipe(
-        select(viewerStateFetchedAtlasesSelector),
-        map(arr => arr.length),
-      )
-    ]).pipe(
-      map(([ expNumTmpl, actNumTmpl, actNumAtlas ]) => {
-        return expNumTmpl === actNumTmpl && actNumAtlas === this.totalAtlasesLength
-      }),
-      distinctUntilChanged(),
-      shareReplay(1),
-    )
-  }
-
-  private getAtlases$ = this.http.get<TAtlas[]>(
-    `${this.bsEndpoint}/atlases`,
-    {
-      observe: 'response'
-    }
-  ).pipe(
-    tap(resp => {
-      const respVersion = resp.headers.get(SIIBRA_API_VERSION_HEADER_KEY)
-      if (respVersion !== SIIBRA_API_VERSION) {
-        this.snackbar.open(`Expecting ${SIIBRA_API_VERSION}, got ${respVersion}. Some functionalities may not work as expected.`, 'Dismiss', {
-          duration: 5000
-        })
-      }
-      console.log(`siibra-api::version::${respVersion}, expecting::${SIIBRA_API_VERSION}`)
-    }),
-    map(resp => {
-      const arr = resp.body
-      const { EXPERIMENTAL_FEATURE_FLAG } = environment
-      if (EXPERIMENTAL_FEATURE_FLAG) return arr
-      return arr
-    }),
-    shareReplay(1),
-  )
-
-  public fetchedAtlases$: Observable<TIAVAtlas[]> = this.getAtlases$.pipe(
-    switchMap(atlases => {
-      return forkJoin(
-        atlases.map(
-          atlas => this.getSpacesAndParc(atlas.id).pipe(
-            map(({ templateSpaces, parcellations }) => {
-              return {
-                '@id': atlas.id,
-                name: atlas.name,
-                templateSpaces: templateSpaces.map(tmpl => {
-                  return {
-                    '@id': tmpl.id,
-                    name: tmpl.name,
-                    availableIn: tmpl.availableParcellations.map(parc => {
-                      return {
-                        '@id': parc.id,
-                        name: parc.name
-                      }
-                    }),
-                    originDatainfos: (tmpl._dataset_specs || []).filter(spec => spec["@type"] === 'fzj/tmp/simpleOriginInfo/v0.0.1')
-                  }
-                }),
-                parcellations: parcellations.filter(p => {
-                  if (p.version?.deprecated) return false
-                  return true
-                }).map(parc => {
-                  return {
-                    '@id': parseId(parc.id),
-                    name: parc.name,
-                    baseLayer: parc.modality === 'cytoarchitecture',
-                    '@version': {
-                      '@next': parc.version?.next,
-                      '@previous': parc.version?.prev,
-                      'name': parc.version?.name,
-                      '@this': parseId(parc.id)
-                    },
-                    groupName: parc.modality || null,
-                    availableIn: parc.availableSpaces.map(space => {
-                      return {
-                        '@id': space.id,
-                        name: space.name,
-                        /**
-                         * TODO need original data format
-                         */
-                        // originalDatasetFormats: [{
-                        //   name: "probability map"
-                        // }]
-                      }
-                    }),
-                    originDatainfos: [...(parc.infos || []), ...(parc._dataset_specs || []).filter(spec => spec["@type"] === 'fzj/tmp/simpleOriginInfo/v0.0.1')]
-                  }
-                })
-              }
-            }),
-            catchError((err, obs) => {
-              console.error(err)
-              return of(null)
-            })
-          )
-        )
-      )
-    }),
-    catchError((err, obs) => of([])),
-    tap((arr: any[]) => this.totalAtlasesLength = arr.length),
-    scan((acc, curr) => acc.concat(curr).sort((a, b) => (a.order || 0) - (b.order || 0)), []),
-    shareReplay(1)
-  )
-
-  private atlasTmplConfig: TAtlasTmplViewerConfig = {}
-
-  async getViewerConfig(atlasId: string, templateId: string, parcId: string) {
-    const atlasLayers = this.atlasTmplConfig[atlasId]
-    const templateLayers = atlasLayers && atlasLayers[templateId]
-    return templateLayers || {}
-  }
-
-  public initFetchTemplate$ = this.fetchedAtlases$.pipe(
-    switchMap(atlases => {
-      return forkJoin(
-        atlases.map(atlas => this.getSpacesAndParc(atlas['@id']).pipe(
-          switchMap(({ templateSpaces, parcellations }) => {
-            this.atlasTmplConfig[atlas["@id"]] = {}
-            return forkJoin(
-              templateSpaces.map(
-                tmpl => {
-                  // hardcode 
-                  // see https://github.com/FZJ-INM1-BDA/siibra-python/issues/98
-                  if (
-                    tmpl.id === 'minds/core/referencespace/v1.0.0/tmp-fsaverage'
-                    && !tmpl.availableParcellations.find(p => p.id === 'minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579-290')  
-                  ) {
-                    tmpl.availableParcellations.push({
-                      id: 'minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579-290',
-                      name: 'Julich-Brain Probabilistic Cytoarchitectonic Maps (v2.9)'
-                    })
-                  }
-                  this.atlasTmplConfig[atlas["@id"]][tmpl.id] = {}
-                  return tmpl.availableParcellations.map(
-                    parc => this.getRegions(atlas['@id'], parc.id, tmpl.id).pipe(
-                      tap(regions => {
-                        recursiveMutate(
-                          regions,
-                          region => region.children,
-                          region => {
-                            /**
-                             * individual map(s)
-                             * this should work for both fully mapped and interpolated
-                             * in the case of interpolated, it sucks that the ngLayerObj will be set multiple times
-                             */
-
-                            const dedicatedMap = region._dataset_specs.filter(
-                              spec => spec["@type"] === 'fzj/tmp/volume_type/v0.0.1'
-                              && spec.space_id === tmpl.id
-                              && spec['volume_type'] === 'neuroglancer/precomputed'
-                            ) as TVolumeSrc<'neuroglancer/precomputed'>[]
-                            if (dedicatedMap.length === 1) {
-                              const ngId = getNgId(atlas['@id'], tmpl.id, parc.id, dedicatedMap[0]['@id'])
-                              region['ngId'] = ngId
-                              region['labelIndex'] = dedicatedMap[0].detail['neuroglancer/precomputed'].labelIndex
-                              this.atlasTmplConfig[atlas["@id"]][tmpl.id][ngId] = {
-                                source: `precomputed://${dedicatedMap[0].url}`,
-                                type: "segmentation",
-                                transform: dedicatedMap[0].detail['neuroglancer/precomputed'].transform
-                              }
-                            }
-  
-                            /**
-                             * if label index is defined
-                             */
-                            if (!!region.labelIndex) {
-                              const hemisphereKey = /left hemisphere|left/.test(region.name)
-                                // these two keys are, unfortunately, more or less hardcoded
-                                // which is less than ideal
-                                ? 'left hemisphere'
-                                : /right hemisphere|right/.test(region.name)
-                                  ? 'right hemisphere'
-                                  : 'whole brain'
-
-                              if (!region['ngId']) {
-                                const hemispheredNgId = getNgId(atlas['@id'], tmpl.id, parc.id, hemisphereKey)
-                                region['ngId'] = hemispheredNgId
-                              }
-                            }
-                          }  
-                        )
-                        this.atlasParcSpcRegionMap.set(
-                          atlas['@id'], tmpl.id, parc.id, regions
-                        )
-  
-                        /**
-                         * populate maps for parc
-                         */
-                        for (const parc of parcellations) {
-                          const precomputedVols = parc._dataset_specs.filter(
-                            spec => spec["@type"] === 'fzj/tmp/volume_type/v0.0.1'
-                              && spec.volume_type === 'neuroglancer/precomputed'
-                              && spec.space_id === tmpl.id
-                          ) as TVolumeSrc<'neuroglancer/precomputed'>[]
-
-                          if (precomputedVols.length === 1) {
-                            const vol = precomputedVols[0]
-                            const key = 'whole brain'
-
-                            const ngIdKey = getNgId(atlas['@id'], tmpl.id, parseId(parc.id), key)
-                            this.atlasTmplConfig[atlas["@id"]][tmpl.id][ngIdKey] = {
-                              source: `precomputed://${vol.url}`,
-                              type: "segmentation",
-                              transform: vol.detail['neuroglancer/precomputed'].transform
-                            }
-                          }
-
-                          if (precomputedVols.length === 2) {
-                            const mapIndexKey = [{
-                              mapIndex: 0,
-                              key: 'left hemisphere'
-                            }, {
-                              mapIndex: 1,
-                              key: 'right hemisphere'
-                            }]
-                            for (const { key, mapIndex } of mapIndexKey) {
-                              const ngIdKey = getNgId(atlas['@id'], tmpl.id, parseId(parc.id), key)
-                              this.atlasTmplConfig[atlas["@id"]][tmpl.id][ngIdKey] = {
-                                source: `precomputed://${precomputedVols[mapIndex].url}`,
-                                type: "segmentation",
-                                transform: precomputedVols[mapIndex].detail['neuroglancer/precomputed'].transform
-                              }
-                            }
-                          }
-
-                          if (precomputedVols.length > 2) {
-                            console.error(`precomputedVols.length > 0, most likely an error`)
-                          }
-                        }
-                      }),
-                      catchError((err, obs) => {
-                        return of(null)
-                      })
-                    )
-                  )
-                }
-              ).reduce(flattenReducer, [])
-            ).pipe(
-              mapTo({ templateSpaces, parcellations, ngLayerObj: this.atlasTmplConfig })
-            )
-          }),
-          map(({ templateSpaces, parcellations, ngLayerObj }) => {
-            return templateSpaces.map(tmpl => {
-
-              // configuring three-surfer
-              let threeSurferConfig = {}
-              const volumes  = tmpl._dataset_specs.filter(v => v["@type"] === 'fzj/tmp/volume_type/v0.0.1') as TVolumeSrc<keyof IVolumeTypeDetail>[]
-              const threeSurferVolSrc = volumes.find(v => v.volume_type === 'threesurfer/gii')
-              if (threeSurferVolSrc) {
-                const foundP = parcellations.find(p => {
-                  return p._dataset_specs.some(spec => spec["@type"] === 'fzj/tmp/volume_type/v0.0.1' && spec.space_id === tmpl.id)
-                })
-                const url = threeSurferVolSrc.url
-                const { surfaces } = threeSurferVolSrc.detail['threesurfer/gii'] as { surfaces: {mode: string, hemisphere: 'left' | 'right', url: string}[] }
-                const modObj = {}
-                for (const surface of surfaces) {
-                  
-                  const hemisphereKey = surface.hemisphere === 'left'
-                    ? 'left hemisphere'
-                    : 'right hemisphere'
-
-
-                  /**
-                   * concating all available gii maps
-                   */
-                  // const allFreesurferLabels = foundP.volumeSrc[tmpl.id][hemisphereKey].filter(v => v.volume_type === 'threesurfer/gii-label')
-                  // for (const lbl of allFreesurferLabels) {
-                  //   const modeToConcat = {
-                  //     mesh: surface.url,
-                  //     hemisphere: surface.hemisphere,
-                  //     colormap: lbl.url
-                  //   }
-
-                  //   const key = `${surface.mode} - ${lbl.name}`
-                  //   if (!modObj[key]) {
-                  //     modObj[key] = []
-                  //   }
-                  //   modObj[key].push(modeToConcat)
-                  // }
-
-                  /**
-                   * only concat first matching gii map
-                   */
-                  const mapIndex = hemisphereKey === 'left hemisphere'
-                    ? 0
-                    : 1
-                  const labelMaps = foundP._dataset_specs.filter(spec => spec["@type"] === 'fzj/tmp/volume_type/v0.0.1' && spec.volume_type === 'threesurfer/gii-label') as TVolumeSrc<'threesurfer/gii-label'>[]
-                  const key = surface.mode
-                  const modeToConcat = {
-                    mesh: surface.url,
-                    hemisphere: surface.hemisphere,
-                    colormap: (() => {
-                      const lbl = labelMaps[mapIndex]
-                      return lbl?.url
-                    })()
-                  }
-                  if (!modObj[key]) {
-                    modObj[key] = []
-                  }
-                  modObj[key].push(modeToConcat)
-
-                }
-                foundP[tmpl.id]
-                threeSurferConfig = {
-                  "three-surfer": {
-                    '@context': {
-                      root: url
-                    },
-                    modes: Object.keys(modObj).map(name => {
-                      return {
-                        name,
-                        meshes: modObj[name]
-                      }
-                    })
-                  },
-                  nehubaConfig: null,
-                  nehubaConfigURL: null,
-                  useTheme: 'dark'
-                }
-              }
-              const darkTheme = tmpl.src_volume_type === 'mri'
-              const nehubaConfig = getNehubaConfig(tmpl)
-              const initialLayers = nehubaConfig.dataset.initialNgState.layers
-              
-              const tmplAuxMesh = `${tmpl.name} auxmesh`
-
-              const precomputedArr = tmpl._dataset_specs.filter(src => src['@type'] === 'fzj/tmp/volume_type/v0.0.1' && src.volume_type === 'neuroglancer/precomputed') as TVolumeSrc<'neuroglancer/precomputed'>[]
-              let visible = true
-              let tmplNgId: string
-              const templateImages: TTemplateImage[] = []
-              for (const precomputedItem of precomputedArr) {
-                const ngIdKey = MultiDimMap.GetKey(precomputedItem["@id"])
-                const precomputedUrl = 'https://neuroglancer.humanbrainproject.eu/precomputed/data-repo-ng-bot/20211001-mebrain/precomputed/images/MEBRAINS_T1.masked' === precomputedItem.url
-                  ? 'https://neuroglancer.humanbrainproject.eu/precomputed/data-repo-ng-bot/20211018-mebrains-masked-templates/precomputed/images/MEBRAINS_T1_masked'
-                  : precomputedItem.url
-                initialLayers[ngIdKey] = {
-                  type: "image",
-                  source: `precomputed://${precomputedUrl}`,
-                  transform: precomputedItem.detail['neuroglancer/precomputed'].transform,
-                  visible
-                }
-                templateImages.push({
-                  "@id": precomputedItem['@id'],
-                  name: precomputedItem.name,
-                  ngId: ngIdKey,
-                  visible
-                })
-                if (visible) {
-                  tmplNgId = ngIdKey
-                }
-                visible = false
-              }
-
-              // TODO
-              // siibra-python accidentally left out volume type of precompmesh
-              // https://github.com/FZJ-INM1-BDA/siibra-python/pull/55
-              // use url to determine for now
-              // const precompmesh = tmpl.volume_src.find(src => src.volume_type === 'neuroglancer/precompmesh')
-              const precompmesh = tmpl._dataset_specs.find(src => src["@type"] === 'fzj/tmp/volume_type/v0.0.1' && !!src.detail?.['neuroglancer/precompmesh']) as TVolumeSrc<'neuroglancer/precompmesh'>
-              const auxMeshes = []
-              if (precompmesh){
-                initialLayers[tmplAuxMesh] = {
-                  source: `precompmesh://${precompmesh.url}`,
-                  type: "segmentation",
-                  transform: precompmesh.detail['neuroglancer/precompmesh'].transform
-                }
-                for (const auxMesh of precompmesh.detail['neuroglancer/precompmesh'].auxMeshes) {
-
-                  auxMeshes.push({
-                    ...auxMesh,
-                    ngId: tmplAuxMesh,
-                    '@id': `${tmplAuxMesh} ${auxMesh.name}`,
-                    visible: true
-                  })
-                }
-              }
-
-              for (const key in (ngLayerObj[atlas["@id"]][tmpl.id] || {})) {
-                initialLayers[key] = ngLayerObj[atlas["@id"]][tmpl.id][key]
-              }
-
-              return {
-                name: tmpl.name,
-                '@id': tmpl.id,
-                fullId: tmpl.id,
-                useTheme: darkTheme ? 'dark' : 'light',
-                ngId: tmplNgId,
-                nehubaConfig,
-                templateImages,
-                auxMeshes,
-                /**
-                 * only populate the parcelltions made available
-                 */
-                parcellations: tmpl.availableParcellations.filter(
-                  p => parcellations.some(p2 => parseId(p2.id) === p.id)
-                ).map(parc => {
-                  const fullParcInfo = parcellations.find(p => parseId(p.id) === parc.id)
-                  const regions = this.atlasParcSpcRegionMap.get(atlas['@id'], tmpl.id, parc.id) || []
-                  return {
-                    fullId: parc.id,
-                    '@id': parc.id,
-                    name: parc.name,
-                    regions,
-                    originDatainfos: [...fullParcInfo.infos, ...(fullParcInfo?._dataset_specs || []).filter(spec => spec["@type"] === 'fzj/tmp/simpleOriginInfo/v0.0.1')]
-                  }
-                }),
-                ...threeSurferConfig
-              }
-            })
-          })
-        ))
-      )
-    }),
-    map(arr => {
-      return arr.reduce(flattenReducer, [])
-    }),
-    catchError((err) => {
-      this.log.warn(`fetching templates error`, err)
-      return of(null)
-    }),
-    shareReplay(1),
-  )
-
-  ngOnDestroy(){
-    while(this.subscriptions.length > 0) this.subscriptions.pop().unsubscribe()
-  }
-}
diff --git a/src/util/util.module.ts b/src/util/util.module.ts
index f128cf3fd266a1851d2951bf21c78ffffd4cedfd..b1007281e3c037b4766b301c0189e15dca21a878 100644
--- a/src/util/util.module.ts
+++ b/src/util/util.module.ts
@@ -1,13 +1,10 @@
 import { NgModule } from "@angular/core";
-import { FilterRowsByVisbilityPipe } from "src/components/flatTree/filterRowsByVisibility.pipe";
 import { KeyListner } from "./directives/keyDownListener.directive";
 
 import { StopPropagationDirective } from "./directives/stopPropagation.directive";
-import { IncludesPipe } from "./pipes/includes.pipe";
 import { SafeResourcePipe } from "./pipes/safeResource.pipe";
 import { CaptureClickListenerDirective } from "./directives/captureClickListener.directive";
-import { AddUnitAndJoin } from "./pipes/addUnitAndJoin.pipe";
-import { NmToMm } from "./pipes/numbers.pipe";
+import { NmToMm } from "./pipes/nmToMm.pipe";
 import { SwitchDirective } from "./directives/switch.directive";
 import { MediaQueryDirective } from './directives/mediaQuery.directive'
 import { LayoutModule } from "@angular/cdk/layout";
@@ -28,13 +25,10 @@ import { MergeObjPipe } from "./mergeObj.pipe";
     LayoutModule
   ],
   declarations: [
-    FilterRowsByVisbilityPipe,
     StopPropagationDirective,
     KeyListner,
-    IncludesPipe,
     SafeResourcePipe,
     CaptureClickListenerDirective,
-    AddUnitAndJoin,
     NmToMm,
     SwitchDirective,
     MediaQueryDirective,
@@ -51,13 +45,10 @@ import { MergeObjPipe } from "./mergeObj.pipe";
     MergeObjPipe,
   ],
   exports: [
-    FilterRowsByVisbilityPipe,
     StopPropagationDirective,
     KeyListner,
-    IncludesPipe,
     SafeResourcePipe,
     CaptureClickListenerDirective,
-    AddUnitAndJoin,
     NmToMm,
     SwitchDirective,
     MediaQueryDirective,
diff --git a/src/util/windowResize/windowResize.directive.ts b/src/util/windowResize/windowResize.directive.ts
index ce8de31804fcaec53296925c5eef5235979719b0..2a8f83fd99b19c883d6060c2709f371b5a4210cb 100644
--- a/src/util/windowResize/windowResize.directive.ts
+++ b/src/util/windowResize/windowResize.directive.ts
@@ -1,4 +1,4 @@
-import { Directive, EventEmitter, Input, OnChanges, OnInit, Output } from "@angular/core";
+import { Directive, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output } from "@angular/core";
 import { Subscription } from "rxjs";
 import { ResizeObserverService } from "./windowResize.service";
 
@@ -7,7 +7,7 @@ import { ResizeObserverService } from "./windowResize.service";
   exportAs: 'iavWindowResize'
 })
 
-export class ResizeObserverDirective implements OnChanges, OnInit {
+export class ResizeObserverDirective implements OnChanges, OnInit, OnDestroy {
   @Input('iav-window-resize-type')
   type: 'debounce' | 'throttle' = 'throttle'
 
@@ -27,14 +27,18 @@ export class ResizeObserverDirective implements OnChanges, OnInit {
 
   constructor(private svc: ResizeObserverService){}
 
-  ngOnInit(){
+  ngOnInit(): void {
     this.configure()
   }
-  ngOnChanges(){
+  ngOnChanges(): void {
     this.configure()
   }
 
-  configure(){
+  ngOnDestroy(): void {
+    while(this.sub.length > 0) this.sub.pop().unsubscribe()
+  }
+
+  configure(): void {
     while(this.sub.length > 0) this.sub.pop().unsubscribe()
 
     let sub: Subscription
diff --git a/src/util/windowResize/windowResize.service.ts b/src/util/windowResize/windowResize.service.ts
index f50c7df0e929a935b56176311b887d585d6412eb..f48a5832517f9e770356deab0f131e1b167b36a8 100644
--- a/src/util/windowResize/windowResize.service.ts
+++ b/src/util/windowResize/windowResize.service.ts
@@ -1,6 +1,6 @@
 import { Injectable } from "@angular/core";
-import { asyncScheduler, fromEvent } from "rxjs";
-import { debounceTime, shareReplay, tap, throttleTime } from "rxjs/operators";
+import { asyncScheduler, fromEvent, Observable } from "rxjs";
+import { debounceTime, shareReplay, throttleTime } from "rxjs/operators";
 
 interface IThrottleConfig {
   leading: boolean
@@ -16,13 +16,13 @@ export class ResizeObserverService {
     shareReplay(1)
   )
 
-  public getThrottledResize(time: number, config?: IThrottleConfig){
+  public getThrottledResize(time: number, config?: IThrottleConfig) : Observable<Event>{
     return this.windowResize.pipe(
       throttleTime(time, asyncScheduler, config || { leading: false, trailing: true }),
     )
   }
 
-  public getDebouncedResize(time: number) {
+  public getDebouncedResize(time: number): Observable<Event> {
     return this.windowResize.pipe(
       debounceTime(time)
     )
diff --git a/src/viewerModule/componentStore.ts b/src/viewerModule/componentStore.ts
index 5fe24c51085c329be8ed3eaee2048f3114cbda43..3e6d6e79b6e6942b44ebf8ad7ef4b5a44f166f74 100644
--- a/src/viewerModule/componentStore.ts
+++ b/src/viewerModule/componentStore.ts
@@ -1,6 +1,6 @@
 import { Injectable } from "@angular/core";
 import { select } from "@ngrx/store";
-import { ReplaySubject, Subject } from "rxjs";
+import { Observable, ReplaySubject, Subject } from "rxjs";
 import { shareReplay } from "rxjs/operators";
 
 export class LockError extends Error{}
@@ -15,14 +15,14 @@ export class LockError extends Error{}
 export class ComponentStore<T>{
   private _state$: Subject<T> = new ReplaySubject<T>(1)
   private _lock: boolean = false
-  get isLocked() {
+  get isLocked(): boolean {
     return this._lock
   }
-  setState(state: T){
+  setState(state: T): void {
     if (this.isLocked) throw new LockError('State is locked')
     this._state$.next(state)
   }
-  select<V>(selectorFn: (state: T) => V) {
+  select<V>(selectorFn: (state: T) => V): Observable<V> {
     return this._state$.pipe(
       select(selectorFn),
       shareReplay(1),
diff --git a/src/viewerModule/constants.ts b/src/viewerModule/constants.ts
index 8fa2d25231b1435428d4ca4f652617904fcc95bc..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644
--- a/src/viewerModule/constants.ts
+++ b/src/viewerModule/constants.ts
@@ -1,4 +0,0 @@
-import { InjectionToken } from "@angular/core";
-import { Observable } from "rxjs";
-
-export const VIEWERMODULE_DARKTHEME = new InjectionToken<Observable<boolean>>('VIEWERMODULE_DARKTHEME')
diff --git a/src/viewerModule/index.ts b/src/viewerModule/index.ts
index 53f950d5421d8dbf54ce6482c72be6dddfe9c377..6e5c74fbe55be040c17b54d98c7b6f29cd66a513 100644
--- a/src/viewerModule/index.ts
+++ b/src/viewerModule/index.ts
@@ -1,2 +1 @@
-export { ViewerModule } from "./module"
-export { VIEWERMODULE_DARKTHEME } from './constants'
\ No newline at end of file
+export { ViewerModule } from "./module"
\ No newline at end of file
diff --git a/src/viewerModule/module.ts b/src/viewerModule/module.ts
index 8cd43cf6ace30f25be8afea1cffe58be1c175881..14779f2a3f322e0162e88c35a615adf78482aec7 100644
--- a/src/viewerModule/module.ts
+++ b/src/viewerModule/module.ts
@@ -1,34 +1,31 @@
 import { CommonModule } from "@angular/common";
 import { NgModule } from "@angular/core";
 import { Observable } from "rxjs";
-import { AtlasCmpParcellationModule } from "src/atlasComponents/parcellation";
-import { ParcellationRegionModule } from "src/atlasComponents/parcellationRegion";
-import { BSFeatureModule, BS_DARKTHEME,  } from "src/atlasComponents/regionalFeatures/bsFeatures";
-import { SplashUiModule } from "src/atlasComponents/splashScreen";
-import { AtlasCmpUiSelectorsModule } from "src/atlasComponents/uiSelectors";
 import { ComponentsModule } from "src/components";
 import { ContextMenuModule, ContextMenuService, TContextMenuReg } from "src/contextMenuModule";
 import { LayoutModule } from "src/layouts/layout.module";
 import { AngularMaterialModule } from "src/sharedModules";
 import { TopMenuModule } from "src/ui/topMenu/module";
 import { CONTEXT_MENU_ITEM_INJECTOR, TContextMenu, UtilModule } from "src/util";
-import { VIEWERMODULE_DARKTHEME } from "./constants";
 import { NehubaModule, NehubaViewerUnit } from "./nehuba";
 import { ThreeSurferModule } from "./threeSurfer";
 import { ViewerCmp } from "./viewerCmp/viewerCmp.component";
-import {UserAnnotationsModule} from "src/atlasComponents/userAnnotations";
-import {QuickTourModule} from "src/ui/quickTour/module";
+import { UserAnnotationsModule } from "src/atlasComponents/userAnnotations";
+import { QuickTourModule } from "src/ui/quickTour/module";
 import { INJ_ANNOT_TARGET } from "src/atlasComponents/userAnnotations/tools/type";
 import { NEHUBA_INSTANCE_INJTKN } from "./nehuba/util";
 import { map } from "rxjs/operators";
 import { TContextArg } from "./viewer.interface";
-import { ViewerStateBreadCrumbModule } from "./viewerStateBreadCrumb/module";
-import { KgRegionalFeatureModule } from "src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature";
-import { API_SERVICE_SET_VIEWER_HANDLE_TOKEN, AtlasViewerAPIServices, setViewerHandleFactory } from "src/atlasViewer/atlasViewer.apiService.service";
-import { ILoadMesh, LOAD_MESH_TOKEN } from "src/messaging/types";
 import { KeyFrameModule } from "src/keyframesModule/module";
 import { ViewerInternalStateSvc } from "./viewerInternalState.service";
-import { LayerBrowserModule } from "src/ui/layerbrowser";
+import { SAPIModule } from 'src/atlasComponents/sapi';
+import { NehubaVCtxToBbox } from "./pipes/nehubaVCtxToBbox.pipe";
+import { SapiViewsModule, SapiViewsUtilModule } from "src/atlasComponents/sapiViews";
+import { DialogModule } from "src/ui/dialogInfo/module";
+import { MouseoverModule } from "src/mouseoverModule";
+import { LogoContainer } from "src/ui/logoContainer/logoContainer.component";
+import { FloatingMouseContextualContainerDirective } from "src/util/directives/floatingMouseContextualContainer.directive";
+import { ShareModule } from "src/share";
 
 @NgModule({
   imports: [
@@ -36,34 +33,28 @@ import { LayerBrowserModule } from "src/ui/layerbrowser";
     NehubaModule,
     ThreeSurferModule,
     LayoutModule,
-    AtlasCmpUiSelectorsModule,
     AngularMaterialModule,
-    SplashUiModule,
     TopMenuModule,
-    ParcellationRegionModule,
     UtilModule,
-    AtlasCmpParcellationModule,
     ComponentsModule,
-    BSFeatureModule,
     UserAnnotationsModule,
     QuickTourModule,
     ContextMenuModule,
-    ViewerStateBreadCrumbModule,
-    KgRegionalFeatureModule,
     KeyFrameModule,
-    LayerBrowserModule,
+    SAPIModule,
+    SapiViewsModule,
+    SapiViewsUtilModule,
+    DialogModule,
+    MouseoverModule,
+    ShareModule,
   ],
   declarations: [
     ViewerCmp,
+    NehubaVCtxToBbox,
+    LogoContainer,
+    FloatingMouseContextualContainerDirective,
   ],
   providers: [
-    {
-      provide: BS_DARKTHEME,
-      useFactory: (obs$: Observable<boolean>) => obs$,
-      deps: [
-        VIEWERMODULE_DARKTHEME
-      ]
-    },
     {
       provide: INJ_ANNOT_TARGET,
       useFactory: (obs$: Observable<NehubaViewerUnit>) => {
@@ -85,21 +76,6 @@ import { LayerBrowserModule } from "src/ui/layerbrowser";
       },
       deps: [ ContextMenuService ]
     },
-    {
-      provide: API_SERVICE_SET_VIEWER_HANDLE_TOKEN,
-      useFactory: setViewerHandleFactory,
-      deps: [ AtlasViewerAPIServices ]
-    },
-    {
-      provide: LOAD_MESH_TOKEN,
-      useFactory: (apiService: AtlasViewerAPIServices) => {
-        return (loadMeshParam: ILoadMesh) => apiService.loadMesh$.next(loadMeshParam)
-      },
-      deps: [
-        AtlasViewerAPIServices
-      ]
-    },
-    AtlasViewerAPIServices,
     ViewerInternalStateSvc,
   ],
   exports: [
diff --git a/src/viewerModule/nehuba/annotation/effects.spec.ts b/src/viewerModule/nehuba/annotation/effects.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6d18fc4adfdfab0a6e1b45b939be6f95748fe134
--- /dev/null
+++ b/src/viewerModule/nehuba/annotation/effects.spec.ts
@@ -0,0 +1,60 @@
+import { TestBed } from "@angular/core/testing"
+import { provideMockActions } from "@ngrx/effects/testing"
+import { Action } from "@ngrx/store"
+import { MockStore, provideMockStore } from "@ngrx/store/testing"
+import { hot } from "jasmine-marbles"
+import { Observable } from "rxjs"
+import { annotation, atlasAppearance } from "src/state"
+import { NgAnnotationEffects } from "./effects"
+
+describe("effects.ts", () => {
+  describe("NgAnnotationEffects", () => {
+    let actions$: Observable<Action>
+    beforeEach(() => {
+      TestBed.configureTestingModule({
+        providers: [
+          provideMockStore(),
+          provideMockActions(() => actions$),
+          NgAnnotationEffects,
+        ]
+      })
+    })
+    describe("onAnnotationHideQuadrant", () => {
+      describe("> when space filtered annotation does not exist", () => {
+        it("> should setOctantRemoval true", () => {
+          const mockStore = TestBed.inject(MockStore)
+          mockStore.overrideSelector(annotation.selectors.spaceFilteredAnnotations, [])
+          const effect = TestBed.inject(NgAnnotationEffects)
+          expect(effect.onAnnotationHideQuadrant).toBeObservable(
+            hot('a', {
+              a: atlasAppearance.actions.setOctantRemoval({
+                flag: true
+              })
+            })
+          )
+        })
+      })
+      describe("> when space filtered annotation exist", () => {
+        it("> should setOctantRemoval false", () => {
+          const mockStore = TestBed.inject(MockStore)
+          mockStore.overrideSelector(annotation.selectors.spaceFilteredAnnotations, [{} as any])
+          const effect = TestBed.inject(NgAnnotationEffects)
+          expect(effect.onAnnotationHideQuadrant).toBeObservable(
+            hot('a', {
+              a: atlasAppearance.actions.setOctantRemoval({
+                flag: false
+              })
+            })
+          )
+        })
+      })
+
+      describe("> on switch of space filtered annotations length", () => {
+        it("> should emit accordingly")
+      })
+      describe("> on repeated emit of space filtered annotations length", () => {
+        it("> should only emit once")
+      })
+    })
+  })
+})
\ No newline at end of file
diff --git a/src/viewerModule/nehuba/annotation/effects.ts b/src/viewerModule/nehuba/annotation/effects.ts
new file mode 100644
index 0000000000000000000000000000000000000000..10c510db4a0133b41962613926b72ea7159884f4
--- /dev/null
+++ b/src/viewerModule/nehuba/annotation/effects.ts
@@ -0,0 +1,21 @@
+import { Injectable } from "@angular/core";
+import { createEffect } from "@ngrx/effects";
+import { select, Store } from "@ngrx/store";
+import { distinctUntilChanged, map } from "rxjs/operators";
+import { annotation, atlasAppearance } from "src/state"
+
+@Injectable()
+export class NgAnnotationEffects{
+  constructor(private store: Store){}
+
+  onAnnotationHideQuadrant = createEffect(() => this.store.pipe(
+    select(annotation.selectors.spaceFilteredAnnotations),
+    map(arr => arr.length > 0),
+    distinctUntilChanged(),
+    map(spaceFilteredAnnotationExists => {
+      return atlasAppearance.actions.setOctantRemoval({
+        flag: !spaceFilteredAnnotationExists
+      })
+    }),
+  ))
+}
diff --git a/src/viewerModule/nehuba/annotation/service.ts b/src/viewerModule/nehuba/annotation/service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fcc109e8f6cc6b81f561e48c42c8123f62107a3e
--- /dev/null
+++ b/src/viewerModule/nehuba/annotation/service.ts
@@ -0,0 +1,91 @@
+import { Injectable, OnDestroy } from "@angular/core";
+import { select, Store } from "@ngrx/store";
+import { Subscription } from "rxjs";
+import { filter, map, pairwise, startWith } from "rxjs/operators";
+import { AnnotationLayer, TNgAnnotationAABBox, TNgAnnotationLine, TNgAnnotationPoint } from "src/atlasComponents/annotations";
+import { annotation } from "src/state";
+
+
+@Injectable({
+  providedIn: 'root'
+})
+
+export class NgAnnotationService implements OnDestroy {
+
+  private subs: Subscription[]  = []
+  static ANNOTATION_LAYER_NAME = 'whale-annotation-layer'
+  static INIT_LAYERS: Set<string> = new Set<string>()
+  static COLOR_MAP = new Map<keyof typeof annotation.AnnotationColor, string>([
+    [annotation.AnnotationColor.WHITE, "#ffffff"],
+    [annotation.AnnotationColor.BLUE, "#00ff00"],
+    [annotation.AnnotationColor.RED, "#ff0000"],
+  ])
+
+  static GET_ANN_LAYER(ann: annotation.UnionAnnotation): AnnotationLayer {
+    const color = ann.color || annotation.AnnotationColor.WHITE
+    const layerEnum = annotation.AnnotationColor[color] || annotation.AnnotationColor.WHITE
+    const layerName = `${NgAnnotationService.ANNOTATION_LAYER_NAME}-${layerEnum}`
+
+    const annLayer = AnnotationLayer.Get(layerName, NgAnnotationService.COLOR_MAP.get(color) || "#ffffff")
+    NgAnnotationService.INIT_LAYERS.add(layerName)
+    return annLayer
+  }
+
+  ngOnDestroy(): void {
+    NgAnnotationService.INIT_LAYERS.forEach(layername => {
+      const layer = AnnotationLayer.Get(layername, '#ffffff')
+      layer.dispose()
+    })
+    NgAnnotationService.INIT_LAYERS.clear()
+    while(this.subs.length) this.subs.pop().unsubscribe()
+  }
+
+  constructor(
+    private store: Store
+  ){
+    
+    this.subs.push(
+      this.store.pipe(
+        select(annotation.selectors.spaceFilteredAnnotations)
+      ).pipe(
+        startWith<annotation.UnionAnnotation[]>([]),
+        pairwise(),
+        map(([prevAnnotations, currAnnotations]) => {
+          const prevAnnotationIds = new Set(prevAnnotations.map(ann => ann["@id"]))
+          const currAnnotationIds = new Set(currAnnotations.map(ann => ann["@id"]))
+          const newAnnotations = currAnnotations.filter(ann => !prevAnnotationIds.has(ann["@id"]))
+          const expiredAnnotations = prevAnnotations.filter(ann => !currAnnotationIds.has(ann["@id"]))
+          return {
+            newAnnotations,
+            expiredAnnotations
+          }
+        }),
+        filter(({ newAnnotations, expiredAnnotations }) => newAnnotations.length > 0 || expiredAnnotations.length > 0)
+      ).subscribe(({ newAnnotations, expiredAnnotations }) => {
+        
+        for (const ann of expiredAnnotations) {
+          const annLayer = NgAnnotationService.GET_ANN_LAYER(ann)
+          annLayer.removeAnnotation({
+            id: ann["@id"]
+          })
+        }
+        for (const ann of newAnnotations) {
+          let annotation: TNgAnnotationPoint | TNgAnnotationLine | TNgAnnotationAABBox
+          let annLayer: AnnotationLayer
+          if (!!ann['openminds']) {
+            annLayer = NgAnnotationService.GET_ANN_LAYER(ann)
+            annotation = {
+              id: ann["@id"],
+              description: ann.description,
+              type: 'point',
+              point: (ann as annotation.Annotation<'openminds'>).openminds.coordinates.map(coord => coord.value * 1e6) as [number, number, number]
+            } as TNgAnnotationPoint
+          }
+
+          if (annotation && annLayer) annLayer.addAnnotation(annotation)
+          else console.warn(`annotation or annotation layer was not initialized.`)
+        }
+      })
+    )
+  }
+}
diff --git a/src/viewerModule/nehuba/config.service/config.service.spec.ts b/src/viewerModule/nehuba/config.service/config.service.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/viewerModule/nehuba/config.service/index.ts b/src/viewerModule/nehuba/config.service/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1d0fd090589abf2a94209c68e8da7eff87c9b630
--- /dev/null
+++ b/src/viewerModule/nehuba/config.service/index.ts
@@ -0,0 +1,18 @@
+export {
+  NehubaConfig,
+  NgConfig,
+  NgConfigViewerState,
+  NgLayerSpec,
+  NgPrecompMeshSpec,
+  NgSegLayerSpec,
+} from "./type"
+export {
+  getParcNgLayers,
+  getTmplAuxNgLayer,
+  getTmplNgLayer,
+  getNgLayersFromVolumesATP,
+  getParcNgId,
+  getRegionLabelIndex,
+  getNehubaConfig,
+  defaultNehubaConfig,
+} from "./util"
diff --git a/src/viewerModule/nehuba/config.service/type.ts b/src/viewerModule/nehuba/config.service/type.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c8154dd44432e6efe3d7920977f5dd287c557726
--- /dev/null
+++ b/src/viewerModule/nehuba/config.service/type.ts
@@ -0,0 +1,115 @@
+
+export type RecursivePartial<T> = {
+  [K in keyof T]?: RecursivePartial<T[K]>
+}
+
+type Vec4 = number[]
+type Vec3 = number[]
+
+export type NgConfigViewerState = {
+  perspectiveOrientation: Vec4
+  perspectiveZoom: number
+  navigation: {
+    pose: {
+      position: {
+        voxelSize: Vec3
+        voxelCoordinates: Vec3
+      }
+      orientation: Vec4
+    }
+    zoomFactor: number
+  }
+}
+
+export type NgConfig = {
+  showDefaultAnnotations: boolean
+  layers: Record<string, NgLayerSpec>
+  gpuMemoryLimit: number
+} & NgConfigViewerState
+
+interface _NehubaConfig {
+  configName: string
+  globals: {
+    hideNullImageValues: boolean
+    useNehubaLayout: {
+      keepDefaultLayouts: boolean
+    }
+    useNehubaMeshLayer: boolean
+    rightClickWithCtrlGlobal: boolean
+    zoomWithoutCtrlGlobal: boolean
+    useCustomSegmentColors: boolean
+  }
+  zoomWithoutCtrl: boolean
+  hideNeuroglancerUI: boolean
+  rightClickWithCtrl: boolean
+  rotateAtViewCentre: boolean
+  enableMeshLoadingControl: boolean
+  zoomAtViewCentre: boolean
+  restrictUserNavigation: boolean
+  disableSegmentSelection: boolean
+  dataset: {
+    imageBackground: Vec4
+    initialNgState: NgConfig
+  }
+  layout: {
+    views: string
+    planarSlicesBackground: Vec4
+    useNehubaPerspective: {
+      enableShiftDrag: boolean
+      doNotRestrictUserNavigation: boolean
+      removePerspectiveSlicesBackground: {
+        mode: string
+        color: Vec4
+      }
+      perspectiveSlicesBackground: Vec4
+      perspectiveBackground: Vec4
+      fixedZoomPerspectiveSlices: {
+        sliceViewportWidth: number
+        sliceViewportHeight: number
+        sliceZoom: number
+        sliceViewportSizeMultiplier: number
+      }
+      mesh: {
+        removeOctant: Vec4
+        backFaceColor: Vec3
+        removeBasedOnNavigation: boolean
+        flipRemovedOctant: boolean
+        surfaceParcellation: boolean
+      }
+      centerToOrigin: boolean
+      drawSubstrates: {
+        color: Vec4
+      }
+      drawZoomLevels: {
+        cutOff: number
+        color: Vec4
+      }
+      restrictZoomLevel: {
+        minZoom: number
+        maxZoom: number
+      }
+      hideImages: boolean
+      waitForMesh: boolean
+    }
+  }
+}
+
+export type NehubaConfig = RecursivePartial<_NehubaConfig>
+
+export type NgLayerSpec = {
+  source: string
+  transform: number[][]
+  opacity?: number
+  visible?: boolean
+}
+
+export type NgPrecompMeshSpec = {
+  auxMeshes: {
+    name: string
+    labelIndicies: number[]
+  }[]
+} & NgLayerSpec
+
+export type NgSegLayerSpec = {
+  labelIndicies: number[]
+} & NgLayerSpec
diff --git a/src/viewerModule/nehuba/config.service/util.spec.ts b/src/viewerModule/nehuba/config.service/util.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7f5727e19755f4ffd0d59540c3ccc93806300fa3
--- /dev/null
+++ b/src/viewerModule/nehuba/config.service/util.spec.ts
@@ -0,0 +1,141 @@
+import { cvtNavigationObjToNehubaConfig } from './util'
+
+const currentNavigation = {
+  position: [4, 5, 6],
+  orientation: [0, 0, 0, 1],
+  perspectiveOrientation: [ 0, 0, 0, 1],
+  perspectiveZoom: 2e5,
+  zoom: 1e5
+}
+
+const defaultPerspectiveZoom = 1e6
+const defaultZoom = 1e6
+
+const defaultNavigationObject = {
+  orientation: [0, 0, 0, 1],
+  perspectiveOrientation: [0 , 0, 0, 1],
+  perspectiveZoom: defaultPerspectiveZoom,
+  zoom: defaultZoom,
+  position: [0, 0, 0],
+  positionReal: true
+}
+
+const defaultNehubaConfigObject = {
+  perspectiveOrientation: [0, 0, 0, 1],
+  perspectiveZoom: 1e6,
+  navigation: {
+    pose: {
+      position: {
+        voxelCoordinates: [0, 0, 0],
+        voxelSize: [1,1,1]
+      },
+      orientation: [0, 0, 0, 1],
+    },
+    zoomFactor: defaultZoom
+  }
+}
+
+const bigbrainNehubaConfig = {
+  "showDefaultAnnotations": false,
+  "layers": {
+  },
+  "navigation": {
+    "pose": {
+      "position": {
+        "voxelSize": [
+          21166.666015625,
+          20000,
+          21166.666015625
+        ],
+        "voxelCoordinates": [
+          -21.8844051361084,
+          16.288618087768555,
+          28.418994903564453
+        ]
+      }
+    },
+    "zoomFactor": 350000
+  },
+  "perspectiveOrientation": [
+    0.3140767216682434,
+    -0.7418519854545593,
+    0.4988985061645508,
+    -0.3195493221282959
+  ],
+  "perspectiveZoom": 1922235.5293810747
+}
+
+describe('> util.ts', () => {
+  
+  describe('> cvtNavigationObjToNehubaConfig', () => {
+    const validNavigationObj = currentNavigation
+    describe('> if inputs are malformed', () => {
+      describe('> if navigation object is malformed, uses navigation default object', () => {
+        it('> if navigation object is null', () => {
+          const v1 = cvtNavigationObjToNehubaConfig(null, bigbrainNehubaConfig)
+          const v2 = cvtNavigationObjToNehubaConfig(defaultNavigationObject, bigbrainNehubaConfig)
+          expect(v1).toEqual(v2)
+        })
+        it('> if navigation object is undefined', () => {
+          const v1 = cvtNavigationObjToNehubaConfig(undefined, bigbrainNehubaConfig)
+          const v2 = cvtNavigationObjToNehubaConfig(defaultNavigationObject, bigbrainNehubaConfig)
+          expect(v1).toEqual(v2)
+        })
+
+        it('> if navigation object is otherwise malformed', () => {
+          const v1 = cvtNavigationObjToNehubaConfig({foo: 'bar'} as any, bigbrainNehubaConfig)
+          const v2 = cvtNavigationObjToNehubaConfig(defaultNavigationObject, bigbrainNehubaConfig)
+          expect(v1).toEqual(v2)
+
+          const v3 = cvtNavigationObjToNehubaConfig({} as any, bigbrainNehubaConfig)
+          const v4 = cvtNavigationObjToNehubaConfig(defaultNavigationObject, bigbrainNehubaConfig)
+          expect(v3).toEqual(v4)
+        })
+      })
+
+      describe('> if nehubaConfig object is malformed, use default nehubaConfig obj', () => {
+        it('> if nehubaConfig is null', () => {
+          const v1 = cvtNavigationObjToNehubaConfig(validNavigationObj, null)
+          const v2 = cvtNavigationObjToNehubaConfig(validNavigationObj, defaultNehubaConfigObject)
+          expect(v1).toEqual(v2)
+        })
+
+        it('> if nehubaConfig is undefined', () => {
+          const v1 = cvtNavigationObjToNehubaConfig(validNavigationObj, undefined)
+          const v2 = cvtNavigationObjToNehubaConfig(validNavigationObj, defaultNehubaConfigObject)
+          expect(v1).toEqual(v2)
+        })
+
+        it('> if nehubaConfig is otherwise malformed', () => {
+          const v1 = cvtNavigationObjToNehubaConfig(validNavigationObj, {})
+          const v2 = cvtNavigationObjToNehubaConfig(validNavigationObj, defaultNehubaConfigObject)
+          expect(v1).toEqual(v2)
+
+          const v3 = cvtNavigationObjToNehubaConfig(validNavigationObj, {foo: 'bar'} as any)
+          const v4 = cvtNavigationObjToNehubaConfig(validNavigationObj, defaultNehubaConfigObject)
+          expect(v3).toEqual(v4)
+        })
+      })
+    })
+    it('> converts navigation object and reference nehuba config object to navigation object', () => {
+      const convertedVal = cvtNavigationObjToNehubaConfig(validNavigationObj, bigbrainNehubaConfig)
+      const { perspectiveOrientation, orientation, zoom, perspectiveZoom, position } = validNavigationObj
+      
+      expect(convertedVal).toEqual({
+        navigation: {
+          pose: {
+            position: {
+              voxelSize: bigbrainNehubaConfig.navigation.pose.position.voxelSize,
+              voxelCoordinates: [0, 1, 2].map(idx => position[idx] / bigbrainNehubaConfig.navigation.pose.position.voxelSize[idx])
+            },
+            orientation
+          },
+          zoomFactor: zoom
+        },
+        perspectiveOrientation: perspectiveOrientation,
+        perspectiveZoom: perspectiveZoom
+      })
+    })
+  })
+
+})
diff --git a/src/viewerModule/nehuba/config.service/util.ts b/src/viewerModule/nehuba/config.service/util.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7233d0f26bb2bda42bfe3b0141c9c9b64a377c77
--- /dev/null
+++ b/src/viewerModule/nehuba/config.service/util.ts
@@ -0,0 +1,485 @@
+import { SapiParcellationModel, SapiSpaceModel, SapiAtlasModel, SapiRegionModel } from 'src/atlasComponents/sapi'
+import { SapiVolumeModel, IDS } from 'src/atlasComponents/sapi'
+import { atlasSelection } from 'src/state'
+import { MultiDimMap } from 'src/util/fn'
+import { ParcVolumeSpec } from "../store/util"
+import {
+  NehubaConfig,
+  NgConfig,
+  RecursivePartial,
+  NgLayerSpec,
+  NgPrecompMeshSpec,
+  NgSegLayerSpec,
+} from "./type"
+
+// fsaverage uses threesurfer, which, whilst do not use ngId, uses 'left' and 'right' as keys 
+const fsAverageKeyVal = {
+  [IDS.PARCELLATION.JBA29]: {
+    "left hemisphere": "left",
+    "right hemisphere": "right"
+  }
+}
+
+/**
+ * in order to maintain backwards compat with url encoding of selected regions
+ * TODO setup a sentry to catch if these are ever used. if not, retire the hard coding 
+ */
+const BACKCOMAP_KEY_DICT = {
+
+  // human multi level
+  'juelich/iav/atlas/v1.0.0/1': {
+    // icbm152
+    [IDS.TEMPLATES.MNI152]: {
+      // julich brain v2.6
+      'minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579-26': {
+        'left hemisphere': 'MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE',
+        'right hemisphere': 'MNI152_V25_RIGHT_NG_SPLIT_HEMISPHERE'
+      },
+      // bundle hcp
+      // even though hcp, long/short bundle, and difumo has no hemisphere distinctions, the way siibra-python parses the region,
+      // and thus attributes left/right hemisphere, still results in some regions being parsed as left/right hemisphere
+      "juelich/iav/atlas/v1.0.0/79cbeaa4ee96d5d3dfe2876e9f74b3dc3d3ffb84304fb9b965b1776563a1069c": {
+        "whole brain": "superficial-white-bundle-HCP",
+        "left hemisphere": "superficial-white-bundle-HCP",
+        "right hemisphere": "superficial-white-bundle-HCP"
+      },
+      // julich brain v1.18
+      "minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579": {
+        "left hemisphere": "jubrain mni152 v18 left",
+        "right hemisphere": "jubrain mni152 v18 right",
+      },
+      // long bundle
+      "juelich/iav/atlas/v1.0.0/5": {
+        "whole brain": "fibre bundle long",
+        "left hemisphere": "fibre bundle long",
+        "right hemisphere": "fibre bundle long",
+      },
+      // bundle short
+      "juelich/iav/atlas/v1.0.0/6": {
+        "whole brain": "fibre bundle short",
+        "left hemisphere": "fibre bundle short",
+        "right hemisphere": "fibre bundle short",
+      },
+      // difumo 64
+      "minds/core/parcellationatlas/v1.0.0/d80fbab2-ce7f-4901-a3a2-3c8ef8a3b721": {
+        "whole brain": "DiFuMo Atlas (64 dimensions)",
+        "left hemisphere": "DiFuMo Atlas (64 dimensions)",
+        "right hemisphere": "DiFuMo Atlas (64 dimensions)",
+      },
+      "minds/core/parcellationatlas/v1.0.0/73f41e04-b7ee-4301-a828-4b298ad05ab8": {
+        "whole brain": "DiFuMo Atlas (128 dimensions)",
+        "left hemisphere": "DiFuMo Atlas (128 dimensions)",
+        "right hemisphere": "DiFuMo Atlas (128 dimensions)",
+      },
+      "minds/core/parcellationatlas/v1.0.0/141d510f-0342-4f94-ace7-c97d5f160235": {
+        "whole brain": "DiFuMo Atlas (256 dimensions)",
+        "left hemisphere": "DiFuMo Atlas (256 dimensions)",
+        "right hemisphere": "DiFuMo Atlas (256 dimensions)",
+      },
+      "minds/core/parcellationatlas/v1.0.0/63b5794f-79a4-4464-8dc1-b32e170f3d16": {
+        "whole brain": "DiFuMo Atlas (512 dimensions)",
+        "left hemisphere": "DiFuMo Atlas (512 dimensions)",
+        "right hemisphere": "DiFuMo Atlas (512 dimensions)",
+      },
+      "minds/core/parcellationatlas/v1.0.0/12fca5c5-b02c-46ce-ab9f-f12babf4c7e1": {
+        "whole brain": "DiFuMo Atlas (1024 dimensions)",
+        "left hemisphere": "DiFuMo Atlas (1024 dimensions)",
+        "right hemisphere": "DiFuMo Atlas (1024 dimensions)",
+      },
+    },
+    // colin 27
+    "minds/core/referencespace/v1.0.0/7f39f7be-445b-47c0-9791-e971c0b6d992": {
+      "minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579-26": {
+        "left hemisphere": "COLIN_V25_LEFT_NG_SPLIT_HEMISPHERE",
+        "right hemisphere": "COLIN_V25_RIGHT_NG_SPLIT_HEMISPHERE",
+      },
+      "minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579": {
+        "left hemisphere": "jubrain colin v18 left",
+        "right hemisphere": "jubrain colin v18 right",
+      }
+    },
+    // big brain
+    "minds/core/referencespace/v1.0.0/a1655b99-82f1-420f-a3c2-fe80fd4c8588": {
+      "minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579-26": {
+
+      },
+      // isocortex
+      "juelich/iav/atlas/v1.0.0/4": {
+        "whole brain": " tissue type: "
+      },
+      // cortical layers
+      "juelich/iav/atlas/v1.0.0/3": {
+        "whole brain": "cortical layers"
+      },
+    },
+
+    // fsaverage
+    "minds/core/referencespace/v1.0.0/tmp-fsaverage": fsAverageKeyVal,
+  },
+  // allen mouse
+  'juelich/iav/atlas/v1.0.0/2': {
+    // ccf v3
+    "minds/core/referencespace/v1.0.0/265d32a0-3d84-40a5-926f-bf89f68212b9": {
+      // ccf v3 2017
+      "minds/core/parcellationatlas/v1.0.0/05655b58-3b6f-49db-b285-64b5a0276f83": {
+        "whole brain": "v3_2017",
+        "left hemisphere": "v3_2017",
+        "right hemisphere": "v3_2017"
+      },
+      // ccf v3 2015,
+      "minds/core/parcellationatlas/v1.0.0/39a1384b-8413-4d27-af8d-22432225401f": {
+        "whole brain": "atlas",
+        "left hemisphere": "atlas",
+        "right hemisphere": "atlas"
+      }
+    }
+  },
+  // waxholm
+  "minds/core/parcellationatlas/v1.0.0/522b368e-49a3-49fa-88d3-0870a307974a": {
+    "minds/core/referencespace/v1.0.0/d5717c4a-0fa1-46e6-918c-b8003069ade8": {
+      // v1.01
+      "minds/core/parcellationatlas/v1.0.0/11017b35-7056-4593-baad-3934d211daba": {
+        "whole brain": "v1_01",
+        "left hemisphere": "v1_01",
+        "right hemisphere": "v1_01"
+      },
+      // v2
+      "minds/core/parcellationatlas/v1.0.0/2449a7f0-6dd0-4b5a-8f1e-aec0db03679d": {
+        "whole brain": "v2",
+        "left hemisphere": "v2",
+        "right hemisphere": "v2"
+      },
+      // v3
+      "minds/core/parcellationatlas/v1.0.0/ebb923ba-b4d5-4b82-8088-fa9215c2e1fe": {
+        "whole brain": "v3",
+        "left hemisphere": "v3",
+        "right hemisphere": "v3"
+      }
+    }
+  }
+}
+
+
+export function getTmplNgLayer(atlas: SapiAtlasModel, tmpl: SapiSpaceModel, spaceVolumes: SapiVolumeModel[]): Record<string, NgLayerSpec>{
+  if (!atlas || !tmpl) return {}
+  const ngId = `_${MultiDimMap.GetKey(atlas["@id"], tmpl["@id"], "tmplImage")}`
+  const tmplImage = spaceVolumes.find(v => "neuroglancer/precomputed" in v.data.detail)
+  if (!tmplImage) return {}
+  return {
+    [ngId]: {
+      source: `precomputed://${tmplImage.data.url.replace(/^precomputed:\/\//, '')}`,
+      ...tmplImage.data.detail["neuroglancer/precomputed"] as NgLayerSpec
+    }
+  }
+}
+
+export function getTmplAuxNgLayer(atlas: SapiAtlasModel, tmpl: SapiSpaceModel, spaceVolumes: SapiVolumeModel[]): Record<string, NgPrecompMeshSpec>{
+  if (!atlas || !tmpl) return {}
+  const ngId = `_${MultiDimMap.GetKey(atlas["@id"], tmpl["@id"], "auxLayer")}`
+  const tmplImage = spaceVolumes.find(v => "neuroglancer/precompmesh" in v.data.detail)
+  if (!tmplImage) return {}
+  return {
+    [ngId]: {
+      source: `precompmesh://${tmplImage.data.url.replace(/^precompmesh:\/\//, '')}`,
+      ...tmplImage.data.detail["neuroglancer/precompmesh"] as NgPrecompMeshSpec
+    }
+  }
+}
+
+export function getParcNgId(atlas: SapiAtlasModel, tmpl: SapiSpaceModel, parc: SapiParcellationModel, region: SapiRegionModel): string {
+
+  let laterality: string = "whole brain"
+  if (region.name.indexOf("left") >= 0) laterality = "left hemisphere"
+  if (region.name.indexOf("right") >= 0) laterality = "right hemisphere"
+
+  /**
+   * for JBA29 in big brain, there exist several volumes. (e.g. v1, v2, v5, interpolated, etc)
+   */
+  if (tmpl['@id'] === IDS.TEMPLATES.BIG_BRAIN && parc['@id'] === IDS.PARCELLATION.JBA29) {
+    laterality = region.hasAnnotation.visualizedIn?.['@id']
+  }
+
+  if (!laterality) {
+    return null
+  }
+
+  let ngId = BACKCOMAP_KEY_DICT[atlas["@id"]]?.[tmpl["@id"]]?.[parc["@id"]]?.[laterality]
+  if (!ngId) {
+    ngId = `_${MultiDimMap.GetKey(atlas["@id"], tmpl["@id"], parc["@id"], laterality)}`
+  }
+  return ngId
+}
+
+const labelIdxRegex = /siibra_python_ng_precomputed_labelindex:\/\/(.*?)#([0-9]+)$/
+
+export function getRegionLabelIndex(_atlas: SapiAtlasModel, _tmpl: SapiSpaceModel, _parc: SapiParcellationModel, region: SapiRegionModel): number {
+  const overwriteLabelIndex = region.hasAnnotation.inspiredBy.map(({ "@id": id }) => labelIdxRegex.exec(id)).filter(v => !!v)
+  if (overwriteLabelIndex.length > 0) {
+    const match = overwriteLabelIndex[0]
+    const volumeId = match[1]
+    const labelIndex = match[2]
+    const _labelIndex = Number(labelIndex)
+    if (!isNaN(_labelIndex)) return _labelIndex
+  }
+  const lblIdx = Number(region?.hasAnnotation?.internalIdentifier)
+  if (isNaN(lblIdx)) return null
+  return lblIdx
+}
+
+export function getParcNgLayers(atlas: SapiAtlasModel, tmpl: SapiSpaceModel, parc: SapiParcellationModel, parcVolumes: { volume: SapiVolumeModel, volumeMetadata: ParcVolumeSpec }[]): Record<string, NgSegLayerSpec>{
+  const returnVal: Record<string, NgSegLayerSpec> = {}
+  for (const parcVol of parcVolumes) {
+    if ("spy/volume/neuroglancer/precomputed" !== parcVol.volume['@type']) continue
+    const { volume, volumeMetadata } = parcVol
+    const { regions } = volumeMetadata
+    if (regions.length === 0) {
+      console.warn(`parc volume with no associated region`)
+      continue
+    }
+    const ngId = getParcNgId(atlas, tmpl, parc, regions[0].region)
+
+    returnVal[ngId] = {
+      source: `precomputed://${volume.data.url.replace(/^precomputed:\/\//, '')}`,
+      transform: (volume.data.detail["neuroglancer/precomputed"] as any).transform,
+      labelIndicies: regions.map(v => v.labelIndex)
+    }
+  }
+  return returnVal
+}
+
+type CongregatedVolume = {
+  tmplVolumes: SapiVolumeModel[]
+  tmplAuxMeshVolumes: SapiVolumeModel[]
+  parcVolumes: { volume: SapiVolumeModel, volumeMetadata: ParcVolumeSpec}[]
+}
+
+export const getNgLayersFromVolumesATP = (volumes: CongregatedVolume, ATP: { atlas: SapiAtlasModel, template: SapiSpaceModel, parcellation: SapiParcellationModel }): {
+  tmplNgLayers: Record<string, NgLayerSpec>
+  tmplAuxNgLayers: Record<string, NgPrecompMeshSpec>
+  parcNgLayers: Record<string, NgSegLayerSpec>
+} => {
+  
+  const { tmplVolumes, tmplAuxMeshVolumes, parcVolumes } = volumes
+  const { atlas, template, parcellation } = ATP
+  return {
+    tmplNgLayers: getTmplNgLayer(atlas, template, tmplVolumes),
+    tmplAuxNgLayers: getTmplAuxNgLayer(atlas, template, tmplAuxMeshVolumes),
+    parcNgLayers: getParcNgLayers(atlas, template, parcellation, parcVolumes)
+  }
+}
+
+export const defaultNehubaConfig: NehubaConfig = {
+  "configName": "",
+  "globals": {
+    "hideNullImageValues": true,
+    "useNehubaLayout": {
+      "keepDefaultLayouts": false
+    },
+    "useNehubaMeshLayer": true,
+    "rightClickWithCtrlGlobal": false,
+    "zoomWithoutCtrlGlobal": false,
+    "useCustomSegmentColors": true
+  },
+  "zoomWithoutCtrl": true,
+  "hideNeuroglancerUI": true,
+  "rightClickWithCtrl": true,
+  "rotateAtViewCentre": true,
+  "enableMeshLoadingControl": true,
+  "zoomAtViewCentre": true,
+  "restrictUserNavigation": true,
+  "disableSegmentSelection": false,
+  "dataset": {
+    "imageBackground": [
+      1,
+      1,
+      1,
+      1
+    ],
+    "initialNgState": {
+      "showDefaultAnnotations": false,
+      "layers": {},
+    }
+  },
+  "layout": {
+    "views": "hbp-neuro",
+    "planarSlicesBackground": [
+      1,
+      1,
+      1,
+      1
+    ],
+    "useNehubaPerspective": {
+      "enableShiftDrag": false,
+      "doNotRestrictUserNavigation": false,
+      "perspectiveSlicesBackground": [
+        1,
+        1,
+        1,
+        1
+      ],
+      "perspectiveBackground": [
+        1,
+        1,
+        1,
+        1
+      ],
+      "mesh": {
+        "backFaceColor": [
+          1,
+          1,
+          1,
+          1
+        ],
+        "removeBasedOnNavigation": true,
+        "flipRemovedOctant": true
+      },
+      "hideImages": false,
+      "waitForMesh": false,
+    }
+  }
+}
+
+export const spaceMiscInfoMap = new Map([
+  [IDS.TEMPLATES.BIG_BRAIN, {
+    name: 'bigbrain',
+    scale: 1,
+  }],
+  [IDS.TEMPLATES.MNI152, {
+    name: 'icbm2009c',
+    scale: 1,
+  }],
+  ['minds/core/referencespace/v1.0.0/7f39f7be-445b-47c0-9791-e971c0b6d992', {
+    name: 'colin27',
+    scale: 1,
+  }],
+  ['minds/core/referencespace/v1.0.0/265d32a0-3d84-40a5-926f-bf89f68212b9', {
+    name: 'allen-mouse',
+    scale: 0.1,
+  }],
+  ['minds/core/referencespace/v1.0.0/d5717c4a-0fa1-46e6-918c-b8003069ade8', {
+    name: 'waxholm',
+    scale: 0.1,
+  }],
+])
+
+export function getNehubaConfig(space: SapiSpaceModel): NehubaConfig {
+
+  const darkTheme = space["@id"] !== "minds/core/referencespace/v1.0.0/a1655b99-82f1-420f-a3c2-fe80fd4c8588"
+  const { scale } = spaceMiscInfoMap.get(space["@id"]) || { scale: 1 }
+  const backgrd = darkTheme
+    ? [0,0,0,1]
+    : [1,1,1,1]
+
+  const rmPsp = darkTheme
+    ? {"mode":"<","color":[0.1,0.1,0.1,1]}
+    :{"color":[1,1,1,1],"mode":"=="}
+  const drawSubstrates = darkTheme
+    ? {"color":[0.5,0.5,1,0.2]}
+    : {"color":[0,0,0.5,0.15]}
+  const drawZoomLevels = darkTheme
+    ? {"cutOff":150000 * scale }
+    : {"cutOff":200000 * scale,"color":[0.5,0,0,0.15] }
+
+  // enable surface parcellation
+  // otherwise, on segmentation selection, the unselected meshes will also be invisible
+  const surfaceParcellation = space["@id"] === 'minds/core/referencespace/v1.0.0/7f39f7be-445b-47c0-9791-e971c0b6d992'
+  return {
+    "configName": "",
+    "globals": {
+      "hideNullImageValues": true,
+      "useNehubaLayout": {
+        "keepDefaultLayouts": false
+      },
+      "useNehubaMeshLayer": true,
+      "rightClickWithCtrlGlobal": false,
+      "zoomWithoutCtrlGlobal": false,
+      "useCustomSegmentColors": true
+    },
+    "zoomWithoutCtrl": true,
+    "hideNeuroglancerUI": true,
+    "rightClickWithCtrl": true,
+    "rotateAtViewCentre": true,
+    "enableMeshLoadingControl": true,
+    "zoomAtViewCentre": true,
+    // "restrictUserNavigation": true,
+    "dataset": {
+      "imageBackground": backgrd,
+      "initialNgState": {
+        "showDefaultAnnotations": false,
+        "layers": {},
+        "navigation": {
+          "zoomFactor": 350000 * scale,
+        },
+        "perspectiveOrientation": [
+          0.3140767216682434,
+          -0.7418519854545593,
+          0.4988985061645508,
+          -0.3195493221282959
+        ],
+        "perspectiveZoom": 1922235.5293810747 * scale
+      }
+    },
+    "layout": {
+      "useNehubaPerspective": {
+        "perspectiveSlicesBackground": backgrd,
+        "removePerspectiveSlicesBackground": rmPsp,
+        "perspectiveBackground": backgrd,
+        "fixedZoomPerspectiveSlices": {
+          "sliceViewportWidth": 300,
+          "sliceViewportHeight": 300,
+          "sliceZoom": 563818.3562426177 * scale,
+          "sliceViewportSizeMultiplier": 2
+        },
+        "mesh": {
+          "backFaceColor": backgrd,
+          "removeBasedOnNavigation": true,
+          "flipRemovedOctant": true,
+          surfaceParcellation
+        },
+        "centerToOrigin": true,
+        "drawSubstrates": drawSubstrates,
+        "drawZoomLevels": drawZoomLevels,
+        "restrictZoomLevel": {
+          "minZoom": 1200000 * scale,
+          "maxZoom": 3500000 * scale
+        }
+      }
+    }
+  }
+}
+
+
+export function cvtNavigationObjToNehubaConfig(navigationObj: atlasSelection.AtlasSelectionState['navigation'], nehubaConfigObj: RecursivePartial<NgConfig>): RecursivePartial<NgConfig>{
+  const {
+    orientation = [0, 0, 0, 1],
+    perspectiveOrientation = [0, 0, 0, 1],
+    perspectiveZoom = 1e6,
+    zoom = 1e6,
+    position = [0, 0, 0],
+  } = navigationObj || {}
+
+  const voxelSize = (() => {
+    const {
+      navigation = {}
+    } = nehubaConfigObj || {}
+    const { pose = {} } = navigation
+    const { position = {} } = pose
+    const { voxelSize = [1, 1, 1] } = position
+    return voxelSize
+  })()
+
+  return {
+    perspectiveOrientation,
+    perspectiveZoom,
+    navigation: {
+      pose: {
+        position: {
+          voxelCoordinates: [0, 1, 2].map(idx => position[idx] / voxelSize[idx]),
+          voxelSize
+        },
+        orientation,
+      },
+      zoomFactor: zoom
+    }
+  }
+}
diff --git a/src/viewerModule/nehuba/constants.ts b/src/viewerModule/nehuba/constants.ts
index bbd36606525015fcd2f8e6315a061ea2a7f56a36..ca5ae4be810af998f93318a6348f751555fabff3 100644
--- a/src/viewerModule/nehuba/constants.ts
+++ b/src/viewerModule/nehuba/constants.ts
@@ -4,17 +4,6 @@ import { Observable } from 'rxjs'
 export { getNgIds } from 'src/util/fn'
 export const NEHUBA_VIEWER_FEATURE_KEY = 'ngViewerFeature'
 
-export interface INgLayerInterface {
-  name: string // displayName
-  source: string
-  mixability: string // base | mixable | nonmixable
-  annotation?: string //
-  id?: string // unique identifier
-  visible?: boolean
-  shader?: string
-  transform?: any
-}
-
 export interface IRegion {
   [key: string]: any
   ngId: string
@@ -75,3 +64,5 @@ export interface IMeshesToLoad {
 }
 
 export const SET_MESHES_TO_LOAD = new InjectionToken<Observable<IMeshesToLoad>>('SET_MESHES_TO_LOAD')
+
+export const PMAP_LAYER_NAME = 'regional-pmap'
diff --git a/src/viewerModule/nehuba/layerCtrl.service/index.ts b/src/viewerModule/nehuba/layerCtrl.service/index.ts
index 05cbb34fe0a5803c935749c2c45e6345bb467746..09065a46f416dd4373e98395f4cf985715718cce 100644
--- a/src/viewerModule/nehuba/layerCtrl.service/index.ts
+++ b/src/viewerModule/nehuba/layerCtrl.service/index.ts
@@ -7,5 +7,5 @@ export {
   SET_COLORMAP_OBS,
   SET_LAYER_VISIBILITY,
   getRgb,
-  INgLayerInterface,
+  
 } from './layerCtrl.util'
diff --git a/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.effects.ts b/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.effects.ts
new file mode 100644
index 0000000000000000000000000000000000000000..579f7fb79ab3e65547cc420391f2097587eeff18
--- /dev/null
+++ b/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.effects.ts
@@ -0,0 +1,328 @@
+import { Injectable } from "@angular/core";
+import { createEffect } from "@ngrx/effects";
+import { select, Store } from "@ngrx/store";
+import { forkJoin, of } from "rxjs";
+import { mapTo, switchMap, withLatestFrom, filter, catchError, map, debounceTime, shareReplay, distinctUntilChanged, startWith, pairwise } from "rxjs/operators";
+import { SAPI, SapiAtlasModel, SapiFeatureModel, SapiParcellationModel, SapiSpaceModel, SapiRegionModel } from "src/atlasComponents/sapi";
+import { SapiVOIDataResponse, SapiVolumeModel } from "src/atlasComponents/sapi/type";
+import { atlasAppearance, atlasSelection, userInteraction } from "src/state";
+import { arrayEqual } from "src/util/array";
+import { EnumColorMapName } from "src/util/colorMaps";
+import { getShader } from "src/util/constants";
+import { getNgLayersFromVolumesATP, getRegionLabelIndex } from "../config.service";
+import { ParcVolumeSpec } from "../store/util";
+import { PMAP_LAYER_NAME } from "../constants";
+
+@Injectable()
+export class LayerCtrlEffects {
+  onRegionSelectClearPmapLayer = createEffect(() => this.store.pipe(
+    select(atlasSelection.selectors.selectedRegions),
+    distinctUntilChanged(
+      arrayEqual((o, n) => o["@id"] === n["@id"])
+    ),
+    mapTo(
+      atlasAppearance.actions.removeCustomLayer({
+        id: PMAP_LAYER_NAME
+      })
+    )
+  ))
+
+  onRegionSelectShowNewPmapLayer = createEffect(() => this.store.pipe(
+    select(atlasSelection.selectors.selectedRegions),
+    filter(regions => regions.length > 0),
+    withLatestFrom(
+      this.store.pipe(
+        atlasSelection.fromRootStore.distinctATP()
+      )
+    ),
+    switchMap(([ regions, { atlas, parcellation, template } ]) => {
+      const sapiRegion = this.sapi.getRegion(atlas["@id"], parcellation["@id"], regions[0].name)
+      return sapiRegion.getMapInfo(template["@id"]).pipe(
+        map(val => 
+          atlasAppearance.actions.addCustomLayer({
+            customLayer: {
+              clType: "customlayer/nglayer",
+              id: PMAP_LAYER_NAME,
+              source: `nifti://${sapiRegion.getMapUrl(template["@id"])}`,
+              shader: getShader({
+                colormap: EnumColorMapName.VIRIDIS,
+                highThreshold: val.max,
+                lowThreshold: val.min,
+                removeBg: true,
+              })
+            }
+          })
+        ),
+        catchError(() => of(
+          atlasAppearance.actions.removeCustomLayer({
+            id: PMAP_LAYER_NAME
+          })
+        ))
+      )
+    }),
+  ))
+
+  onATP$ = this.store.pipe(
+    atlasSelection.fromRootStore.distinctATP(),
+    map(val => val as { atlas: SapiAtlasModel, parcellation: SapiParcellationModel, template: SapiSpaceModel })
+  )
+
+  onShownFeature = createEffect(() => this.store.pipe(
+    select(userInteraction.selectors.selectedFeature),
+    startWith(null as SapiFeatureModel),
+    pairwise(),
+    map(([ prev, curr ]) => {
+      const removeLayers: atlasAppearance.NgLayerCustomLayer[] = []
+      const addLayers: atlasAppearance.NgLayerCustomLayer[] = []
+      if (prev?.["@type"] === "siibra/features/voi") {
+        removeLayers.push(
+          ...(prev as SapiVOIDataResponse).volumes.map(v => {
+            return {
+              id: v.metadata.fullName,
+              clType: "customlayer/nglayer",
+              source: v.data.url,
+              transform: v.data.detail['neuroglancer/precomputed']['transform'],
+              opacity: 1.0,
+              visible: true,
+              shader: v.data.detail['neuroglancer/precomputed']['shader'] || getShader()
+            } as atlasAppearance.NgLayerCustomLayer
+          })
+        )
+      }
+      if (curr?.["@type"] === "siibra/features/voi") {
+        addLayers.push(
+          ...(curr as SapiVOIDataResponse).volumes.map(v => {
+            return {
+              id: v.metadata.fullName,
+              clType: "customlayer/nglayer",
+              source: `precomputed://${v.data.url}`,
+              transform: v.data.detail['neuroglancer/precomputed']['transform'],
+              opacity: v.data.detail['neuroglancer/precomputed']['opacity'] || 1.0,
+              visible: true,
+              shader: v.data.detail['neuroglancer/precomputed']['shader'] || getShader()
+            } as atlasAppearance.NgLayerCustomLayer
+          })
+        )
+      }
+      return { removeLayers, addLayers }
+    }),
+    filter(({ removeLayers, addLayers }) => removeLayers.length !== 0 || addLayers.length !== 0),
+    switchMap(({ removeLayers, addLayers }) => of(...[
+      ...removeLayers.map(
+        l => atlasAppearance.actions.removeCustomLayer({ id: l.id })
+      ),
+      ...addLayers.map(
+        l => atlasAppearance.actions.addCustomLayer({ customLayer: l })
+      )
+    ]))
+  ))
+
+  onATPClearBaseLayers = createEffect(() => this.onATP$.pipe(
+    withLatestFrom(
+      this.store.pipe(
+        select(atlasAppearance.selectors.customLayers),
+        map(
+          cl => cl.filter(layer =>
+            layer.clType === "baselayer/nglayer"
+            || layer.clType === "customlayer/nglayer"
+          )
+        )
+      )
+    ),
+    switchMap(([_, layers]) => 
+      of(
+        ...layers.map(layer => 
+          atlasAppearance.actions.removeCustomLayer({
+            id: layer.id
+          })  
+        )
+      )
+    )
+  ))
+
+  private getNgLayers(atlas: SapiAtlasModel, parcellation: SapiParcellationModel, template: SapiSpaceModel){
+
+    if (!!parcellation && !template) {
+      throw new Error(`parcellation defined, but template not defined!`)
+    }
+    
+    /**
+     * some labelled maps (such as julich brain in big brain) do not have the volumes defined on the parcellation level.
+     * While we have the URLs of these volumes (the method we use is also kind of hacky), and in theory, we could construct a volume object directly
+     * It is probably better to fetch the correct volume object to begin with
+     */
+    const parcVolumes$ = !parcellation
+      ? of([] as {volume: SapiVolumeModel, volumeMetadata: ParcVolumeSpec}[])
+      : forkJoin([
+        this.sapi.getParcellation(atlas["@id"], parcellation["@id"]).getRegions(template["@id"]).pipe(
+          map(regions => {
+
+            const volumeIdToRegionMap = new Map<string, {
+              labelIndex: number
+              region: SapiRegionModel
+            }[]>()
+
+            for (const r of regions) {
+              const volumeId = r?.hasAnnotation?.visualizedIn?.["@id"]
+              if (!volumeId) continue
+
+              const labelIndex = getRegionLabelIndex(atlas, template, parcellation, r)
+              if (!labelIndex) continue
+
+              if (!volumeIdToRegionMap.has(volumeId)) {
+                volumeIdToRegionMap.set(volumeId, [])
+              }
+              volumeIdToRegionMap.get(volumeId).push({
+                labelIndex,
+                region: r
+              })
+            }
+            return volumeIdToRegionMap
+          })
+        ),
+        this.sapi.getParcellation(atlas["@id"], parcellation["@id"]).getVolumes()
+      ]).pipe(
+        switchMap(([ volumeIdToRegionMap, volumes ]) => {
+          const missingVolumeIds = Array.from(volumeIdToRegionMap.keys()).filter(id => volumes.map(v => v["@id"]).indexOf(id) < 0)
+
+          const volumesFromParc: {volume: SapiVolumeModel, volumeMetadata: ParcVolumeSpec}[] = volumes.map(
+            volume => {
+              const found = volumeIdToRegionMap.get(volume["@id"])
+              if (!found) return null
+
+              try {
+
+                const volumeMetadata: ParcVolumeSpec = {
+                  regions: found,
+                  parcellation,
+                  volumeSrc: volume.data.url
+                }
+                return {
+                  volume,
+                  volumeMetadata,
+                }
+              } catch (e) {
+                console.error(e)
+                return null
+              }
+            }
+          ).filter(v => v?.volumeMetadata?.regions)
+
+          if (missingVolumeIds.length === 0) return of([...volumesFromParc])
+          return forkJoin(
+            missingVolumeIds.map(missingId => {
+              if (!volumeIdToRegionMap.has(missingId)) {
+                console.warn(`volumeIdToRegionMap does not have volume with id ${missingId}`)
+                return of(null as SapiVolumeModel)
+              }
+              const { region } = volumeIdToRegionMap.get(missingId)[0]
+              return this.sapi.getRegion(atlas["@id"], parcellation["@id"], region.name).getVolumeInstance(missingId).pipe(
+                catchError((err, obs) => of(null as SapiVolumeModel))
+              )
+            })
+          ).pipe(
+            map((missingVolumes: SapiVolumeModel[]) => {
+
+              const volumesFromRegion: { volume: SapiVolumeModel, volumeMetadata: ParcVolumeSpec }[] = missingVolumes.map(
+                volume => {
+                  if (!volume || !volumeIdToRegionMap.has(volume['@id'])) {
+                    return null
+                  }
+
+                  try {
+
+                    const found = volumeIdToRegionMap.get(volume['@id'])
+                    const volumeMetadata: ParcVolumeSpec = {
+                      regions: found,
+                      parcellation,
+                      volumeSrc: volume.data.url
+                    }
+                    return {
+                      volume,
+                      volumeMetadata
+                    }
+                  } catch (e) {
+                    console.error(`volume from region error`, e)
+                    return null
+                  }
+                }
+              ).filter(
+                v => !!v
+              )
+
+              return [
+                ...volumesFromParc,
+                ...volumesFromRegion
+              ]
+            })
+          )
+        })
+      )
+    
+    const spaceVols$ = !!template
+      ? this.sapi.getSpace(atlas["@id"], template["@id"]).getVolumes().pipe(
+        shareReplay(1),
+      )
+      : of([] as SapiVolumeModel[])
+    
+    return forkJoin({
+      tmplVolumes: spaceVols$.pipe(
+        map(
+          volumes => volumes.filter(vol => "neuroglancer/precomputed" in vol.data.detail)
+        ),
+      ),
+      tmplAuxMeshVolumes: spaceVols$.pipe(
+        map(
+          volumes => volumes.filter(vol => "neuroglancer/precompmesh" in vol.data.detail)
+        ),
+      ),
+      parcVolumes: parcVolumes$.pipe(
+      )
+    })
+  }
+
+  onATPDebounceNgLayers$ = this.onATP$.pipe(
+    debounceTime(16),
+    switchMap(({ atlas, parcellation, template }) => 
+      this.getNgLayers(atlas, parcellation, template).pipe(
+        map(volumes => getNgLayersFromVolumesATP(volumes, { atlas, parcellation, template }))
+      )
+    ),
+    shareReplay(1)
+  )
+
+  onATPDebounceAddBaseLayers$ = createEffect(() => this.onATPDebounceNgLayers$.pipe(
+    switchMap(ngLayers => {
+      const { parcNgLayers, tmplAuxNgLayers, tmplNgLayers } = ngLayers
+      
+      const customBaseLayers: atlasAppearance.NgLayerCustomLayer[] = []
+      for (const layers of [parcNgLayers, tmplAuxNgLayers, tmplNgLayers]) {
+        for (const key in layers) {
+          const { source, transform, opacity, visible } = layers[key]
+          customBaseLayers.push({
+            clType: "baselayer/nglayer",
+            id: key,
+            source,
+            transform,
+            opacity,
+            visible,
+          })
+        }
+      }
+      return of(
+        ...customBaseLayers.map(layer => 
+          atlasAppearance.actions.addCustomLayer({
+            customLayer: layer
+          })  
+        )
+      )
+    })
+  ))
+
+  constructor(
+    private store: Store<any>,
+    private sapi: SAPI,  
+  ){
+
+  }
+}
\ No newline at end of file
diff --git a/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.service.spec.ts b/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.service.spec.ts
index 45bbe33795462fcb6669da903c56063dc76f11d9..23ad93defa44bb856c658d5b5e850acf9961895d 100644
--- a/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.service.spec.ts
+++ b/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.service.spec.ts
@@ -1,14 +1,15 @@
-import { fakeAsync, TestBed, tick } from "@angular/core/testing"
+import { fakeAsync, TestBed } from "@angular/core/testing"
 import { MockStore, provideMockStore } from "@ngrx/store/testing"
-import { viewerStateCustomLandmarkSelector, viewerStateSelectedParcellationSelector, viewerStateSelectedRegionsSelector, viewerStateSelectedTemplateSelector } from "src/services/state/viewerState/selectors"
 import { NehubaLayerControlService } from "./layerCtrl.service"
 import * as layerCtrlUtil from '../constants'
-import { hot } from "jasmine-marbles"
-import { IColorMap } from "./layerCtrl.util"
-import { debounceTime } from "rxjs/operators"
-import { ngViewerSelectorClearView, ngViewerSelectorLayers } from "src/services/state/ngViewerState.store.helper"
-import { RouterService } from "src/routerModule/router.service"
+import {
+  annotation,
+  atlasAppearance,
+  atlasSelection
+} from "src/state"
+import { LayerCtrlEffects } from "./layerCtrl.effects"
 import { NEVER } from "rxjs"
+import { RouterService } from "src/routerModule/router.service"
 
 describe('> layerctrl.service.ts', () => {
   describe('> NehubaLayerControlService', () => {
@@ -26,6 +27,12 @@ describe('> layerctrl.service.ts', () => {
           },
           NehubaLayerControlService,
           provideMockStore(),
+          {
+            provide: LayerCtrlEffects,
+            useValue: {
+              onATPDebounceNgLayers$: NEVER
+            }
+          }
         ]
       })
 
@@ -35,10 +42,12 @@ describe('> layerctrl.service.ts', () => {
         layerCtrlUtil,
         'getMultiNgIdsRegionsLabelIndexMap'
       ).and.returnValue(() => getMultiNgIdsRegionsLabelIndexMapReturnVal)
-      mockStore.overrideSelector(viewerStateCustomLandmarkSelector, [])
-      mockStore.overrideSelector(viewerStateSelectedRegionsSelector, [])
-      mockStore.overrideSelector(viewerStateSelectedTemplateSelector, {})
-      mockStore.overrideSelector(viewerStateSelectedParcellationSelector, {})
+      mockStore.overrideSelector(atlasAppearance.selectors.customLayers, [])
+      mockStore.overrideSelector(atlasAppearance.selectors.showDelineation, true)
+      mockStore.overrideSelector(annotation.selectors.annotations, [])
+      mockStore.overrideSelector(atlasSelection.selectors.selectedRegions, [])
+      mockStore.overrideSelector(atlasSelection.selectors.selectedTemplate, {} as any)
+      mockStore.overrideSelector(atlasSelection.selectors.selectedParcellation, {} as any)
     })
 
     it('> can be init', () => {
@@ -51,132 +60,27 @@ describe('> layerctrl.service.ts', () => {
         describe('> template/parc has no aux meshes', () => {
 
           it('> calls getMultiNgIdsRegionsLabelIndexMapReturn', () => {
-            const service = TestBed.inject(NehubaLayerControlService)
-            service.setColorMap$.subscribe()
-            expect(getMultiNgIdsRegionsLabelIndexMapSpy).toHaveBeenCalled()
+            
           })
 
           it('> emitted value is as expected', fakeAsync(() => {
-            const map = new Map<number, layerCtrlUtil.IRegion>()
-            getMultiNgIdsRegionsLabelIndexMapReturnVal.set(
-              'foo-bar',
-              map
-            )
-            map.set(1, {
-              ngId: 'foo-bar',
-              rgb: [100, 200, 255]
-            })
-            map.set(2, {
-              ngId: 'foo-bar',
-              rgb: [15, 15, 15]
-            })
-
-            const service = TestBed.inject(NehubaLayerControlService)
-            let v: any
-            service.setColorMap$.subscribe(val => {
-              v = val
-            })
-            tick(32)
-            const expectedVal = {
-              'foo-bar': {
-                1: { red: 100, green: 200, blue: 255 },
-                2: { red: 15, green: 15, blue: 15}
-              }
-            }
-            expect(v).toEqual(expectedVal)
+
           }))
 
         })
 
         describe('> template/parc has aux meshes', () => {
-          let tmplAuxMeshes = [{
-            name: 'foo-bar',
-            ngId: 'bazz',
-            labelIndicies: [1,2,3],
-            rgb: [100, 100, 100]
-          }, {
-            name: 'hello-world',
-            ngId: 'hello-world',
-            labelIndicies: [4,5,6],
-            rgb: [200, 200, 200]
-          }]
-          let parcAuxMeshes = [{
-            name: 'hello-world',
-            ngId: 'hello-world',
-            labelIndicies: [10,11,12],
-            rgb: [255, 255, 255]
-          }]
+
           beforeEach(() => {
-            mockStore.overrideSelector(viewerStateSelectedTemplateSelector, {
-              auxMeshes: tmplAuxMeshes
-            })
-            mockStore.overrideSelector(viewerStateSelectedParcellationSelector, {
-              auxMeshes: parcAuxMeshes
-            })
+
           })
 
           it('> should inherit values from tmpl and parc',  fakeAsync(() => {
 
-            const service = TestBed.inject(NehubaLayerControlService)
-            let val
-            service.setColorMap$.subscribe(v => {
-              val = v
-            })
-
-            tick(32)
-
-            expect(val).toEqual({
-              'bazz': {
-                1: { red: 100, green: 100, blue: 100 },
-                2: { red: 100, green: 100, blue: 100 },
-                3: { red: 100, green: 100, blue: 100 },
-              },
-              'hello-world': {
-                4: { red: 200, green: 200, blue: 200 },
-                5: { red: 200, green: 200, blue: 200 },
-                6: { red: 200, green: 200, blue: 200 },
-                10: { red: 255, green: 255, blue: 255 },
-                11: { red: 255, green: 255, blue: 255 },
-                12: { red: 255, green: 255, blue: 255 },
-              }
-            })
           }))
 
           it('> should overwrite any value if at all, from region', fakeAsync(() => {
-            const map = new Map<number, layerCtrlUtil.IRegion>()
-            map.set(10, {
-              ngId: 'hello-world',
-              rgb: [0, 0, 0]
-            })
-            map.set(15, {
-              ngId: 'hello-world',
-              rgb: [0, 0, 0]
-            })
-            getMultiNgIdsRegionsLabelIndexMapReturnVal.set('hello-world', map)
-
-            const service = TestBed.inject(NehubaLayerControlService)
-            let val
-            service.setColorMap$.subscribe(v => {
-              val = v
-            })
-
-            tick(32)
-            expect(val).toEqual({
-              'bazz': {
-                1: { red: 100, green: 100, blue: 100 },
-                2: { red: 100, green: 100, blue: 100 },
-                3: { red: 100, green: 100, blue: 100 },
-              },
-              'hello-world': {
-                4: { red: 200, green: 200, blue: 200 },
-                5: { red: 200, green: 200, blue: 200 },
-                6: { red: 200, green: 200, blue: 200 },
-                10: { red: 255, green: 255, blue: 255 },
-                11: { red: 255, green: 255, blue: 255 },
-                12: { red: 255, green: 255, blue: 255 },
-                15: { red: 0, green: 0, blue: 0 },
-              }
-            })
+
           }))
         })
       })
@@ -195,66 +99,13 @@ describe('> layerctrl.service.ts', () => {
     
       describe('> overwriteColorMap$ firing', () => {
         beforeEach(() => {
-          mockStore.overrideSelector(viewerStateSelectedTemplateSelector, {})
-          mockStore.overrideSelector(viewerStateSelectedParcellationSelector, {})
-          const map = new Map<number, layerCtrlUtil.IRegion>()
-          getMultiNgIdsRegionsLabelIndexMapReturnVal.set(
-            'foo-bar',
-            map
-          )
-          map.set(1, {
-            ngId: 'foo-bar',
-            rgb: [100, 200, 255]
-          })
-          map.set(2, {
-            ngId: 'foo-bar',
-            rgb: [15, 15, 15]
-          })
         })
 
         it('> should overwrite existing colormap', () => {
-          const service = TestBed.inject(NehubaLayerControlService)
-          service.overwriteColorMap$.next(foobar2)
-
-          expect(service.setColorMap$).toBeObservable(
-            hot('(b)', {
-              a: foobar1,
-              b: foobar2
-            })
-          )
+
         })
 
         it('> unsub/resub should not result in overwritecolormap last emitted value', fakeAsync(() => {
-          const service = TestBed.inject(NehubaLayerControlService)
-
-          let subscrbiedVal: IColorMap
-          const sub = service.setColorMap$.pipe(
-            debounceTime(16),
-          ).subscribe(val => {
-            subscrbiedVal = val
-          })
-
-          // see TODO this is a dirty fix
-          tick(32)
-          service.overwriteColorMap$.next(foobar2)
-          tick(32)
-          expect(subscrbiedVal).toEqual(foobar2)
-          tick(16)
-          sub.unsubscribe()
-          subscrbiedVal = null
-
-          // mock emit selectParc etc...
-          mockStore.overrideSelector(viewerStateSelectedParcellationSelector, {})
-          mockStore.setState({})
-          const sub2 = service.setColorMap$.pipe(
-            debounceTime(16),
-          ).subscribe(val => {
-            subscrbiedVal = val
-          })
-
-          tick(32)
-          expect(subscrbiedVal).toEqual(foobar1)
-          sub2.unsubscribe()
 
         }))
       })
@@ -263,48 +114,10 @@ describe('> layerctrl.service.ts', () => {
 
     describe('> visibleLayer$', () => {
       beforeEach(() => {
-        mockStore.overrideSelector(viewerStateSelectedTemplateSelector, {
-          ngId: 'tmplNgId',
-          auxMeshes: [{
-            ngId: 'tmplAuxId1',
-            labelIndicies: [1,2,3]
-          },{
-            ngId: 'tmplAuxId2',
-            labelIndicies: [1,2,3]
-          }]
-        })
-
-        mockStore.overrideSelector(viewerStateSelectedParcellationSelector, {
-          auxMeshes: [{
-            ngId: 'parcAuxId1',
-            labelIndicies: [1,2,3],
-          },{
-            ngId: 'parcAuxId2',
-            labelIndicies: [1,2,3]
-          }]
-        })
-
-        getMultiNgIdsRegionsLabelIndexMapReturnVal.set(
-          'regionsNgId1', null
-        )
 
-        getMultiNgIdsRegionsLabelIndexMapReturnVal.set(
-          'regionsNgId2', null
-        )
       })
       it('> combines ngId of template, aux mesh and regions', () => {
-        const service = TestBed.inject(NehubaLayerControlService)
-        expect(service.visibleLayer$).toBeObservable(hot('a', {
-          a: [
-            'tmplNgId',
-            'tmplAuxId1',
-            'tmplAuxId2',
-            'parcAuxId1',
-            'parcAuxId2',
-            'regionsNgId1',
-            'regionsNgId2',
-          ]
-        }))
+
       })
     })
 
@@ -318,106 +131,48 @@ describe('> layerctrl.service.ts', () => {
         labelIndex: 2
       }
       beforeEach(() => {
-        mockStore.overrideSelector(viewerStateSelectedRegionsSelector, [])
-        mockStore.overrideSelector(ngViewerSelectorLayers, [])
-        mockStore.overrideSelector(ngViewerSelectorClearView, false)
-        mockStore.overrideSelector(viewerStateSelectedParcellationSelector, {})
       })
 
       it('> by default, should return []', () => {
-        const service = TestBed.inject(NehubaLayerControlService)
-        expect(service.segmentVis$).toBeObservable(
-          hot('a', {
-            a: []
-          })
-        )
+
       })
 
       describe('> if sel regions exist', () => {
         beforeEach(() => {
-          mockStore.overrideSelector(viewerStateSelectedRegionsSelector, [
-            region1, region2
-          ])
+
         })
 
         it('> default, should return encoded strings', () => {
-          mockStore.overrideSelector(viewerStateSelectedRegionsSelector, [
-            region1, region2
-          ])
-          const service = TestBed.inject(NehubaLayerControlService)
-          expect(service.segmentVis$).toBeObservable(
-            hot('a', {
-              a: [`ngid#1`, `ngid#2`]
-            })
-          )
+
         })
 
         it('> if clearflag is true, then return []', () => {
 
-          mockStore.overrideSelector(ngViewerSelectorClearView, true)
-          const service = TestBed.inject(NehubaLayerControlService)
-          expect(service.segmentVis$).toBeObservable(
-            hot('a', {
-              a: []
-            })
-          )
         })        
       })
 
       describe('> if non mixable layer exist', () => {
         beforeEach(() => {
-          mockStore.overrideSelector(ngViewerSelectorLayers, [{
-            mixability: 'nonmixable'
-          }])
         })
 
         it('> default, should return null', () => {
-          const service = TestBed.inject(NehubaLayerControlService)
-          expect(service.segmentVis$).toBeObservable(
-            hot('a', {
-              a: null
-            })
-          )
+
         })
 
         it('> if regions selected, should still return null', () => {
 
-          mockStore.overrideSelector(viewerStateSelectedRegionsSelector, [
-            region1, region2
-          ])
-          const service = TestBed.inject(NehubaLayerControlService)
-          expect(service.segmentVis$).toBeObservable(
-            hot('a', {
-              a: null
-            })
-          )
         })
 
         describe('> if clear flag is set', () => {
           beforeEach(() => {
-            mockStore.overrideSelector(ngViewerSelectorClearView, true)
+            
           })
 
           it('> default, should return []', () => {
-            const service = TestBed.inject(NehubaLayerControlService)
-            expect(service.segmentVis$).toBeObservable(
-              hot('a', {
-                a: []
-              })
-            )
           })
 
           it('> if reg selected, should return []', () => {
 
-            mockStore.overrideSelector(viewerStateSelectedRegionsSelector, [
-              region1, region2
-            ])
-            const service = TestBed.inject(NehubaLayerControlService)
-            expect(service.segmentVis$).toBeObservable(
-              hot('a', {
-                a: []
-              })
-            )
           })
         })
       })
diff --git a/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.service.ts b/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.service.ts
index 3c0bbbeb52c0eb12e1e989c8ad685db4d9bbf92e..136c0d0a314394628ddccdcd91010e85420f7524 100644
--- a/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.service.ts
+++ b/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.service.ts
@@ -1,19 +1,21 @@
-import { Inject, Injectable, OnDestroy, Optional } from "@angular/core";
+import { Injectable, OnDestroy } from "@angular/core";
 import { select, Store } from "@ngrx/store";
-import { BehaviorSubject, combineLatest, from, merge, NEVER, Observable, of, Subject, Subscription } from "rxjs";
-import { debounceTime, distinctUntilChanged, filter, map, mapTo, shareReplay, startWith, switchMap, withLatestFrom } from "rxjs/operators";
-import { viewerStateCustomLandmarkSelector, viewerStateSelectedParcellationSelector, viewerStateSelectedRegionsSelector, viewerStateSelectedTemplateSelector } from "src/services/state/viewerState/selectors";
-import { getRgb, IColorMap, INgLayerCtrl, INgLayerInterface, TNgLayerCtrl } from "./layerCtrl.util";
-import { getMultiNgIdsRegionsLabelIndexMap } from "../constants";
-import { IAuxMesh } from '../store'
-import { REGION_OF_INTEREST } from "src/util/interfaces";
-import { TRegionDetail } from "src/util/siibraApiConstants/types";
-import { EnumColorMapName } from "src/util/colorMaps";
-import { getShader, PMAP_DEFAULT_CONFIG } from "src/util/constants";
-import { ngViewerActionAddNgLayer, ngViewerActionRemoveNgLayer, ngViewerSelectorClearView, ngViewerSelectorLayers } from "src/services/state/ngViewerState.store.helper";
-import { serialiseParcellationRegion } from 'common/util'
-import { _PLI_VOLUME_INJ_TOKEN, _TPLIVal } from "src/glue";
-import { RouterService } from "src/routerModule/router.service";
+import { combineLatest, merge, Observable, Subject, Subscription } from "rxjs";
+import { debounceTime, distinctUntilChanged, filter, map, pairwise, shareReplay, startWith, switchMap, withLatestFrom } from "rxjs/operators";
+import { IColorMap, INgLayerCtrl, TNgLayerCtrl } from "./layerCtrl.util";
+import { SAPIRegion } from "src/atlasComponents/sapi/core";
+import { getParcNgId } from "../config.service"
+import { getRegionLabelIndex } from "../config.service/util";
+import { annotation, atlasAppearance, atlasSelection } from "src/state";
+import { serializeSegment } from "../util";
+import { LayerCtrlEffects } from "./layerCtrl.effects";
+import { arrayEqual } from "src/util/array";
+import { ColorMapCustomLayer } from "src/state/atlasAppearance";
+import { SapiRegionModel } from "src/atlasComponents/sapi";
+import { AnnotationLayer } from "src/atlasComponents/annotations";
+import { PMAP_LAYER_NAME } from "../constants"
+import { EnumColorMapName, mapKeyColorMap } from "src/util/colorMaps";
+import { getShader } from "src/util/constants";
 
 export const BACKUP_COLOR = {
   red: 255,
@@ -21,241 +23,152 @@ export const BACKUP_COLOR = {
   blue: 255
 }
 
-export function getAuxMeshesAndReturnIColor(auxMeshes: IAuxMesh[]): IColorMap{
-  const returnVal: IColorMap = {}
-  for (const auxMesh of auxMeshes as IAuxMesh[]) {
-    const { ngId, labelIndicies, rgb = [255, 255, 255] } = auxMesh
-    const auxMeshColorMap = returnVal[ngId] || {}
-    for (const lblIdx of labelIndicies) {
-      auxMeshColorMap[lblIdx as number] = {
-        red: rgb[0] as number,
-        green: rgb[1] as number,
-        blue: rgb[2] as number,
-      }
-    }
-    returnVal[ngId] = auxMeshColorMap
-  }
-  return returnVal
-}
-
 @Injectable({
   providedIn: 'root'
 })
 export class NehubaLayerControlService implements OnDestroy{
 
-  static PMAP_LAYER_NAME = 'regional-pmap'
-
   private selectedRegion$ = this.store$.pipe(
-    select(viewerStateSelectedRegionsSelector),
+    select(atlasSelection.selectors.selectedRegions),
     shareReplay(1),
   )
 
-  private selectedParcellation$ = this.store$.pipe(
-    select(viewerStateSelectedParcellationSelector)
+
+  private defaultNgLayers$ = this.layerEffects.onATPDebounceNgLayers$
+
+  private selectedATP$ = this.store$.pipe(
+    atlasSelection.fromRootStore.distinctATP(),
+    shareReplay(1),
   )
 
-  private selectedTemplateSelector$ = this.store$.pipe(
-    select(viewerStateSelectedTemplateSelector)
+  public selectedATPR$ = this.selectedATP$.pipe(
+    switchMap(({ atlas, template, parcellation }) => 
+      this.store$.pipe(
+        select(atlasSelection.selectors.selectedParcAllRegions),
+        map(regions => ({
+          atlas, template, parcellation, regions
+        })),
+        shareReplay(1)
+      )
+    )
   )
 
-  private selParcNgIdMap$ = this.selectedParcellation$.pipe(
-    map(parc => getMultiNgIdsRegionsLabelIndexMap(parc)),
-    shareReplay(1),
+  private customLayers$ = this.store$.pipe(
+    select(atlasAppearance.selectors.customLayers),
+    distinctUntilChanged(arrayEqual((o, n) => o.id === n.id)),
+    shareReplay(1)
   )
-  
-  private activeColorMap$: Observable<IColorMap> = combineLatest([
-    this.selParcNgIdMap$.pipe(
-      map(map => {
+  private activeColorMap$ = combineLatest([
+    combineLatest([
+      this.selectedATPR$,
+      this.customLayers$,
+    ]).pipe(
+      map(([{ atlas, parcellation, regions, template }, layers]) => {
         const returnVal: IColorMap = {}
-        for (const [ key, val ] of map.entries()) {
-          returnVal[key] = {}
-          for (const [ lblIdx, region ] of val.entries()) {
-            const rgb = getRgb(lblIdx, region)
-            returnVal[key][lblIdx] = rgb
+
+        const cmCustomLayers = layers.filter(l => l.clType === "customlayer/colormap") as ColorMapCustomLayer[]
+        const cmBaseLayers = layers.filter(l => l.clType === "baselayer/colormap") as ColorMapCustomLayer[]
+        
+        const useCm = (() => {
+          /**
+           * if custom layer exist, use the last custom layer
+           */
+          if (cmCustomLayers.length > 0) return cmCustomLayers[cmCustomLayers.length - 1].colormap
+          /**
+           * otherwise, use last baselayer
+           */
+          if (cmBaseLayers.length > 0) return cmBaseLayers[cmBaseLayers.length - 1].colormap
+          /**
+           * fallback color map
+           */
+          return {
+            set: () => {
+              throw new Error(`cannot set`)
+            },
+            get: (r: SapiRegionModel) => SAPIRegion.GetDisplayColor(r)
           }
+        })()
+        
+        for (const r of regions) {
+
+          if (!r.hasAnnotation) continue
+          if (!r.hasAnnotation.visualizedIn) continue
+
+          const ngId = getParcNgId(atlas, template, parcellation, r)
+          const labelIndex = getRegionLabelIndex(atlas, template, parcellation, r)
+          if (!labelIndex) continue
+
+          const [ red, green, blue ] = useCm.get(r)
+
+          if (!returnVal[ngId]) {
+            returnVal[ngId] = {}
+          }
+          returnVal[ngId][labelIndex] = { red, green, blue }
         }
         return returnVal
       })
     ),
-    this.selectedRegion$,
-    this.selectedTemplateSelector$.pipe(
-      map(template => {
-        const { auxMeshes = [] } = template || {}
-        return getAuxMeshesAndReturnIColor(auxMeshes)
-      })
-    ),
-    this.selectedParcellation$.pipe(
-      map(parc => {
-        const { auxMeshes = [] } = parc || {}
-        return getAuxMeshesAndReturnIColor(auxMeshes)
-      })
-    ),
-  ]).pipe(
-    map(([ regions, selReg, ...auxMeshesArr ]) => {
-      
-      const returnVal: IColorMap = {}
-      if (selReg.length === 0) {
-        for (const key in regions) {
-          returnVal[key] = regions[key]
-        }
-      } else {
-        /**
-         * if selected regions are non empty
-         * set the selected regions to show color,
-         * but the rest to show white 
-         */
-        for (const key in regions) {
-          const colorMap = {}
-          returnVal[key] = colorMap
-          for (const lblIdx in regions[key]) {
-            if (selReg.some(r => r.ngId === key && r.labelIndex === Number(lblIdx))) {
-              colorMap[lblIdx] = regions[key][lblIdx]
-            } else {
-              colorMap[lblIdx] = BACKUP_COLOR
+    this.defaultNgLayers$.pipe(
+      map(({ tmplAuxNgLayers }) => {
+        const returnVal: IColorMap = {}
+        for (const ngId in tmplAuxNgLayers) {
+          returnVal[ngId] = {}
+          const { auxMeshes } = tmplAuxNgLayers[ngId]
+          for (const auxMesh of auxMeshes) {
+            const { labelIndicies } = auxMesh
+            for (const lblIdx of labelIndicies) {
+              returnVal[ngId][lblIdx] = BACKUP_COLOR
             }
           }
         }
-      }
-
-      for (const auxMeshes of auxMeshesArr) {
-        for (const key in auxMeshes) {
-          const existingObj = returnVal[key] || {}
-          returnVal[key] = {
-            ...existingObj,
-            ...auxMeshes[key],
-          }
-        }
-      }
-      this.activeColorMap = returnVal
-      return returnVal
-    })
-  )
-
-  private auxMeshes$: Observable<IAuxMesh[]> = combineLatest([
-    this.selectedTemplateSelector$,
-    this.selectedParcellation$,
+        return returnVal
+      })
+    )
   ]).pipe(
-    map(([ tmpl, parc ]) => {
-      const { auxMeshes: tmplAuxMeshes = [] as IAuxMesh[] } = tmpl || {}
-      const { auxMeshes: parclAuxMeshes = [] as IAuxMesh[] } = parc || {}
-      return [...tmplAuxMeshes, ...parclAuxMeshes]
-    })
+    map(([cmParc, cmAux]) => ({
+      ...cmParc,
+      ...cmAux
+    }))
   )
 
   private sub: Subscription[] = []
 
-  ngOnDestroy(){
+  ngOnDestroy(): void{
     while (this.sub.length > 0) this.sub.pop().unsubscribe()
   }
 
-  private pliVol$: Observable<string[]> = this._pliVol$
-    ? this._pliVol$.pipe(
-      map(arr => {
-        const output = []
-        for (const item of arr) {
-          for (const volume of item.data["iav-registered-volumes"].volumes) {
-            output.push(volume.name)
-          }
-        }
-        return output
-      })
-    )
-    : NEVER
   constructor(
     private store$: Store<any>,
-    private routerSvc: RouterService,
-    @Optional() @Inject(_PLI_VOLUME_INJ_TOKEN) private _pliVol$: Observable<_TPLIVal[]>,
-    @Optional() @Inject(REGION_OF_INTEREST) roi$: Observable<TRegionDetail>
+    private layerEffects: LayerCtrlEffects,
   ){
 
-    if (roi$) {
-
-      this.sub.push(
-        roi$.pipe(
-          switchMap(roi => {
-            if (!roi || !roi.hasRegionalMap) {
-              // clear pmap
-              return of(null)
-            }
-            
-            const { links } = roi
-            const { regional_map: regionalMapUrl, regional_map_info: regionalMapInfoUrl } = links
-            return from(fetch(regionalMapInfoUrl).then(res => res.json())).pipe(
-              map(regionalMapInfo => {
-                return {
-                  roi,
-                  regionalMapUrl,
-                  regionalMapInfo
-                }
-              })
-            )
-          })
-        ).subscribe(processedRoi => {
-          if (!processedRoi) {
-            this.store$.dispatch(
-              ngViewerActionRemoveNgLayer({
-                layer: {
-                  name: NehubaLayerControlService.PMAP_LAYER_NAME
-                }
-              })
-            )
-            return
-          }
-          const { 
-            roi,
-            regionalMapUrl,
-            regionalMapInfo
-          } = processedRoi
-          const { min, max, colormap = EnumColorMapName.VIRIDIS } = regionalMapInfo || {} as any
-
-          const shaderObj = {
-            ...PMAP_DEFAULT_CONFIG,
-            ...{ colormap },
-            ...( typeof min !== 'undefined' ? { lowThreshold: min } : {} ),
-            ...( max ? { highThreshold: max } : { highThreshold: 1 } )
-          }
+    this.sub.push(
 
-          const layer = {
-            name: NehubaLayerControlService.PMAP_LAYER_NAME,
-            source : `nifti://${regionalMapUrl}`,
-            mixability : 'nonmixable',
-            shader : getShader(shaderObj),
+      /**
+       * on store showdelin
+       * toggle parcnglayers visibility
+       */
+      this.store$.pipe(
+        select(atlasAppearance.selectors.showDelineation),
+        withLatestFrom(this.defaultNgLayers$)
+      ).subscribe(([flag, { parcNgLayers }]) => {
+        const layerObj = {}
+        for (const key in parcNgLayers) {
+          layerObj[key] = {
+            visible: flag
           }
+        }
 
-          this.store$.dispatch(
-            ngViewerActionAddNgLayer({ layer })
-          )
-
-          // this.layersService.highThresholdMap.set(layerName, highThreshold)
-          // this.layersService.lowThresholdMap.set(layerName, lowThreshold)
-          // this.layersService.colorMapMap.set(layerName, cmap)
-          // this.layersService.removeBgMap.set(layerName, removeBg)
+        this.manualNgLayersControl$.next({
+          type: 'update',
+          payload: layerObj
         })
-      )
-    }
-
-    this.sub.push(
-      this.ngLayers$.subscribe(({ ngLayers }) => {
-        this.ngLayersRegister.layers = ngLayers
-      })
+      }),
     )
 
     this.sub.push(
-      this.store$.pipe(
-        select(ngViewerSelectorClearView),
-        distinctUntilChanged()
-      ).subscribe(flag => {
-        const pmapLayer = this.ngLayersRegister.layers.find(l => l.name === NehubaLayerControlService.PMAP_LAYER_NAME)
-        if (!pmapLayer) return
-        const payload = {
-          type: 'update',
-          payload: {
-            [NehubaLayerControlService.PMAP_LAYER_NAME]: {
-              visible: !flag
-            }
-          }
-        } as TNgLayerCtrl<'update'>
-        this.manualNgLayersControl$.next(payload)
+      this.ngLayers$.subscribe(({ customLayers }) => {
+        this.ngLayersRegister = customLayers
       })
     )
 
@@ -264,18 +177,17 @@ export class NehubaLayerControlService implements OnDestroy{
      */
     this.sub.push(
       this.store$.pipe(
-        select(viewerStateCustomLandmarkSelector),
-        withLatestFrom(this.auxMeshes$)
-      ).subscribe(([landmarks, auxMeshes]) => {
-        
+        select(annotation.selectors.annotations),
+        withLatestFrom(this.defaultNgLayers$)
+      ).subscribe(([landmarks, { tmplAuxNgLayers }]) => {
         const payload: {
           [key: string]: number
         } = {}
         const alpha = landmarks.length > 0
           ? 0.2
           : 1.0
-        for (const auxMesh of auxMeshes) {
-          payload[auxMesh.ngId] = alpha
+        for (const ngId in tmplAuxNgLayers) {
+          payload[ngId] = alpha
         }
         
         this.manualNgLayersControl$.next({
@@ -286,98 +198,73 @@ export class NehubaLayerControlService implements OnDestroy{
     )
   }
 
-  public activeColorMap: IColorMap
-
-  public overwriteColorMap$ = new BehaviorSubject<IColorMap>(null)
-
-  public setColorMap$: Observable<IColorMap> = merge(
-    this.activeColorMap$.pipe(
-      // TODO this is a dirty fix
-      // it seems, sometimes, overwritecolormap and activecolormap can emit at the same time
-      // (e.g. when reg selection changes)
-      // this ensures that the activecolormap emits later, and thus take effect over overwrite colormap
-      debounceTime(16),
-    ),
-    this.overwriteColorMap$.pipe(
-      filter(v => !!v),
-    )
+  public setColorMap$: Observable<IColorMap> = this.activeColorMap$.pipe(
+    debounceTime(16),
   ).pipe(
     shareReplay(1)
   )
 
-  public expectedLayerNames$ = combineLatest([
-    this.selectedTemplateSelector$,
-    this.auxMeshes$,
-    this.selParcNgIdMap$,
-  ]).pipe(
-    map(([ tmpl, auxMeshes, parcNgIdMap ]) => {
-      const ngIdSet = new Set<string>()
-      const { ngId } = tmpl
-      ngIdSet.add(ngId)
-      for (const auxMesh of auxMeshes) {
-        const { ngId } = auxMesh
-        ngIdSet.add(ngId as string)
-      }
-      for (const ngId of parcNgIdMap.keys()) {
-        ngIdSet.add(ngId)
-      }
-      return Array.from(ngIdSet)
-    })
-  )
-
-  public visibleLayer$: Observable<string[]> = combineLatest([
-    this.expectedLayerNames$,
-    this.pliVol$.pipe(
-      startWith([])
-    ),
-    this.routerSvc.customRoute$.pipe(
-      startWith({}),
-      map(val => val['x-voi'] === "d71d369a-c401-4d7e-b97a-3fb78eed06c5"
-        ? ["VOI_1 (area V1)", "VOI_2 (area V2)"]
-        : []),
-    )
-  ]).pipe(
-    map(([ expectedLayerNames, layerNames, voiLayers ]) => {
-      const ngIdSet = new Set<string>([...layerNames, ...expectedLayerNames, ...voiLayers])
-      return Array.from(ngIdSet)
+  public expectedLayerNames$ = this.defaultNgLayers$.pipe(
+    map(({ parcNgLayers, tmplAuxNgLayers, tmplNgLayers }) => {
+      return [
+        ...Object.keys(parcNgLayers),
+        ...Object.keys(tmplAuxNgLayers),
+        ...Object.keys(tmplNgLayers),
+      ]
     })
   )
 
   /**
    * define when shown segments should be updated
    */
+  public _segmentVis$: Observable<string[]> = combineLatest([
+    this.selectedATP$,
+    this.selectedRegion$
+  ]).pipe(
+    map(() => [''])
+  )
+
   public segmentVis$: Observable<string[]> = combineLatest([
     /**
      * selectedRegions
      */
     this.selectedRegion$,
+    this.customLayers$.pipe(
+      map(layers => layers.filter(l => l.clType === "customlayer/colormap").length > 0),
+    ),
     /**
      * if layer contains non mixable layer
      */
-    this.store$.pipe(
-      select(ngViewerSelectorLayers),
-      map(layers => layers.findIndex(l => l.mixability === 'nonmixable') >= 0),
+    this.customLayers$.pipe(
+      map(layers => layers.filter(l => l.clType === "customlayer/nglayer").length > 0),
     ),
-    /**
-     * clearviewqueue, indicating something is controlling colour map
-     * show all seg
-     */
-    this.store$.pipe(
-      select(ngViewerSelectorClearView),
-      distinctUntilChanged()
-    )
   ]).pipe(
-    withLatestFrom(this.selectedParcellation$),
-    map(([[ regions, nonmixableLayerExists, clearViewFlag ], selParc]) => {
-      if (nonmixableLayerExists && !clearViewFlag) {
+    withLatestFrom(this.selectedATPR$),
+    map(([[ selectedRegions, customMapExists, nonmixableLayerExists ], { atlas, parcellation, template, regions }]) => {
+      /**
+       * if non mixable layer exist (e.g. pmap)
+       * and no custom color map exist
+       * hide all segmentations
+       */
+      if (!customMapExists && nonmixableLayerExists) {
         return null
       }
-      const { ngId: defaultNgId } = selParc || {}
   
-      /* selectedregionindexset needs to be updated regardless of forceshowsegment */
-      const selectedRegionIndexSet = new Set<string>(regions.map(({ngId = defaultNgId, labelIndex}) => serialiseParcellationRegion({ ngId, labelIndex })))
-      if (selectedRegionIndexSet.size > 0 && !clearViewFlag) {
-        return [...selectedRegionIndexSet]
+      /**
+       * if custom map exists, roi is all regions
+       * otherwise, roi is only selectedRegions
+       */
+      const roi = customMapExists ? regions : selectedRegions
+
+      const roiIndexSet = new Set<string>(
+        roi.map(r => {
+          const ngId = getParcNgId(atlas, template, parcellation, r)
+          const label = getRegionLabelIndex(atlas, template, parcellation, r)
+          return ngId && label && serializeSegment(ngId, label)
+        }).filter(v => !!v)
+      )
+      if (roiIndexSet.size > 0) {
+        return [...roiIndexSet]
       } else {
         return []
       }
@@ -388,38 +275,38 @@ export class NehubaLayerControlService implements OnDestroy{
    * ngLayers controller
    */
 
-  private ngLayersRegister: {layers: INgLayerInterface[]} = {
-    layers: []
-  }
-  public removeNgLayers(layerNames: string[]) {
-    this.ngLayersRegister.layers
-      .filter(layer => layerNames?.findIndex(l => l === layer.name) >= 0)
-      .map(l => l.name)
-      .forEach(layerName => {
-        this.store$.dispatch(ngViewerActionRemoveNgLayer({
-          layer: {
-            name: layerName
-          }
-        }))
-      })
-  }
-  public addNgLayer(layers: INgLayerInterface[]){
-    this.store$.dispatch(ngViewerActionAddNgLayer({
-      layer: layers
-    }))
+  private ngLayersRegister: atlasAppearance.NgLayerCustomLayer[] = []
+
+  private getUpdatedCustomLayer(isSameLayer: (o: atlasAppearance.NgLayerCustomLayer, n: atlasAppearance.NgLayerCustomLayer) => boolean){
+    return this.store$.pipe(
+      select(atlasAppearance.selectors.customLayers),
+      map(customLayers => customLayers.filter(l => l.clType === "customlayer/nglayer") as atlasAppearance.NgLayerCustomLayer[]),
+      pairwise(),
+      map(([ oldCustomLayers, newCustomLayers ]) => {
+        return newCustomLayers.filter(n => oldCustomLayers.some(o => o.id === n.id && !isSameLayer(o, n)))
+      }),
+      filter(arr => arr.length > 0),
+    )
   }
-  private ngLayers$ = this.store$.pipe(
-    select(ngViewerSelectorLayers),
-    map((ngLayers: INgLayerInterface[]) => {
-      const newLayers = ngLayers.filter(l => {
-        const registeredLayerNames = this.ngLayersRegister.layers.map(l => l.name)
-        return !registeredLayerNames.includes(l.name)
+
+  private updateCustomLayerTransparency$ = this.getUpdatedCustomLayer((o, n) => o.opacity === n.opacity)
+  private updateCustomLayerColorMap$ = this.getUpdatedCustomLayer((o, n) => o.shader === n.shader)
+
+  private ngLayers$ = this.customLayers$.pipe(
+    map(customLayers => customLayers.filter(l => l.clType === "customlayer/nglayer") as atlasAppearance.NgLayerCustomLayer[]),
+    distinctUntilChanged(
+      arrayEqual((o, n) => o.id === n.id)
+    ),
+    map(customLayers => {
+      const newLayers = customLayers.filter(l => {
+        const registeredLayerNames = this.ngLayersRegister.map(l => l.id)
+        return !registeredLayerNames.includes(l.id)
       })
-      const removeLayers = this.ngLayersRegister.layers.filter(l => {
-        const stateLayerNames = ngLayers.map(l => l.name)
-        return !stateLayerNames.includes(l.name)
+      const removeLayers = this.ngLayersRegister.filter(l => {
+        const stateLayerNames = customLayers.map(l => l.id)
+        return !stateLayerNames.includes(l.id)
       })
-      return { newLayers, removeLayers, ngLayers }
+      return { newLayers, removeLayers, customLayers }
     }),
     shareReplay(1)
   )
@@ -431,11 +318,9 @@ export class NehubaLayerControlService implements OnDestroy{
       map(newLayers => {
 
         const newLayersObj: any = {}
-        newLayers.forEach(({ name, source, ...rest }) => newLayersObj[name] = {
+        newLayers.forEach(({ id, source, ...rest }) => newLayersObj[id] = {
           ...rest,
           source,
-          // source: getProxyUrl(source),
-          // ...getProxyOther({source})
         })
   
         return {
@@ -448,14 +333,75 @@ export class NehubaLayerControlService implements OnDestroy{
       map(({ removeLayers }) => removeLayers),
       filter(layers => layers.length > 0),
       map(removeLayers => {
-        const removeLayerNames = removeLayers.map(v => v.name)
+        const removeLayerNames = removeLayers.map(v => v.id)
         return {
           type: 'remove',
           payload: { names: removeLayerNames }
         } as TNgLayerCtrl<'remove'>
       })
     ),
+    this.updateCustomLayerTransparency$.pipe(
+      map(layers => {
+        const payload: Record<string, number> = {}
+        for (const layer of layers) {
+          const opacity = layer.opacity ?? 0.8
+          payload[layer.id] = opacity
+        }
+        return {
+          type: 'setLayerTransparency',
+          payload
+        } as TNgLayerCtrl<'setLayerTransparency'>
+      })
+    ),
+    this.updateCustomLayerColorMap$.pipe(
+      map(layers => {
+        const payload: Record<string, string> = {}
+        for (const layer of layers) {
+          const shader = layer.shader ?? getShader()
+          payload[layer.id] = shader
+        }
+        return {
+          type: 'updateShader',
+          payload
+        } as TNgLayerCtrl<'updateShader'>
+      })
+    ),
     this.manualNgLayersControl$,
   ).pipe(
   )
+
+  public visibleLayer$: Observable<string[]> = combineLatest([
+    this.expectedLayerNames$.pipe(
+      map(expectedLayerNames => {
+        const ngIdSet = new Set<string>([...expectedLayerNames])
+        return Array.from(ngIdSet)
+      })
+    ),
+    this.ngLayers$.pipe(
+      map(({ customLayers }) => customLayers),
+      startWith([] as atlasAppearance.NgLayerCustomLayer[]),
+      map(customLayers => {
+        /**
+         * pmap control has its own visibility controller
+         */
+        return customLayers
+          .map(l => l.id)
+          .filter(name => name !== PMAP_LAYER_NAME)
+      })
+    ),
+    this.customLayers$.pipe(
+      map(cl => {
+        const otherColormapExist = cl.filter(l => l.clType === "customlayer/colormap").length > 0
+        const pmapExist = cl.filter(l => l.clType === "customlayer/nglayer").length > 0
+        return pmapExist && !otherColormapExist
+      }),
+      distinctUntilChanged(),
+      map(flag => flag
+        ? [ PMAP_LAYER_NAME ]
+        : []
+      )
+    )
+  ]).pipe(
+    map(([ expectedLayerNames, customLayerNames, pmapName ]) => [...expectedLayerNames, ...customLayerNames, ...pmapName, ...AnnotationLayer.Map.keys()])
+  )
 }
diff --git a/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.util.ts b/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.util.ts
index f6fc08184e0d31d4ed9229ca6840b9981ac8c4f2..1fa9f52ff34373fdb76dd62ddeb7acffc71ff091 100644
--- a/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.util.ts
+++ b/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.util.ts
@@ -1,6 +1,7 @@
 import { InjectionToken } from '@angular/core'
 import { strToRgb } from 'common/util'
 import { Observable } from 'rxjs'
+import { atlasAppearance } from 'src/state'
 
 export interface IColorMap {
   [key: string]: {
@@ -42,14 +43,17 @@ export interface INgLayerCtrl {
     names: string[]
   }
   add: {
-    [key: string]: INgLayerInterface
+    [key: string]: atlasAppearance.NgLayerCustomLayer
   }
   update: {
-    [key: string]: INgLayerInterface
+    [key: string]: Partial<atlasAppearance.NgLayerCustomLayer>
   }
   setLayerTransparency: {
     [key: string]: number
   }
+  updateShader: {
+    [key: string]: string
+  }
 }
 
 export type TNgLayerCtrl<T extends keyof INgLayerCtrl> = {
@@ -61,14 +65,3 @@ export const SET_COLORMAP_OBS = new InjectionToken<Observable<IColorMap>>('SET_C
 export const SET_LAYER_VISIBILITY = new InjectionToken<Observable<string[]>>('SET_LAYER_VISIBILITY')
 export const SET_SEGMENT_VISIBILITY = new InjectionToken<Observable<string[]>>('SET_SEGMENT_VISIBILITY')
 export const NG_LAYER_CONTROL = new InjectionToken<TNgLayerCtrl<keyof INgLayerCtrl>>('NG_LAYER_CONTROL')
-
-export interface INgLayerInterface {
-  name: string // displayName
-  source: string
-  mixability: string // base | mixable | nonmixable
-  annotation?: string //
-  id?: string // unique identifier
-  visible?: boolean
-  shader?: string
-  transform?: any
-}
diff --git a/src/viewerModule/nehuba/layoutOverlay/index.ts b/src/viewerModule/nehuba/layoutOverlay/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..cd90fbdb2c5cb739e9eeb5759b61a57197100597
--- /dev/null
+++ b/src/viewerModule/nehuba/layoutOverlay/index.ts
@@ -0,0 +1,3 @@
+export {
+  NehubaLayoutOverlayModule
+} from "./module"
diff --git a/src/viewerModule/nehuba/layoutOverlay/module.ts b/src/viewerModule/nehuba/layoutOverlay/module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4160b8d12d401b9acd2d7a0fc65f3af288d9261d
--- /dev/null
+++ b/src/viewerModule/nehuba/layoutOverlay/module.ts
@@ -0,0 +1,35 @@
+import { CommonModule } from "@angular/common";
+import { NgModule } from "@angular/core";
+import { MatMenuModule } from "@angular/material/menu";
+import { ViewerCtrlModule } from "../viewerCtrl";
+import { NehubaLayoutOverlay } from "./nehuba.layoutOverlay/nehuba.layoutOverlay.component";
+import { QuickTourModule } from "src/ui/quickTour";
+import { SpinnerModule } from "src/components/spinner";
+import { UtilModule } from "src/util";
+import { WindowResizeModule } from "src/util/windowResize";
+import { LayoutModule } from "src/layouts/layout.module";
+import { MatButtonModule } from "@angular/material/button";
+import { MatTooltipModule } from "@angular/material/tooltip";
+
+@NgModule({
+  imports: [
+    MatMenuModule,
+    CommonModule,
+    LayoutModule,
+    ViewerCtrlModule,
+    QuickTourModule,
+    SpinnerModule,
+    UtilModule,
+    WindowResizeModule,
+    MatButtonModule,
+    MatTooltipModule,
+  ],
+  declarations: [
+    NehubaLayoutOverlay,
+  ],
+  exports: [
+    NehubaLayoutOverlay
+  ]
+})
+
+export class NehubaLayoutOverlayModule{}
\ No newline at end of file
diff --git a/src/viewerModule/nehuba/layoutOverlay/nehuba.layoutOverlay/nehuba.layoutOverlay.component.ts b/src/viewerModule/nehuba/layoutOverlay/nehuba.layoutOverlay/nehuba.layoutOverlay.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..93af83e0d5f39e69334043acd31657aa18231e11
--- /dev/null
+++ b/src/viewerModule/nehuba/layoutOverlay/nehuba.layoutOverlay/nehuba.layoutOverlay.component.ts
@@ -0,0 +1,304 @@
+import { AfterViewInit, ChangeDetectorRef, Component, Inject, OnDestroy } from "@angular/core";
+import { select, Store } from "@ngrx/store";
+import { combineLatest, fromEvent, interval, merge, Observable, of, Subject, Subscription } from "rxjs";
+import { userInterface } from "src/state";
+import { NehubaViewerUnit } from "../../nehubaViewer/nehubaViewer.component";
+import { NEHUBA_INSTANCE_INJTKN, takeOnePipe, getFourPanel, getHorizontalOneThree, getSinglePanel, getVerticalOneThree } from "../../util";
+import { QUICKTOUR_DESC, ARIA_LABELS, IDS } from 'common/constants'
+import { IQuickTourData } from "src/ui/quickTour/constrants";
+import { debounce, debounceTime, distinctUntilChanged, filter, map, mapTo, switchMap, take } from "rxjs/operators";
+
+@Component({
+  selector: `nehuba-layout-overlay`,
+  templateUrl: `./nehuba.layoutOverlay.template.html`,
+  styleUrls: [
+    `./nehuba.layoutOverlay.style.css`
+  ]
+})
+
+export class NehubaLayoutOverlay implements OnDestroy, AfterViewInit{
+
+  public ARIA_LABELS = ARIA_LABELS
+  public IDS = IDS
+  public currentPanelMode: userInterface.PanelMode = "FOUR_PANEL"
+  public currentOrder: string = '0123'
+  public currentHoveredIndex: number
+
+  public quickTourSliceViewSlide: IQuickTourData = {
+    order: 1,
+    description: QUICKTOUR_DESC.SLICE_VIEW,
+  }
+
+  public quickTour3dViewSlide: IQuickTourData = {
+    order: 2,
+    description: QUICKTOUR_DESC.PERSPECTIVE_VIEW,
+  }
+
+  public quickTourIconsSlide: IQuickTourData = {
+    order: 3,
+    description: QUICKTOUR_DESC.VIEW_ICONS,
+  }
+
+  ngOnDestroy(): void {
+    while(this.subscription.length > 0) this.subscription.pop().unsubscribe()
+    while(this.nehubaUnitSubs.length > 0) this.nehubaUnitSubs.pop().unsubscribe()
+  }
+
+  ngAfterViewInit(): void {
+    this.setQuickTourPos()
+  }
+
+  handleCycleViewEvent(): void {
+    if (this.currentPanelMode !== "SINGLE_PANEL") return
+    this.store$.dispatch(
+      userInterface.actions.cyclePanelMode()
+    )
+  }
+
+  public toggleMaximiseMinimise(index: number): void {
+    this.store$.dispatch(
+      userInterface.actions.toggleMaximiseView({
+        targetIndex: index
+      })
+    )
+  }
+
+  public zoomNgView(panelIndex: number, factor: number): void {
+    const ngviewer = this.nehubaUnit?.nehubaViewer?.ngviewer
+    if (!ngviewer) throw new Error(`ngviewer not defined!`)
+
+    /**
+     * panelIndex < 3 === slice view
+     */
+    if (panelIndex < 3) {
+      /**
+       * factor > 1 === zoom out
+       */
+      ngviewer.navigationState.zoomBy(factor)
+    } else {
+      ngviewer.perspectiveNavigationState.zoomBy(factor)
+    }
+  }
+
+  public quickTourOverwritingPos = {
+    'dialog': {
+      left: '0px',
+      top: '0px',
+    },
+    'arrow': {
+      left: '0px',
+      top: '0px',
+    }
+  }
+
+  setQuickTourPos(): void {
+    const { innerWidth, innerHeight } = window
+    this.quickTourOverwritingPos = {
+      'dialog': {
+        left: `${innerWidth / 2}px`,
+        top: `${innerHeight / 2}px`,
+      },
+      'arrow': {
+        left: `${innerWidth / 2 - 48}px`,
+        top: `${innerHeight / 2 - 48}px`,
+      }
+    }
+  }
+
+  public panelMode$ = this.store$.pipe(
+    select(userInterface.selectors.panelMode)
+  )
+
+  public panelOrder$ = this.store$.pipe(
+    select(userInterface.selectors.panelOrder),
+  )
+
+  public volumeChunkLoading$: Subject<boolean> = new Subject()
+
+  constructor(
+    private store$: Store,
+    private cdr: ChangeDetectorRef,
+    @Inject(NEHUBA_INSTANCE_INJTKN) nehuba$: Observable<NehubaViewerUnit>
+  ){
+    this.subscription.push(
+      nehuba$.subscribe(nehuba => {
+        this.nehubaUnit = nehuba
+        this.onNewNehubaUnit(nehuba)
+      })
+    )
+  }
+
+  private onNewNehubaUnit(nehubaUnit: NehubaViewerUnit){
+    
+    while(this.nehubaUnitSubs.length) this.nehubaUnitSubs.pop().unsubscribe()
+    this.nehubaViewPanels = []
+    this.nanometersToOffsetPixelsFn = []
+
+    if (!nehubaUnit) {
+      return
+    }
+
+    const removeExistingPanels = () => {
+      const element = nehubaUnit.nehubaViewer.ngviewer.layout.container.componentValue.element as HTMLElement
+      while (element.childElementCount > 0) {
+        element.removeChild(element.firstElementChild)
+      }
+      return element
+    }
+
+    this.nehubaUnitSubs.push(
+
+      /**
+       * sliceview loading event
+       */
+      fromEvent<CustomEvent>(
+        nehubaUnit.elementRef.nativeElement,
+        'sliceRenderEvent'
+      ).pipe(
+        map(ev => {
+          const { missingImageChunks, missingChunks } = ev.detail
+          return { missingImageChunks, missingChunks }
+        }),
+        distinctUntilChanged((o, n) => o.missingChunks === n.missingChunks && o.missingImageChunks === n.missingImageChunks)
+      ).subscribe(({ missingImageChunks, missingChunks }) => {
+        this.volumeChunkLoading$.next(
+          missingImageChunks > 0 || missingChunks > 0
+        )
+        this.detectChanges()
+      }),
+
+      /**
+       * map slice view to weakmap
+       */
+      fromEvent<CustomEvent>(
+        nehubaUnit.elementRef.nativeElement,
+        'sliceRenderEvent'
+      ).pipe(
+        takeOnePipe()
+      ).subscribe(ev => {
+        for (const idx of [0, 1, 2]) {
+          const e = ev[idx] as CustomEvent
+          const el = e.target as HTMLElement
+          this.viewPanelWeakMap.set(el, idx)
+          this.nehubaViewPanels[idx] = el
+          this.nanometersToOffsetPixelsFn[idx] = e.detail.nanometersToOffsetPixels
+        }
+      }),
+
+
+      /**
+       * map perspective to weakmap
+       */
+      fromEvent<CustomEvent>(
+        nehubaUnit.elementRef.nativeElement,
+        'perpspectiveRenderEvent'
+      ).pipe(
+        take(1)
+      ).subscribe(ev => {
+        const perspPanel = ev.target as HTMLElement
+        this.nehubaViewPanels[3] = perspPanel
+        this.viewPanelWeakMap.set(perspPanel, 3)
+      }),
+
+      /**
+       * on mouseover, emit hovered panel index
+       */
+      fromEvent(
+        nehubaUnit.elementRef.nativeElement,
+        'mouseover'
+      ).pipe(
+        switchMap((ev: MouseEvent) => merge(
+          of(this.findPanelIndex(ev.target as HTMLElement)),
+          fromEvent(nehubaUnit.elementRef.nativeElement, 'mouseout').pipe(
+            mapTo(null as number),
+          ),
+        )),
+        debounceTime(16),
+      ).subscribe(val => {
+        this.currentHoveredIndex = val
+        this.detectChanges()
+      }),
+
+      /**
+       * on store change layout, update layout on nehuba
+       */
+      combineLatest([
+        this.panelMode$,
+        this.panelOrder$,
+      ]).pipe(
+        debounce(() => 
+          nehubaUnit?.nehubaViewer?.ngviewer
+            ? of(true)
+            : interval(16).pipe(
+              filter(() => nehubaUnit?.nehubaViewer?.ngviewer),
+              take(1)
+            )
+        )
+      ).subscribe(([mode, panelOrder]) => {
+        
+        this.currentPanelMode = mode as userInterface.PanelMode
+        this.currentOrder = panelOrder
+
+        const viewPanels = panelOrder.split('').map(v => Number(v)).map(idx => this.nehubaViewPanels[idx]) as [HTMLElement, HTMLElement, HTMLElement, HTMLElement]
+  
+        /**
+         * TODO smarter with event stream
+         */
+        if (!viewPanels.every(v => !!v)) {
+          return
+        }
+  
+        switch (this.currentPanelMode) {
+        case "H_ONE_THREE": {
+          const element = removeExistingPanels()
+          const newEl = getHorizontalOneThree(viewPanels)
+          element.appendChild(newEl)
+          break;
+        }
+        case "V_ONE_THREE": {
+          const element = removeExistingPanels()
+          const newEl = getVerticalOneThree(viewPanels)
+          element.appendChild(newEl)
+          break;
+        }
+        case "FOUR_PANEL": {
+          const element = removeExistingPanels()
+          const newEl = getFourPanel(viewPanels)
+          element.appendChild(newEl)
+          break;
+        }
+        case "SINGLE_PANEL": {
+          const element = removeExistingPanels()
+          const newEl = getSinglePanel(viewPanels)
+          element.appendChild(newEl)
+          break;
+        }
+        default:
+        }
+        for (const panel of viewPanels) {
+          (panel as HTMLElement).classList.add('neuroglancer-panel')
+        }
+  
+        this.detectChanges()
+        nehubaUnit.redraw()
+      })
+    )
+
+    this.detectChanges()
+  }
+
+  public detectChanges(): void {
+    this.cdr.detectChanges()
+  }
+  
+  private nehubaUnit: NehubaViewerUnit
+
+  private findPanelIndex = (panel: HTMLElement) => this.viewPanelWeakMap.get(panel)
+  private viewPanelWeakMap = new WeakMap<HTMLElement, number>()
+  private nehubaViewPanels: HTMLElement[] = []
+
+  private subscription: Subscription[] = []
+  private nehubaUnitSubs: Subscription[] = []
+
+  private nanometersToOffsetPixelsFn: ((...arg: any[]) => any)[] = []
+}
\ No newline at end of file
diff --git a/src/viewerModule/nehuba/layoutOverlay/nehuba.layoutOverlay/nehuba.layoutOverlay.style.css b/src/viewerModule/nehuba/layoutOverlay/nehuba.layoutOverlay/nehuba.layoutOverlay.style.css
new file mode 100644
index 0000000000000000000000000000000000000000..beea6a73ad8a3e22daa03c55a5d6022f175edda4
--- /dev/null
+++ b/src/viewerModule/nehuba/layoutOverlay/nehuba.layoutOverlay/nehuba.layoutOverlay.style.css
@@ -0,0 +1,41 @@
+:host
+{
+  pointer-events: none;
+}
+
+current-layout
+{
+  display: block;
+  width: 100%;
+  height: 100%;
+  pointer-events: none;
+  left: 0;
+  top: 0;
+}
+
+.layout-cell
+{
+  width: 100%;
+  height: 100%;
+}
+
+.opacity-crossfade
+{
+  transition: opacity 170ms ease-in-out,
+    transform 250ms ease-in-out;
+}
+
+.opacity-crossfade
+{
+
+  opacity: 0.0;
+  pointer-events: none;
+}
+
+.opacity-crossfade.onHover,
+.opacity-crossfade:hover,
+:host-context([ismobile="true"]) .opacity-crossfade.always-show-touchdevice
+{
+  opacity: 1.0 !important;
+  pointer-events: all !important;
+}
diff --git a/src/viewerModule/nehuba/layoutOverlay/nehuba.layoutOverlay/nehuba.layoutOverlay.template.html b/src/viewerModule/nehuba/layoutOverlay/nehuba.layoutOverlay/nehuba.layoutOverlay.template.html
new file mode 100644
index 0000000000000000000000000000000000000000..e716c29fac236a4c7394b76a68a5027ffaf94646
--- /dev/null
+++ b/src/viewerModule/nehuba/layoutOverlay/nehuba.layoutOverlay/nehuba.layoutOverlay.template.html
@@ -0,0 +1,152 @@
+<current-layout
+  [current-layout-use-layout]="currentPanelMode"
+  [iav-key-listener]="[{ type: 'keydown', key: ' ', target: 'document', capture: true }]"
+  (iav-key-event)="handleCycleViewEvent()">
+  <div class="layout-cell" cell-i
+    iav-window-resize
+    [iav-window-resize-time]="64"
+    (iav-window-resize-event)="setQuickTourPos()"
+    quick-tour
+    [quick-tour-description]="quickTourSliceViewSlide.description"
+    [quick-tour-order]="quickTourSliceViewSlide.order"
+    [quick-tour-overwrite-arrow]="sliceViewArrow"
+    [quick-tour-overwrite-position]="quickTourOverwritingPos">
+    <ng-content *ngTemplateOutlet="ngPanelOverlayTmpl; context: { panelIndex: currentOrder | getProperty : 0 | parseAsNumber }"></ng-content>
+  </div>
+  <div class="layout-cell" cell-ii>
+    <ng-content *ngTemplateOutlet="ngPanelOverlayTmpl; context: { panelIndex: currentOrder | getProperty : 1 | parseAsNumber }"></ng-content>
+  </div>
+  <div class="layout-cell" cell-iii>
+    <ng-content *ngTemplateOutlet="ngPanelOverlayTmpl; context: { panelIndex: currentOrder | getProperty : 2 | parseAsNumber }"></ng-content>
+  </div>
+  <div class="layout-cell" cell-iv
+    quick-tour
+    [quick-tour-description]="quickTour3dViewSlide.description"
+    [quick-tour-order]="quickTour3dViewSlide.order">
+    <ng-content *ngTemplateOutlet="ngPanelOverlayTmpl; context: { panelIndex: currentOrder | getProperty : 3 | parseAsNumber }"></ng-content>
+  </div>
+</current-layout>
+
+<!-- slice view overlay tmpl -->
+<ng-template #ngPanelOverlayTmpl let-panelIndex="panelIndex">
+
+  <!-- perspective view tmpl -->
+  <ng-template #overlayPerspectiveTmpl>
+    
+      <!-- mesh loading is still weird -->
+      <!-- if the precomputed server does not have the necessary fragment file, then the numberws will not collate -->
+      <!-- TODO -->
+  </ng-template>
+
+  <iav-layout-fourcorners class="w-100 h-100 d-block">
+
+    <!-- panel controller -->
+    <div iavLayoutFourCornersBottomRight class="position-relative honing">
+
+      <ng-container *ngTemplateOutlet="panelCtrlTmpl; context: {
+        panelIndex: panelIndex,
+        visible: currentHoveredIndex === panelIndex
+      }">
+      </ng-container>
+      
+      <div *ngIf="volumeChunkLoading$ | async"
+        class="position-absolute bottom-0 right-0">
+        <spinner-cmp></spinner-cmp>
+      </div>
+    </div>
+  </iav-layout-fourcorners>
+
+</ng-template>
+
+<!-- panel control template -->
+<ng-template
+  #panelCtrlTmpl
+  let-panelIndex="panelIndex"
+  let-visible="visible">
+  <div class="ws-no-wrap opacity-crossfade always-show-touchdevice pe-all overlay-btn-container"
+    [ngClass]="{ onHover: visible }"
+    [attr.data-viewer-controller-visible]="visible"
+    [attr.data-viewer-controller-index]="panelIndex">
+
+    <div class="position-absolute w-100 h-100 pe-none"
+      *ngIf="panelIndex === 1"
+      quick-tour
+      [quick-tour-description]="quickTourIconsSlide.description"
+      [quick-tour-order]="quickTourIconsSlide.order">
+    </div>
+
+    <!-- perspective specific control -->
+    <ng-container *ngIf="panelIndex === 3">
+
+      <button mat-icon-button color="primary"
+        (click)="detectChanges()"
+        [matMenuTriggerFor]="viewerCtrlMenu">
+        <i class="fas fa-cog"></i>
+      </button>
+
+    </ng-container>
+
+    <!-- factor < 1.0 === zoom in -->
+    <button mat-icon-button color="primary"
+      (click)="zoomNgView(panelIndex, 0.9)"
+      [attr.aria-label]="ARIA_LABELS.ZOOM_IN">
+      <i class="fas fa-search-plus"></i>
+    </button>
+
+    <!-- factor > 1.0 === zoom out -->
+    <button mat-icon-button color="primary"
+      (click)="zoomNgView(panelIndex, 1.1)"
+      [attr.aria-label]="ARIA_LABELS.ZOOM_OUT">
+      <i class="fas fa-search-minus"></i>
+    </button>
+
+    <ng-template
+      [ngTemplateOutlet]="maxBtnTmpl"
+      [ngTemplateOutletContext]="{ $implicit: panelIndex }">
+    </ng-template>
+  </div>
+</ng-template>
+
+<ng-template #maxBtnTmpl let-panelIndex>
+  <button
+    mat-icon-button
+    color="primary"
+    (click)="toggleMaximiseMinimise(panelIndex)">
+    <ng-template
+      [ngIf]="currentPanelMode === 'SINGLE_PANEL'"
+      [ngIfElse]="expandTmpl">
+      <i class="fas fa-compress"></i>
+    </ng-template>
+
+    <ng-template #expandTmpl>
+      <i class="fas fa-expand"></i>
+    </ng-template>
+  </button>
+</ng-template>
+
+
+<!-- viewer ctrl -->
+<mat-menu #viewerCtrlMenu>
+  <viewer-ctrl-component
+    class="d-block m-2 ml-3 mr-3"
+    (click)="$event.stopPropagation()">
+  </viewer-ctrl-component>
+</mat-menu>
+
+<ng-template #sliceViewArrow>
+  <svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
+    <path id="quarter_circle" d="M22.6151 96.5C22.6151 96.5 18.5 84.1266 18.5 76.5C18.5001 62 18.1151 59.5 22.6151 47C27.115 34.5 39.3315 27.7229 47.5 25C56.5 22 63 22.5 72.5 24C83.1615 25.6834 83.5 26 91 29" />
+    <g id="arrow_top_left">
+      <path id="arrow_stem" d="M37 40C35.5882 38.5882 17.6863 20.6863 12 15" />
+      <path id="arrow_head" d="M6 24C6.38926 21.7912 6.68496 18.3286 6.71205 16.0803C6.73751 13.9665 6.632 13.6135 6.52632 11.5C6.46368 10.2469 6.52632 11.5 6 8C11 10 9.71916 9.74382 11 9.99999C13.5 10.5 13.743 10.7451 17 11C20 11.2348 21.1276 11 22 11"  stroke-linecap="round" stroke-linejoin="round"/>
+    </g>
+    <g id="arrow_left">
+      <path id="arrow_stem_2" d="M29.4229 78.5771C27.1573 78.5771 18.3177 78.5771 9.19238 78.5771" />
+      <path id="arrow_head_2" d="M13.3137 89.6274C12.0271 87.7903 9.78778 85.1328 8.2171 83.5238C6.74048 82.0112 6.41626 81.8362 4.84703 80.4164C3.91668 79.5747 4.84703 80.4164 2 78.3137C6.94975 76.1924 5.86291 76.9169 6.94974 76.1924C9.07106 74.7782 9.41624 74.7797 11.8995 72.6569C14.1868 70.7016 14.8181 69.7382 15.435 69.1213"  stroke-linecap="round" stroke-linejoin="round"/>
+    </g>
+    <g id="arrow_top">
+      <path id="arrow_stem_3" d="M77.0057 32.0057C77.0057 30.3124 77.0057 16.2488 77.0057 9.42862" />
+      <path id="arrow_head_3" d="M68.4342 11.1429C69.9189 10.1032 72.0665 8.29351 73.3667 7.02421C74.5891 5.83091 74.7305 5.5689 75.8779 4.30076C76.5581 3.54892 75.8779 4.30076 77.5771 2C79.2914 6.00002 78.7059 5.12172 79.2915 6.00002C80.4343 7.71431 80.4331 7.99326 82.1486 10C83.7287 11.8485 84.5072 12.3587 85.0058 12.8572"  stroke-linecap="round" stroke-linejoin="round"/>
+    </g>
+  </svg>
+</ng-template>
\ No newline at end of file
diff --git a/src/viewerModule/nehuba/maximisePanelButton/maximisePanelButton.component.ts b/src/viewerModule/nehuba/maximisePanelButton/maximisePanelButton.component.ts
deleted file mode 100644
index 7aab44ac4156b05454a3d7bce915068cdf24a07e..0000000000000000000000000000000000000000
--- a/src/viewerModule/nehuba/maximisePanelButton/maximisePanelButton.component.ts
+++ /dev/null
@@ -1,51 +0,0 @@
-import { Component, Input } from "@angular/core";
-import { select, Store } from "@ngrx/store";
-import { Observable } from "rxjs";
-import { distinctUntilChanged, map } from "rxjs/operators";
-import { PANELS } from 'src/services/state/ngViewerState.store.helper'
-import { ARIA_LABELS } from 'common/constants'
-import { ngViewerSelectorPanelMode, ngViewerSelectorPanelOrder } from "src/services/state/ngViewerState/selectors";
-
-const {
-  MAXIMISE_VIEW,
-  UNMAXIMISE_VIEW,
-} = ARIA_LABELS
-
-@Component({
-  selector: 'maximise-panel-button',
-  templateUrl: './maximisePanelButton.template.html',
-  styleUrls: [
-    './maximisePanelButton.style.css',
-  ],
-})
-
-export class MaximisePanelButton {
-
-  public ARIA_LABEL_MAXIMISE_VIEW = MAXIMISE_VIEW
-  public ARIA_LABEL_UNMAXIMISE_VIEW = UNMAXIMISE_VIEW
-
-  @Input() public panelIndex: number
-
-  private panelMode$: Observable<string>
-  private panelOrder$: Observable<string>
-
-  public isMaximised$: Observable<boolean>
-
-  constructor(
-    private store$: Store<any>,
-  ) {
-    this.panelMode$ = this.store$.pipe(
-      select(ngViewerSelectorPanelMode),
-      distinctUntilChanged(),
-    )
-
-    this.panelOrder$ = this.store$.pipe(
-      select(ngViewerSelectorPanelOrder),
-      distinctUntilChanged(),
-    )
-
-    this.isMaximised$ = this.panelMode$.pipe(
-      map(panelMode => panelMode === PANELS.SINGLE_PANEL),
-    )
-  }
-}
diff --git a/src/viewerModule/nehuba/maximisePanelButton/maximisePanelButton.template.html b/src/viewerModule/nehuba/maximisePanelButton/maximisePanelButton.template.html
deleted file mode 100644
index 03cf3cd01e57071b42cb3234dfadfe6eab5f34b1..0000000000000000000000000000000000000000
--- a/src/viewerModule/nehuba/maximisePanelButton/maximisePanelButton.template.html
+++ /dev/null
@@ -1,17 +0,0 @@
-<ng-content *ngTemplateOutlet="maximiseBtnTmpl; context: { $implicit: (isMaximised$ | async) }">
-</ng-content>
-
-<ng-template #maximiseBtnTmpl let-ismaximised>
-
-  <button
-    [matTooltip]="ismaximised ? 'Restore four panel view' : 'Maximise this panel'"
-    mat-icon-button
-    [attr.aria-label]="ismaximised ? ARIA_LABEL_UNMAXIMISE_VIEW : ARIA_LABEL_MAXIMISE_VIEW"
-    color="primary">
-    <i *ngIf="ismaximised; else expandIconTemplate" class="fas fa-compress"></i>
-  </button>
-
-  <ng-template #expandIconTemplate>
-    <i class="fas fa-expand"></i>
-  </ng-template>
-</ng-template>
diff --git a/src/viewerModule/nehuba/mesh.effects/mesh.effects.ts b/src/viewerModule/nehuba/mesh.effects/mesh.effects.ts
new file mode 100644
index 0000000000000000000000000000000000000000..74a109766a28373517447d90cea0fd804883a16d
--- /dev/null
+++ b/src/viewerModule/nehuba/mesh.effects/mesh.effects.ts
@@ -0,0 +1,49 @@
+import { Injectable } from "@angular/core";
+import { createEffect } from "@ngrx/effects";
+import { Store } from "@ngrx/store";
+import { map, mapTo } from "rxjs/operators";
+import { actionSetAuxMeshes, IAuxMesh } from "../store";
+import { atlasSelection } from "src/state"
+import { LayerCtrlEffects } from "../layerCtrl.service/layerCtrl.effects";
+
+@Injectable()
+export class MeshEffects{
+  constructor(
+    private store: Store<any>,
+    private effect: LayerCtrlEffects,
+  ){
+
+  }
+
+  onATPSelectResetAuxMeshes = createEffect(() => this.store.pipe(
+    atlasSelection.fromRootStore.distinctATP(),
+    mapTo(
+      actionSetAuxMeshes({
+        payload: []
+      })
+    )
+  ))
+
+  onNgLayersEmitSetAuxMeshes = createEffect(() => this.effect.onATPDebounceNgLayers$.pipe(
+    map(({ tmplAuxNgLayers }) => {
+      const newAuxMeshes: IAuxMesh[] = []
+      for (const key in tmplAuxNgLayers) {
+        
+        for (const mesh of tmplAuxNgLayers[key].auxMeshes) {
+          newAuxMeshes.push({
+            "@id": `aux-mesh-${mesh.name}`,
+            ngId: key,
+            labelIndicies: mesh.labelIndicies,
+            name: mesh.name,
+            rgb: [255, 255, 255],
+            visible: true,
+            displayName: mesh.name
+          })
+        }
+      }
+      return actionSetAuxMeshes({
+        payload: newAuxMeshes
+      })
+    })
+  ))
+}
\ No newline at end of file
diff --git a/src/viewerModule/nehuba/mesh.service/mesh.service.spec.ts b/src/viewerModule/nehuba/mesh.service/mesh.service.spec.ts
index 967762e46acb61182a31dfc625772c14da8a2dca..ef3ac53320fbfa6ee86ec80ac140c105a2534af8 100644
--- a/src/viewerModule/nehuba/mesh.service/mesh.service.spec.ts
+++ b/src/viewerModule/nehuba/mesh.service/mesh.service.spec.ts
@@ -1,134 +1,76 @@
 import { TestBed } from "@angular/core/testing"
 import { MockStore, provideMockStore } from "@ngrx/store/testing"
 import { hot } from "jasmine-marbles"
-import { viewerStateSelectedParcellationSelector, viewerStateSelectedRegionsSelector, viewerStateSelectedTemplateSelector } from "src/services/state/viewerState/selectors"
+import { NehubaMeshService } from "./mesh.service"
+import { atlasSelection } from "src/state"
+import { SapiRegionModel } from "src/atlasComponents/sapi"
+import * as configSvc from "../config.service"
+import { LayerCtrlEffects } from "../layerCtrl.service/layerCtrl.effects"
+import { NEVER, of, pipe } from "rxjs"
+import { mapTo, take } from "rxjs/operators"
 import { selectorAuxMeshes } from "../store"
-import { getLayerNameIndiciesFromParcRs, collateLayerNameIndicies, findFirstChildrenWithLabelIndex, NehubaMeshService } from "./mesh.service"
 
 
-const fits1 = {
-  ngId: 'foobar',
-  labelIndex: 123,
-  children: []
+const fits1 = {} as SapiRegionModel
+const auxMesh = {
+  "@id": 'bla',
+  labelIndicies: [1,2,3],
+  name: 'bla',
+  ngId: 'bla',
+  rgb: [255, 255, 255] as [number, number, number],
+  visible: true,
+  displayName: 'bla'
 }
 
-const fits1_1 = {
-  ngId: 'foobar',
-  labelIndex: 5,
-  children: []
-}
-
-const fits2 = {
-  ngId: 'helloworld',
-  labelIndex: 567,
-  children: []
-}
-
-const fits2_1 = {
-  ngId: 'helloworld',
-  labelIndex: 11,
-  children: []
-}
-
-const nofit1 = {
-  ngId: 'bazz',
-  children: []
-}
-
-const nofit2 = {
-  ngId: 'but',
-  children: []
-}
-
-describe('> mesh.server.ts', () => {
-  describe('> findFirstChildrenWithLabelIndex', () => {
-    it('> if root fits, return root', () => {
-      const result = findFirstChildrenWithLabelIndex({
-        ...fits1,
-        children: [fits2]
-      })
-
-      expect(result).toEqual([{
-        ...fits1,
-        children: [fits2]
-      }])
-    })
-
-    it('> if root doesnt fit, will try to find the next node, until one fits', () => {
-      const result = findFirstChildrenWithLabelIndex({
-        ...nofit1,
-        children: [fits1, fits2]
-      })
-      expect(result).toEqual([fits1, fits2])
-    })
-
-    it('> if notthings fits, will return empty array', () => {
-      const result = findFirstChildrenWithLabelIndex({
-        ...nofit1,
-        children: [nofit1, nofit2]
-      })
-      expect(result).toEqual([])
-    })
+describe('> mesh.service.ts', () => {
+  let getParcNgIdSpy: jasmine.Spy = jasmine.createSpy('getParcNgId')
+  let getRegionLabelIndexSpy: jasmine.Spy = jasmine.createSpy('getRegionLabelIndexSpy')
+  let getATPSpy: jasmine.Spy = jasmine.createSpy('distinctATP')
+
+  const mockAtlas = {
+    '@id': 'mockAtlas'
+  }
+  const mockTmpl = {
+    '@id': 'mockTmpl'
+  }
+  const mockParc = {
+    '@id': 'mockParc'
+  }
+
+  beforeEach(() => {
+    spyOnProperty(configSvc, 'getParcNgId').and.returnValue(getParcNgIdSpy)
+    spyOnProperty(configSvc, 'getRegionLabelIndex').and.returnValue(getRegionLabelIndexSpy)
+    getATPSpy = spyOn(atlasSelection.fromRootStore, 'distinctATP')
+    getATPSpy.and.returnValue(
+      pipe(
+        mapTo({
+          atlas: mockAtlas,
+          parcellation: mockParc,
+          template: mockTmpl
+        })
+      )
+    )
   })
 
-  describe('> collateLayerNameIndicies', () => {
-    it('> collates same ngIds', () => {
-      const result = collateLayerNameIndicies([
-        fits1_1, fits1, fits2, fits2_1
-      ])
-      expect(result).toEqual({
-        [fits1.ngId]: [fits1_1.labelIndex, fits1.labelIndex],
-        [fits2.ngId]: [fits2.labelIndex, fits2_1.labelIndex]
-      })
-    })
+  afterEach(() => {
+    getParcNgIdSpy.calls.reset()
+    getRegionLabelIndexSpy.calls.reset()
+    getATPSpy.calls.reset()
   })
-
-  describe('> getLayerNameIndiciesFromParcRs', () => {
-    const root = {
-      ...fits1,
-      children: [
-        {
-          ...nofit1,
-          children: [
-            {
-              ...fits1_1,
-              children: [
-                fits2, fits2_1
-              ]
-            }
-          ]
-        }
-      ]
-    }
-    const parc = {
-      regions: [ root ]
-    }
-    it('> if selectedRegion.length === 0, selects top most regions with labelIndex', () => {
-      const result = getLayerNameIndiciesFromParcRs(parc, [])
-      expect(result).toEqual({
-        [root.ngId]: [root.labelIndex]
-      })
-    })
-
-    it('> if selReg.length !== 0, select region ngId & labelIndex', () => {
-      const result = getLayerNameIndiciesFromParcRs(parc, [ fits1_1 ])
-      expect(result).toEqual({
-        [fits1_1.ngId]: [fits1_1.labelIndex]
-      })
-    })
-  })
-
   describe('> NehubaMeshService', () => {
     beforeEach(() => {
       TestBed.configureTestingModule({
         providers: [
           provideMockStore(),
           NehubaMeshService,
+          {
+            provide: LayerCtrlEffects,
+            useValue: {
+              onATPDebounceNgLayers$: NEVER
+            }
+          }
         ]
       })
-      const mockStore = TestBed.inject(MockStore)
-      mockStore.overrideSelector(viewerStateSelectedParcellationSelector, {})
-      mockStore.overrideSelector(viewerStateSelectedTemplateSelector, {})
     })
 
     it('> can be init', () => {
@@ -136,39 +78,106 @@ describe('> mesh.server.ts', () => {
       expect(service).toBeTruthy()
     })
 
-    it('> mixes in auxillaryMeshIndices', () => {
-      const mockStore = TestBed.inject(MockStore)
-      mockStore.overrideSelector(viewerStateSelectedRegionsSelector, [ fits1 ])
+    describe("> loadMeshes$", () => {
 
-      mockStore.overrideSelector(selectorAuxMeshes, [{
-        ngId: fits2.ngId,
-        labelIndicies: [11, 22],
-        "@id": '',
-        name: '',
-        rgb: [100, 100, 100],
-        visible: true,
-        displayName: ''
-      }])
+      describe("> auxMesh defined", () => {
+
+        const ngId = 'blabla'
+        const labelIndex = 12
+
+        beforeEach(() => {
+
+          const mockStore = TestBed.inject(MockStore)
+          mockStore.overrideSelector(atlasSelection.selectors.selectedRegions, [ fits1 ])
+          mockStore.overrideSelector(atlasSelection.selectors.selectedParcAllRegions, [])
+          mockStore.overrideSelector(selectorAuxMeshes, [auxMesh])
+    
+          getParcNgIdSpy.and.returnValue(ngId)
+          getRegionLabelIndexSpy.and.returnValue(labelIndex)
 
-      const service = TestBed.inject(NehubaMeshService)
-      expect(
-        service.loadMeshes$
-      ).toBeObservable(
-        hot('(ab)', {
-          a: {
-            layer: {
-              name: fits1.ngId
-            },
-            labelIndicies: [ fits1.labelIndex ]
-          },
-          b: {
-            layer: {
-              name: fits2.ngId,
-            },
-            labelIndicies: [11, 22]
-          }
         })
-      )
+
+        it("> auxMesh ngId labelIndex emitted", () => {
+
+          const service = TestBed.inject(NehubaMeshService)
+          expect(
+            service.loadMeshes$
+          ).toBeObservable(
+            hot('(ab)', {
+              a: {
+                layer: {
+                  name: ngId
+                },
+                labelIndicies: [ labelIndex ]
+              },
+              b: {
+                layer: {
+                  name: auxMesh.ngId,
+                },
+                labelIndicies: auxMesh.labelIndicies
+              }
+            })
+          )
+        })
+      })
+
+      describe("> if multiple ngid and labelindicies are present", () => {
+
+        const ngId1 = 'blabla'
+        const labelIndex1 = 12
+
+        const ngId2 = 'foobar'
+        const labelIndex2 = 13
+
+        beforeEach(() => {
+
+          const mockStore = TestBed.inject(MockStore)
+          mockStore.overrideSelector(atlasSelection.selectors.selectedRegions, [ fits1 ])
+          mockStore.overrideSelector(atlasSelection.selectors.selectedParcAllRegions, [fits1, fits1])
+          mockStore.overrideSelector(selectorAuxMeshes, [])
+    
+          getParcNgIdSpy.and.returnValues(ngId1, ngId2, ngId2)
+          getRegionLabelIndexSpy.and.returnValues(labelIndex1, labelIndex2, labelIndex2)
+        })
+
+        it('> should call getParcNgIdSpy and getRegionLabelIndexSpy thrice', () => {
+          const service = TestBed.inject(NehubaMeshService)
+          service.loadMeshes$.pipe(
+            take(1)
+          ).subscribe(() => {
+
+            expect(getParcNgIdSpy).toHaveBeenCalledTimes(3)
+            expect(getRegionLabelIndexSpy).toHaveBeenCalledTimes(3)
+          })
+        })
+
+        /**
+         * in the case of julich brain 2.9 in colin 27, we expect selecting a region will hide meshes from all relevant ngIds (both left and right)
+         */
+        it('> expect the emitted value to be incl all ngIds', () => {
+          const service = TestBed.inject(NehubaMeshService)
+          expect(
+            service.loadMeshes$
+          ).toBeObservable(
+            hot('(ab)', {
+              a: {
+                layer: {
+                  name: ngId1
+                },
+                labelIndicies: []
+              },
+              b: {
+                layer: {
+                  name: ngId2
+                },
+                labelIndicies: [ labelIndex2 ]
+              }
+            })
+          )
+
+        })
+      })
+
     })
   })
 })
diff --git a/src/viewerModule/nehuba/mesh.service/mesh.service.ts b/src/viewerModule/nehuba/mesh.service/mesh.service.ts
index dd17226af19ad4c3ec2dfc8f10dbc79a96e53635..d372ce460746d01c5cf560518e1532f38616b882 100644
--- a/src/viewerModule/nehuba/mesh.service/mesh.service.ts
+++ b/src/viewerModule/nehuba/mesh.service/mesh.service.ts
@@ -1,56 +1,13 @@
 import { Injectable, OnDestroy } from "@angular/core";
 import { select, Store } from "@ngrx/store";
-import { combineLatest, Observable, of } from "rxjs";
-import { switchMap } from "rxjs/operators";
-import { viewerStateSelectedParcellationSelector, viewerStateSelectedRegionsSelector, viewerStateSelectedTemplateSelector } from "src/services/state/viewerState/selectors";
+import { combineLatest, merge, Observable, of } from "rxjs";
+import { map, switchMap } from "rxjs/operators";
 import { IMeshesToLoad } from '../constants'
-import { flattenReducer } from 'common/util'
-import { IAuxMesh, selectorAuxMeshes, actionSetAuxMeshes } from "../store";
-
-interface IRegion {
-  ngId?: string
-  labelIndex?: number
-  children: IRegion[]
-}
-
-interface IParc {
-  ngId?: string
-  regions: IRegion[]
-}
-
-type TCollatedLayerNameIdx = {
-  [key: string]: number[]
-}
-
-export function findFirstChildrenWithLabelIndex(region: IRegion): IRegion[]{
-  if (region.ngId && region.labelIndex) {
-    return [ region ]
-  }
-  return region.children
-    .map(findFirstChildrenWithLabelIndex)
-    .reduce(flattenReducer, [])
-}
-
-export function collateLayerNameIndicies(regions: IRegion[]){
-  const returnObj: TCollatedLayerNameIdx = {}
-  for (const r of regions) {
-    if (returnObj[r.ngId]) {
-      returnObj[r.ngId].push(r.labelIndex)
-    } else {
-      returnObj[r.ngId] = [r.labelIndex]
-    }
-  }
-  return returnObj
-}
-
-export function getLayerNameIndiciesFromParcRs(parc: IParc, rs: IRegion[]): TCollatedLayerNameIdx {
-
-  const arrOfRegions = (rs.length === 0 ? parc.regions : rs)
-    .map(findFirstChildrenWithLabelIndex)
-    .reduce(flattenReducer, []) as IRegion[]
-
-  return collateLayerNameIndicies(arrOfRegions)
-}
+import { selectorAuxMeshes } from "../store";
+import { LayerCtrlEffects } from "../layerCtrl.service/layerCtrl.effects";
+import { atlasSelection } from "src/state";
+import { Tree } from "src/components/flatHierarchy/treeView/treeControl"
+import { getParcNgId, getRegionLabelIndex } from "../config.service";
 
 /**
  * control mesh loading etc
@@ -62,98 +19,113 @@ export class NehubaMeshService implements OnDestroy {
   private onDestroyCb: (() => void)[] = []
 
   constructor(
-    private store$: Store<any>
+    private store$: Store<any>,
+    private effect: LayerCtrlEffects,
   ){
-    const auxMeshSub = combineLatest([
-      this.selectedTemplate$,
-      this.selectedParc$
-    ]).subscribe(([ tmpl, parc ]) => {
-      const { auxMeshes: tmplAuxMeshes = [] as IAuxMesh[] } = tmpl || {}
-      const { auxMeshes: parcAuxMeshes = [] as IAuxMesh[]} = parc || {}
-      this.store$.dispatch(
-        actionSetAuxMeshes({
-          payload: [...tmplAuxMeshes, ...parcAuxMeshes]
-        })
-      )
-    })
-    this.onDestroyCb.push(() => auxMeshSub.unsubscribe())
   }
 
-  ngOnDestroy(){
+  ngOnDestroy(): void {
     while(this.onDestroyCb.length > 0) this.onDestroyCb.pop()()
   }
 
-  private selectedTemplate$ = this.store$.pipe(
-    select(viewerStateSelectedTemplateSelector)
-  )
-
-  private selectedRegions$ = this.store$.pipe(
-    select(viewerStateSelectedRegionsSelector)
-  )
-
-  private selectedParc$ = this.store$.pipe(
-    select(viewerStateSelectedParcellationSelector)
-  )
 
-  private auxMeshes$ = this.store$.pipe(
-    select(selectorAuxMeshes),
+  public auxMeshes$ = this.effect.onATPDebounceNgLayers$.pipe(
+    map(({ tmplAuxNgLayers }) => tmplAuxNgLayers)
   )
 
-  public loadMeshes$: Observable<IMeshesToLoad> = combineLatest([
-    this.auxMeshes$,
-    this.selectedTemplate$,
-    this.selectedParc$,
-    this.selectedRegions$,
-  ]).pipe(
-    switchMap(([auxMeshes, template, parc, selRegions]) => {
-      
-      /**
-       * if colin 27 and julich brain 2.9.0, select all regions
-       */
-      let overrideSelRegion = null
-      if (
-        template['@id'] === 'minds/core/referencespace/v1.0.0/7f39f7be-445b-47c0-9791-e971c0b6d992' &&
-        parc['@id'] === 'minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579-290'
-      ) {
-        overrideSelRegion = []
-      }
-
-      const obj = getLayerNameIndiciesFromParcRs(parc, overrideSelRegion || selRegions)
-      const { auxillaryMeshIndices = [] } = parc
-      const arr: IMeshesToLoad[] = []
-      for (const key in obj) {
-        const labelIndicies = Array.from(new Set([...obj[key], ...auxillaryMeshIndices]))
-        arr.push({
-          layer: {
-            name: key
-          },
-          labelIndicies
-        })
-      }
-      
-      const auxLayers: {
-        [key: string]: number[]
-      } = {}
+  public loadMeshes$: Observable<IMeshesToLoad> = merge(
+    combineLatest([
+      this.store$.pipe(
+        atlasSelection.fromRootStore.distinctATP(),
+      ),
+      this.store$.pipe(
+        select(atlasSelection.selectors.selectedParcAllRegions),
+      ),
+      this.store$.pipe(
+        select(atlasSelection.selectors.selectedRegions),
+      )
+    ]).pipe(
+      switchMap(([{ atlas, template, parcellation }, regions, selectedRegions]) => {
+        const ngIdRecord: Record<string, number[]> = {}
+        
+        const tree = new Tree(
+          regions,
+          (c, p) => (c.hasParent || []).some(_p => _p["@id"] === p["@id"])
+        )
+
+        for (const r of regions) {
+          const regionLabelIndex = getRegionLabelIndex( atlas, template, parcellation, r )
+          if (!regionLabelIndex) {
+            continue
+          }
+          if (
+            tree.someAncestor(r, anc => !!getRegionLabelIndex(atlas, template, parcellation, anc))
+          ) {
+            continue
+          }
+          const ngId = getParcNgId(atlas, template, parcellation, r)
+          if (!ngIdRecord[ngId]) {
+            ngIdRecord[ngId] = []
+          }
+          ngIdRecord[ngId].push(regionLabelIndex)
+        }
 
-      for (const auxMesh of auxMeshes) {
-        const { name, ngId, labelIndicies } = auxMesh
-        if (!auxLayers[ngId]) {
-          auxLayers[ngId] = []
+        if (selectedRegions.length > 0) {
+          /**
+           * If regions are selected, reset the meshes
+           */
+          for (const key in ngIdRecord) {
+            ngIdRecord[key] = []
+          }
+
+          /**
+           * only show selected region
+           */
+          for (const r of selectedRegions) {
+            const ngId = getParcNgId(atlas, template, parcellation, r)
+            const regionLabelIndex = getRegionLabelIndex( atlas, template, parcellation, r )
+            if (!ngIdRecord[ngId]) {
+              ngIdRecord[ngId] = []
+            }
+            ngIdRecord[ngId].push(regionLabelIndex)
+          }
         }
-        if (auxMesh.visible) {
-          auxLayers[ngId].push(...labelIndicies)
+        const arr: IMeshesToLoad[] = []
+
+        for (const ngId in ngIdRecord) {
+          const labelIndicies = ngIdRecord[ngId]
+          arr.push({
+            labelIndicies,
+            layer: { name: ngId }
+          })
         }
-      }
-      for (const key in auxLayers) {
-        arr.push({
-          layer: {
-            name: key
-          },
-          labelIndicies: auxLayers[key]
-        })
-      }
-
-      return of(...arr)
-    }),
+  
+        return of(...arr)
+      })
+    ),
+    this.store$.pipe(
+      select(selectorAuxMeshes),
+      switchMap(auxMeshes => {
+        const obj: Record<string, number[]> = {}
+        const arr: IMeshesToLoad[] = []
+        for (const mesh of auxMeshes) {
+          if (!obj[mesh.ngId]) {
+            obj[mesh.ngId] = []
+          }
+          if (mesh.visible) {
+            obj[mesh.ngId].push(...mesh.labelIndicies)
+          }
+        }
+        for (const key in obj) {
+          arr.push({
+            layer: {
+              name: key
+            },
+            labelIndicies: obj[key]
+          })
+        }
+        return of(...arr)
+      })
+    )
   )
 }
diff --git a/src/viewerModule/nehuba/module.ts b/src/viewerModule/nehuba/module.ts
index 4e45d608c551ffce7fc8b0e8a2532da8159fc8d3..087c53efda843dfa3eb33112d666fbc6746af4f6 100644
--- a/src/viewerModule/nehuba/module.ts
+++ b/src/viewerModule/nehuba/module.ts
@@ -10,12 +10,8 @@ import { NEHUBA_VIEWER_FEATURE_KEY } from "./constants";
 import { reducer } from "./store";
 import { NehubaGlueCmp } from "./nehubaViewerGlue/nehubaViewerGlue.component";
 import { UtilModule } from "src/util";
-import { LayoutModule } from "src/layouts/layout.module";
-import { TouchSideClass } from "./touchSideClass.directive";
 import { ComponentsModule } from "src/components";
 import { AngularMaterialModule } from "src/sharedModules";
-import { MaximisePanelButton } from "./maximisePanelButton/maximisePanelButton.component";
-import { Landmark2DModule } from "src/ui/nehubaContainer/2dLandmarks/module";
 import { MouseoverModule } from "src/mouseoverModule";
 import { StatusCardComponent } from "./statusCard/statusCard.component";
 import { ShareModule } from "src/share";
@@ -25,8 +21,13 @@ import { StateModule } from "src/state";
 import { AuthModule } from "src/auth";
 import {QuickTourModule} from "src/ui/quickTour/module";
 import { WindowResizeModule } from "src/util/windowResize";
-import { ViewerCtrlModule } from "./viewerCtrl";
 import { DragDropFileModule } from "src/dragDropFile/module";
+import { NgLayerCtrlCmp } from "./ngLayerCtl/ngLayerCtrl.component";
+import { EffectsModule } from "@ngrx/effects";
+import { MeshEffects } from "./mesh.effects/mesh.effects";
+import { NehubaLayoutOverlayModule } from "./layoutOverlay";
+import { NgAnnotationService } from "./annotation/service";
+import { NgAnnotationEffects } from "./annotation/effects";
 
 @NgModule({
   imports: [
@@ -34,14 +35,11 @@ import { DragDropFileModule } from "src/dragDropFile/module";
     FormsModule,
     ReactiveFormsModule,
     UtilModule,
-    LayoutModule,
     AngularMaterialModule,
-    Landmark2DModule,
     ComponentsModule,
     MouseoverModule,
     ShareModule,
     WindowResizeModule,
-    ViewerCtrlModule,
     DragDropFileModule,
 
     /**
@@ -54,24 +52,30 @@ import { DragDropFileModule } from "src/dragDropFile/module";
       NEHUBA_VIEWER_FEATURE_KEY,
       reducer
     ),
-    QuickTourModule
+    EffectsModule.forFeature([
+      MeshEffects,
+      NgAnnotationEffects,
+    ]),
+    QuickTourModule,
+    NehubaLayoutOverlayModule,
   ],
   declarations: [
     NehubaViewerContainerDirective,
     NehubaViewerUnit,
     NehubaViewerTouchDirective,
     NehubaGlueCmp,
-    TouchSideClass,
-    MaximisePanelButton,
     StatusCardComponent,
+    NgLayerCtrlCmp,
   ],
   exports: [
     NehubaViewerUnit,
     NehubaViewerTouchDirective,
     NehubaGlueCmp,
     StatusCardComponent,
+    NgLayerCtrlCmp,
   ],
   providers: [
+    
     {
       provide: IMPORT_NEHUBA_INJECT_TOKEN,
       useFactory: importNehubaFactory,
@@ -80,11 +84,16 @@ import { DragDropFileModule } from "src/dragDropFile/module";
     {
       provide: NEHUBA_INSTANCE_INJTKN,
       useValue: new BehaviorSubject(null)
-    }
+    },
+    NgAnnotationService
   ],
   schemas: [
     CUSTOM_ELEMENTS_SCHEMA
   ]
 })
 
-export class NehubaModule{}
+export class NehubaModule{
+
+  // eslint-disable-next-line  @typescript-eslint/no-empty-function
+  constructor(_svc: NgAnnotationService){}
+}
diff --git a/src/viewerModule/nehuba/navigation.service/navigation.effects.ts b/src/viewerModule/nehuba/navigation.service/navigation.effects.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ae3c4d4e977b6190f2303bc8d2b00ccf72fd3084
--- /dev/null
+++ b/src/viewerModule/nehuba/navigation.service/navigation.effects.ts
@@ -0,0 +1,120 @@
+import { Inject, Injectable, OnDestroy } from "@angular/core";
+import { Actions, createEffect, ofType } from "@ngrx/effects";
+import { select, Store } from "@ngrx/store";
+import { combineLatest, Observable, Subscription } from "rxjs";
+import { filter, map, mapTo, tap, withLatestFrom } from "rxjs/operators";
+import { atlasSelection, MainState, userInterface, userPreference } from "src/state"
+import { CYCLE_PANEL_MESSAGE } from "src/util/constants";
+import { timedValues } from "src/util/generator";
+import { NehubaViewerUnit } from "../nehubaViewer/nehubaViewer.component";
+import { NEHUBA_INSTANCE_INJTKN } from "../util";
+import { navAdd, navMul } from "./navigation.util";
+
+@Injectable()
+export class NehubaNavigationEffects implements OnDestroy{
+
+  private subscription: Subscription[] = []
+  private nehubaInst: NehubaViewerUnit
+  private rafRef: number
+
+  /**
+   * This is an implementation which reconciles local state with the global navigation state.
+   * - it should **never** set global state.
+   * - there exist two (potential) source of navigation state change
+   *    - directly set via global state (with statuscard, url, etc)
+   *    - via viewer (mostly through user interaction)
+   *   if the latter were emitted, it is the local's responsibility to check the (debounced) diff between local/global state,
+   *   and update global state accordingly.
+   * - This effect updates the internal navigation state. It should leave reporting any diff to the local viewer's native implementation.
+   */
+  onNavigateTo = createEffect(() => this.action.pipe(
+    ofType(atlasSelection.actions.navigateTo),
+    filter(() => !!this.nehubaInst),
+    withLatestFrom(
+      this.store.pipe(
+        select(userPreference.selectors.useAnimation)
+      ),
+      this.store.pipe(
+        select(atlasSelection.selectors.navigation)
+      )
+    ),
+    tap(([{ navigation, animation, physical }, globalAnimationFlag, currentNavigation]) => {
+      if (!animation || !globalAnimationFlag) {
+        this.nehubaInst.setNavigationState({
+          ...navigation,
+          positionReal: physical
+        })
+        return
+      }
+
+      const gen = timedValues()
+      const src = currentNavigation
+
+      const dest = {
+        ...src,
+        ...navigation
+      }
+
+      const delta = navAdd(dest, navMul(src, -1))
+
+      const animate = () => {
+        
+        /**
+         * if nehubaInst becomes nullish whilst animation is running
+         */  
+        if (!this.nehubaInst) {
+          this.rafRef = null
+          return
+        }
+
+        const next = gen.next()
+        const d =  next.value
+
+        const n = navAdd(src, navMul(delta, d))
+        this.nehubaInst.setNavigationState({
+          ...n,
+          positionReal: true
+        })
+
+        if ( !next.done ) {
+          this.rafRef = requestAnimationFrame(() => animate())
+        } else {
+          this.rafRef = null
+        }
+      }
+      this.rafRef = requestAnimationFrame(() => animate())
+
+    })
+  ), { dispatch: false })
+
+  onMaximise = createEffect(() => combineLatest([
+    this.store.pipe(
+      select(userPreference.selectors.useMobileUi),
+    ),
+    this.store.pipe(
+      select(userInterface.selectors.panelMode),
+      map(mode => mode === "SINGLE_PANEL")
+    )
+  ]).pipe(
+    filter(([ useMobileUi, singlePanelMode ]) => singlePanelMode && !useMobileUi),
+    mapTo(
+      userInterface.actions.snackBarMessage({
+        message: CYCLE_PANEL_MESSAGE
+      })
+    )
+  ))
+
+  constructor(
+    private action: Actions,
+    private store: Store<MainState>,
+    @Inject(NEHUBA_INSTANCE_INJTKN) nehubaInst$: Observable<NehubaViewerUnit>,
+  ){
+    this.subscription.push(
+      nehubaInst$.subscribe(val => this.nehubaInst = val),
+    )
+  }
+
+  ngOnDestroy(): void {
+    while(this.subscription.length > 0) this.subscription.pop().unsubscribe()
+  }
+}
\ No newline at end of file
diff --git a/src/viewerModule/nehuba/navigation.service/navigation.service.spec.ts b/src/viewerModule/nehuba/navigation.service/navigation.service.spec.ts
index 9fdde1ebbab0cd2c3a8d71c01d935d9b175b897c..9180048b03a28473737f81fa49eb4d1800fcb87b 100644
--- a/src/viewerModule/nehuba/navigation.service/navigation.service.spec.ts
+++ b/src/viewerModule/nehuba/navigation.service/navigation.service.spec.ts
@@ -1,12 +1,11 @@
 import { discardPeriodicTasks, fakeAsync, TestBed, tick } from '@angular/core/testing'
 import { MockStore, provideMockStore } from '@ngrx/store/testing'
 import { BehaviorSubject, of, Subject } from 'rxjs'
-import { selectViewerConfigAnimationFlag } from 'src/services/state/viewerConfig/selectors'
-import { viewerStateSelectorNavigation } from 'src/services/state/viewerState/selectors'
 import * as NavUtil from './navigation.util'
 import { NehubaViewerUnit } from '../nehubaViewer/nehubaViewer.component'
 import { NEHUBA_INSTANCE_INJTKN } from '../util'
 import { NehubaNavigationService } from './navigation.service'
+import { userPreference, atlasSelection } from "src/state"
 
 const nav1 = {
   position: [1,2,3],
@@ -63,11 +62,11 @@ describe('> navigation.service.ts', () => {
 
       const mockStore = TestBed.inject(MockStore)
       mockStore.overrideSelector(
-        viewerStateSelectorNavigation,
+        atlasSelection.selectors.navigation,
         nav1
       )
       mockStore.overrideSelector(
-        selectViewerConfigAnimationFlag,
+        userPreference.selectors.useAnimation,
         true
       )
     })
@@ -108,7 +107,7 @@ describe('> navigation.service.ts', () => {
         service['nehubaViewerInstance'] = nehubaInst as NehubaViewerUnit
 
         const mockStore = TestBed.inject(MockStore)
-        mockStore.overrideSelector(viewerStateSelectorNavigation, nav1)
+        mockStore.overrideSelector(atlasSelection.selectors.navigation, nav1)
         dispatchSpy = spyOn(mockStore, 'dispatch').and.callFake(() => {})
       })
 
diff --git a/src/viewerModule/nehuba/navigation.service/navigation.service.ts b/src/viewerModule/nehuba/navigation.service/navigation.service.ts
index ed751e8b85149abb089fbd5cf5adf0ec888abdee..e06ddd27fe488f686aec8dcd9c803491df1fad60 100644
--- a/src/viewerModule/nehuba/navigation.service/navigation.service.ts
+++ b/src/viewerModule/nehuba/navigation.service/navigation.service.ts
@@ -2,13 +2,11 @@ import { Inject, Injectable, OnDestroy, Optional } from "@angular/core";
 import { select, Store } from "@ngrx/store";
 import { Observable, ReplaySubject, Subscription } from "rxjs";
 import { debounceTime } from "rxjs/operators";
-import { selectViewerConfigAnimationFlag } from "src/services/state/viewerConfig/selectors";
-import { viewerStateChangeNavigation } from "src/services/state/viewerState/actions";
-import { viewerStateSelectorNavigation } from "src/services/state/viewerState/selectors";
 import { NehubaViewerUnit } from "../nehubaViewer/nehubaViewer.component";
 import { NEHUBA_INSTANCE_INJTKN } from "../util";
-import { timedValues } from 'src/util/generator'
-import { INavObj, navAdd, navMul, navObjEqual } from './navigation.util'
+import { INavObj, navObjEqual } from './navigation.util'
+import { actions } from "src/state/atlasSelection";
+import { atlasSelection, userPreference } from "src/state";
 
 @Injectable()
 export class NehubaNavigationService implements OnDestroy{
@@ -33,7 +31,7 @@ export class NehubaNavigationService implements OnDestroy{
   ){
     this.subscriptions.push(
       this.store$.pipe(
-        select(selectViewerConfigAnimationFlag)
+        select(userPreference.selectors.useAnimation)
       ).subscribe(flag => this.globalAnimationFlag = flag)
     )
 
@@ -52,59 +50,28 @@ export class NehubaNavigationService implements OnDestroy{
     this.subscriptions.push(
       // realtime state nav state
       this.store$.pipe(
-        select(viewerStateSelectorNavigation)
+        select(atlasSelection.selectors.navigation)
       ).subscribe(v => {
         this.storeNav = v
         // if stored nav differs from viewerNav
         if (!this.viewerNavLock && this.nehubaViewerInstance) {
           const navEql = navObjEqual(this.storeNav, this.viewerNav)
           if (!navEql) {
-            this.navigateViewer({
-              ...this.storeNav,
-              positionReal: true
-            })
+            this.navigateViewer(this.storeNav)
           }
         }
       })
     )
   }
 
-  navigateViewer(navigation: INavObj & { positionReal?: boolean, animation?: any }){
+  navigateViewer(navigation: INavObj): void {
     if (!navigation) return
-    const { animation, ...rest } = navigation
-    if (animation && this.globalAnimationFlag) {
-
-      const gen = timedValues()
-      const src = this.viewerNav
-
-      const dest = {
-        ...src,
-        ...navigation
-      }
-
-      const delta = navAdd(dest, navMul(src, -1))
-
-      const animate = () => {
-        const next = gen.next()
-        const d =  next.value
-
-        const n = navAdd(src, navMul(delta, d))
-        this.nehubaViewerInstance.setNavigationState({
-          ...n,
-          positionReal: true
-        })
-
-        if ( !next.done ) {
-          this.rafRef = requestAnimationFrame(() => animate())
-        }
-      }
-      this.rafRef = requestAnimationFrame(() => animate())
-    } else {
-      this.nehubaViewerInstance.setNavigationState(rest)
-    }
+    // TODO
+    // readd consider how to do animation
+    this.nehubaViewerInstance.setNavigationState(navigation)
   }
 
-  setupViewerSub(){
+  setupViewerSub(): void {
     this.viewerInstanceSubscriptions.push(
       // realtime viewer nav state
       this.nehubaViewerInstance.viewerPositionChange.subscribe(
@@ -134,7 +101,7 @@ export class NehubaNavigationService implements OnDestroy{
         
         if (!navEql) {
           this.store$.dispatch(
-            viewerStateChangeNavigation({
+            actions.setNavigation({
               navigation: roundedNav
             })
           )
@@ -143,11 +110,11 @@ export class NehubaNavigationService implements OnDestroy{
     )
   }
 
-  clearViewerSub(){
+  clearViewerSub(): void {
     while (this.viewerInstanceSubscriptions.length > 0) this.viewerInstanceSubscriptions.pop().unsubscribe()
   }
 
-  ngOnDestroy(){
+  ngOnDestroy(): void {
     this.clearViewerSub()
     while (this.subscriptions.length > 0) this.subscriptions.pop().unsubscribe()
   }
diff --git a/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.spec.ts b/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.spec.ts
index 304ef3b4b0b15be8061ffc4e614c1291e9aa07e9..c6437fc7c93844af930c70ce2c5ae019227f76cf 100644
--- a/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.spec.ts
+++ b/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.spec.ts
@@ -1,12 +1,10 @@
-import { TestBed, async, fakeAsync, tick, ComponentFixture } from "@angular/core/testing"
-import { CommonModule, DOCUMENT } from "@angular/common"
+import { TestBed, fakeAsync, tick, ComponentFixture } from "@angular/core/testing"
+import { CommonModule } from "@angular/common"
 import { NehubaViewerUnit, IMPORT_NEHUBA_INJECT_TOKEN, scanFn } from "./nehubaViewer.component"
-import { importNehubaFactory } from "../util"
 import { AtlasWorkerService } from "src/atlasViewer/atlasViewer.workerService.service"
 import { LoggingModule, LoggingService } from "src/logging"
-import { APPEND_SCRIPT_TOKEN, appendScriptFactory } from "src/util/constants"
 import { IMeshesToLoad, SET_MESHES_TO_LOAD } from "../constants"
-import { ReplaySubject, Subject } from "rxjs"
+import { Subject } from "rxjs"
 import { IColorMap, SET_COLORMAP_OBS, SET_LAYER_VISIBILITY } from "../layerCtrl.service"
 
 describe('> nehubaViewer.component.ts', () => {
@@ -78,14 +76,12 @@ describe('> nehubaViewer.component.ts', () => {
   })
 
   describe('> NehubaViewerUnit', () => {
-    let provideSetMeshToLoadCtrl = true
-    let provideLayerVisibility = true
-    let provideSetColorObs = true
     const setMeshToLoadCtl$ = new Subject<IMeshesToLoad>()
-    let setLayerVisibility$: Subject<string[]>
-    let setcolorMap$: Subject<IColorMap>
-    beforeEach(async(() => {
-      TestBed.configureTestingModule({
+    let setLayerVisibility$: Subject<string[]> = new Subject()
+    let setcolorMap$: Subject<IColorMap> = new Subject()
+    let fixture: ComponentFixture<NehubaViewerUnit>
+    beforeEach(async () => {
+      await TestBed.configureTestingModule({
         imports: [
           CommonModule,
           LoggingModule
@@ -100,111 +96,41 @@ describe('> nehubaViewer.component.ts', () => {
           },
           {
             provide: SET_MESHES_TO_LOAD,
-            useFactory: () => provideSetMeshToLoadCtrl
-              ? setMeshToLoadCtl$
-              : null
+            useFactory: () => setMeshToLoadCtl$
           },
           {
             provide: SET_LAYER_VISIBILITY,
-            useFactory: () => {
-              setLayerVisibility$ = provideLayerVisibility
-                ? new ReplaySubject(1)
-                : null
-              return setLayerVisibility$
-            } 
+            useValue: setLayerVisibility$
           },
           {
             provide: SET_COLORMAP_OBS,
-            useFactory: () => {
-              setcolorMap$ = provideSetColorObs
-                ? new ReplaySubject(1)
-                : null
-              return setcolorMap$
-            }
+            useValue: setcolorMap$
           },
           AtlasWorkerService,
           LoggingService,
         ]
       }).compileComponents()
-    }))
+    })
 
     it('> creates component', () => {
-      const fixture = TestBed.createComponent(NehubaViewerUnit)
+      fixture = TestBed.createComponent(NehubaViewerUnit)
       expect(fixture.componentInstance).toBeTruthy()
     })
 
-    describe('> on create', () => {
-      it('> calls onInit lifecycle param properly', () => {
-        const onInitSpy = jasmine.createSpy('onInit')
-        const fixture = TestBed.createComponent(NehubaViewerUnit)
-        fixture.componentInstance.lifecycle = {
-          onInit: onInitSpy
-        }
-
-        fixture.detectChanges()
-
-        expect(onInitSpy).toHaveBeenCalled()
-      })
-    })
-
     describe('> loading meshes', () => {
-      describe('> native', () => {
-        beforeAll(() => {
-          provideSetMeshToLoadCtrl = false
-        })
-        it('> on loadMeshes$ emit, calls nehubaViewer.setMeshesToLoad', fakeAsync(() => {
-
-          const fixture = TestBed.createComponent(NehubaViewerUnit)
-          fixture.componentInstance.nehubaViewer = {
-            setMeshesToLoad: jasmine.createSpy('setMeshesToLoad').and.returnValue(null),
-            dispose: () => {}
-          }
-  
-          fixture.detectChanges()
-          fixture.componentInstance['loadMeshes$'].next({
-            layer: {
-              name: 'foo-bar'
-            },
-            labelIndicies: [1,2,3]
-          })
-          tick(1000)
-          expect(fixture.componentInstance.nehubaViewer.setMeshesToLoad).toHaveBeenCalledWith([1,2,3], { name: 'foo-bar' })
-        }))
+      beforeEach(() => {
+        fixture = TestBed.createComponent(NehubaViewerUnit)
+        fixture.componentInstance.nehubaViewer = {
+          setMeshesToLoad: jasmine.createSpy('setMeshesToLoad').and.returnValue(null),
+          dispose: () => {}
+        }
+        fixture.componentInstance['_nehubaReady'] = true
       })
 
       describe('> injecting SET_MESHES_TO_LOAD', () => {
-        beforeAll(() => {
-          provideSetMeshToLoadCtrl = true
-        })
-        it('> navtive loadMeshes method will not trigger loadMesh call',fakeAsync(() => {
-
-          const fixture = TestBed.createComponent(NehubaViewerUnit)
-          fixture.detectChanges()
-          const setMeshToLoadSpy = jasmine.createSpy('setMeshesToLoad').and.returnValue(null)
-          fixture.componentInstance.nehubaViewer = {
-            setMeshesToLoad: setMeshToLoadSpy,
-            dispose: () => {}
-          }
-  
-          fixture.detectChanges()
-          fixture.componentInstance['loadMeshes$'].next({
-            layer: {
-              name: 'foo-bar'
-            },
-            labelIndicies: [1,2,3]
-          })
-          tick(1000)
-          expect(setMeshToLoadSpy).not.toHaveBeenCalledWith([1,2,3], { name: 'foo-bar' })
-        }))
 
         it('> when injected obs emits, will trigger loadMesh call', fakeAsync(() => {
 
-          const fixture = TestBed.createComponent(NehubaViewerUnit)
-          fixture.componentInstance.nehubaViewer = {
-            setMeshesToLoad: jasmine.createSpy('setMeshesToLoad').and.returnValue(null),
-            dispose: () => {}
-          }
-  
           fixture.detectChanges()
           setMeshToLoadCtl$.next({
             labelIndicies: [1,2,3],
@@ -212,7 +138,7 @@ describe('> nehubaViewer.component.ts', () => {
               name: 'foo-bar'
             }
           })
-          tick(1000)
+          tick(400)
           expect(fixture.componentInstance.nehubaViewer.setMeshesToLoad).toHaveBeenCalledWith([1,2,3], { name: 'foo-bar' })
         }))
       })
@@ -250,12 +176,12 @@ describe('> nehubaViewer.component.ts', () => {
           dispose: () => {}
         }
 
-        provideLayerVisibility = true
+        fixture = TestBed.createComponent(NehubaViewerUnit)
+        fixture.componentInstance.nehubaViewer = nehubaViewerSpy
+        fixture.componentInstance['_nehubaReady'] = true
       })
 
       it('> if provided obs does not emit, does not call manage layers', fakeAsync(() => {
-        const fixture = TestBed.createComponent(NehubaViewerUnit)
-        fixture.componentInstance.nehubaViewer = nehubaViewerSpy
         fixture.detectChanges()
         tick(320)
         expect(managedLayersSpy).not.toHaveBeenCalled()
@@ -264,9 +190,7 @@ describe('> nehubaViewer.component.ts', () => {
       describe('> if provided obs does emit', () => {
 
         const setup = (emit = []) => {
-          const fixture = TestBed.createComponent(NehubaViewerUnit)
           setLayerVisibility$.next(emit)
-          fixture.componentInstance.nehubaViewer = nehubaViewerSpy
           fixture.detectChanges()
           tick(640)
         }
@@ -311,11 +235,13 @@ describe('> nehubaViewer.component.ts', () => {
       let prvSetCMSpy: jasmine.Spy
       const setup = () => {
 
-        const fixture = TestBed.createComponent(NehubaViewerUnit)
+        fixture = TestBed.createComponent(NehubaViewerUnit)
+        fixture.componentInstance['_nehubaReady'] = true
+
         /**
          * set nehubaViewer, since some methods check viewer is loaded
          */
-         fixture.componentInstance.nehubaViewer = {
+        fixture.componentInstance.nehubaViewer = {
            ngviewer: {},
            dispose: () => {}
          }
@@ -323,9 +249,6 @@ describe('> nehubaViewer.component.ts', () => {
         prvSetCMSpy = spyOn<any>(fixture.componentInstance, 'setColorMap').and.callFake(() => {})
       }
 
-      beforeEach(() => {
-        provideSetColorObs = true
-      })
       describe('> obs does not emit', () => {
         beforeEach(fakeAsync(() => {
           setup()
diff --git a/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.ts b/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.ts
index 8281c6565f4f6c717e8586877285781f631a1069..b1f70202ac24bfbe8375be62bcee549eb5132694 100644
--- a/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.ts
+++ b/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.ts
@@ -1,12 +1,11 @@
-import { Component, ElementRef, EventEmitter, OnDestroy, OnInit, Output, Inject, Optional, ChangeDetectionStrategy } from "@angular/core";
-import { fromEvent, Subscription, ReplaySubject, BehaviorSubject, Observable, race, timer, Subject } from 'rxjs'
-import { debounceTime, filter, map, scan, startWith, mapTo, switchMap, take, skip, tap, distinctUntilChanged } from "rxjs/operators";
+import { Component, ElementRef, EventEmitter, OnDestroy, Output, Inject, Optional } from "@angular/core";
+import { fromEvent, Subscription, BehaviorSubject, Observable, Subject, of, interval } from 'rxjs'
+import { debounceTime, filter, map, scan, switchMap, take, distinctUntilChanged, debounce } from "rxjs/operators";
 import { AtlasWorkerService } from "src/atlasViewer/atlasViewer.workerService.service";
-import { StateInterface as ViewerConfiguration } from "src/services/state/viewerConfig.store";
 import { LoggingService } from "src/logging";
 import { bufferUntil, getExportNehuba, getViewer, setNehubaViewer, switchMapWaitFor } from "src/util/fn";
-import { NEHUBA_INSTANCE_INJTKN, scanSliceViewRenderFn } from "../util";
-import { deserialiseParcRegionId, arrayOrderedEql } from 'common/util'
+import { deserializeSegment, NEHUBA_INSTANCE_INJTKN } from "../util";
+import { arrayOrderedEql } from 'common/util'
 import { IMeshesToLoad, SET_MESHES_TO_LOAD } from "../constants";
 import { IColorMap, SET_COLORMAP_OBS, SET_LAYER_VISIBILITY } from "../layerCtrl.service";
 
@@ -36,10 +35,6 @@ interface LayerLabelIndex {
   labelIndicies: number[]
 }
 
-export interface INehubaLifecycleHook{
-  onInit?: () => void
-}
-
 export const scanFn = (acc: LayerLabelIndex[], curr: LayerLabelIndex) => {
   const found = acc.find(layerLabelIndex => {
     return layerLabelIndex.layer.name === curr.layer.name
@@ -62,17 +57,12 @@ export const scanFn = (acc: LayerLabelIndex[], curr: LayerLabelIndex) => {
   styleUrls : [
     './nehubaViewer.style.css',
   ],
-  // OnPush seems to improve performance significantly
-  changeDetection: ChangeDetectionStrategy.OnPush
 })
 
-export class NehubaViewerUnit implements OnInit, OnDestroy {
+export class NehubaViewerUnit implements OnDestroy {
 
-  private sliceviewLoading$: Observable<boolean>
 
-  public overrideShowLayers: string[] = []
-
-  public lifecycle: INehubaLifecycleHook
+  public ngIdSegmentsMap: Record<string, number[]> = {}
 
   public viewerPosInVoxel$ = new BehaviorSubject(null)
   public viewerPosInReal$ = new BehaviorSubject(null)
@@ -83,6 +73,7 @@ export class NehubaViewerUnit implements OnInit, OnDestroy {
 
   private subscriptions: Subscription[] = []
 
+  private _nehubaReady = false
   @Output() public nehubaReady: EventEmitter<null> = new EventEmitter()
   @Output() public layersChanged: EventEmitter<null> = new EventEmitter()
   private layersChangedHandler: any
@@ -90,7 +81,6 @@ export class NehubaViewerUnit implements OnInit, OnDestroy {
   @Output() public mouseoverSegmentEmitter:
     EventEmitter<{
       segmentId: number | null
-      segment: string | null
       layer: {
         name?: string
         url?: string
@@ -98,15 +88,17 @@ export class NehubaViewerUnit implements OnInit, OnDestroy {
     }> = new EventEmitter()
   @Output() public mouseoverLandmarkEmitter: EventEmitter<string> = new EventEmitter()
   @Output() public mouseoverUserlandmarkEmitter: EventEmitter<string> = new EventEmitter()
-  @Output() public regionSelectionEmitter: EventEmitter<{segment: number, layer: {name?: string, url?: string}}> = new EventEmitter()
+  @Output() public regionSelectionEmitter: EventEmitter<{
+    segment: number
+    layer: {
+      name?: string
+      url?: string
+  }}> = new EventEmitter()
   @Output() public errorEmitter: EventEmitter<any> = new EventEmitter()
 
-  public auxilaryMeshIndices: number[] = []
 
   /* only used to set initial navigation state */
   public initNav: any
-  public initRegions: any[]
-  public initNiftiLayers: any[] = []
 
   public config: any
   public nehubaViewer: any
@@ -137,11 +129,6 @@ export class NehubaViewerUnit implements OnInit, OnDestroy {
 
   public ondestroySubscriptions: Subscription[] = []
 
-  private createNehubaPromiseRs: () => void
-  private createNehubaPromise = new Promise<void>(rs => {
-    this.createNehubaPromiseRs = rs
-  })
-
   public nehubaLoaded: boolean = false
 
   public landmarksLoaded: boolean = false
@@ -176,54 +163,22 @@ export class NehubaViewerUnit implements OnInit, OnDestroy {
         this.patchNG()
         this.loadNehuba()
 
-        this.layersChangedHandler = this.nehubaViewer.ngviewer.layerManager.layersChanged.add(() => this.layersChanged.emit(null))
-        this.nehubaViewer.ngviewer.registerDisposer(this.layersChangedHandler)
-      })
-      .then(() => {
-        // all mutation to this.nehubaViewer should await createNehubaPromise
-        this.createNehubaPromiseRs()
+        const viewer = this.nehubaViewer.ngviewer
+        this.layersChangedHandler = viewer.layerManager.layersChanged.add(() => {
+          this.layersChanged.emit(null)
+          const readiedLayerNames: string[] = viewer.layerManager.managedLayers.filter(l => l.layer).map(l => l.name)
+          for (const layerName in this.ngIdSegmentsMap) {
+            if (!readiedLayerNames.includes(layerName)) {
+              return
+            }
+          }
+          this._nehubaReady = true
+          this.nehubaReady.emit(null)
+        })
+        viewer.registerDisposer(this.layersChangedHandler)
       })
       .catch(e => this.errorEmitter.emit(e))
 
-    /**
-     * TODO move to layerCtrl.service
-     */
-    this.ondestroySubscriptions.push(
-      fromEvent(this.workerService.worker, 'message').pipe(
-        filter((message: any) => {
-
-          if (!message) {
-            // this.log.error('worker response message is undefined', message)
-            return false
-          }
-          if (!message.data) {
-            // this.log.error('worker response message.data is undefined', message.data)
-            return false
-          }
-          if (message.data.type !== 'ASSEMBLED_LANDMARKS_VTK') {
-            /* worker responded with not assembled landmark, no need to act */
-            return false
-          }
-          if (!message.data.url) {
-            /* file url needs to be defined */
-            return false
-          }
-          return true
-        }),
-        debounceTime(100),
-        filter(e => this.templateId === e.data.template),
-        map(e => e.data.url),
-      ).subscribe(url => {
-        this.removeSpatialSearch3DLandmarks()
-        const _ = {}
-        _[NG_LANDMARK_LAYER_NAME] = {
-          type : 'mesh',
-          source : `vtk://${url}`,
-          shader : FRAGMENT_MAIN_WHITE,
-        }
-        this.loadLayer(_)
-      }),
-    )
 
     /**
      * TODO move to layerCtrl.service
@@ -289,7 +244,7 @@ export class NehubaViewerUnit implements OnInit, OnDestroy {
       this.ondestroySubscriptions.push(
         this.setColormap$.pipe(
           switchMap(switchMapWaitFor({
-            condition: () => !!(this.nehubaViewer?.ngviewer)
+            condition: () => this._nehubaReady
           })),
           debounceTime(160),
         ).subscribe(v => {
@@ -311,7 +266,9 @@ export class NehubaViewerUnit implements OnInit, OnDestroy {
     if (this.layerVis$) {
       this.ondestroySubscriptions.push(
         this.layerVis$.pipe(
-          switchMap(switchMapWaitFor({ condition: () => !!(this.nehubaViewer?.ngviewer) })),
+          switchMap(switchMapWaitFor({
+            condition: () => this._nehubaReady
+          })),
           distinctUntilChanged(arrayOrderedEql),
           debounceTime(160),
         ).subscribe((layerNames: string[]) => {
@@ -338,7 +295,14 @@ export class NehubaViewerUnit implements OnInit, OnDestroy {
 
     if (this.segVis$) {
       this.ondestroySubscriptions.push(
-        this.segVis$.pipe().subscribe(val => {
+        this.segVis$.pipe(
+          switchMap(
+            switchMapWaitFor({
+              condition: () => this._nehubaReady,
+              leading: true,
+            })
+          )
+        ).subscribe(val => {
           // null === hide all seg
           if (val === null) {
             this.hideAllSeg()
@@ -361,7 +325,7 @@ export class NehubaViewerUnit implements OnInit, OnDestroy {
       this.ondestroySubscriptions.push(
         this.layerCtrl$.pipe(
           bufferUntil(({
-            condition: () => !!this.nehubaViewer?.ngviewer
+            condition: () => this._nehubaReady
           }))
         ).subscribe(messages => {
           for (const message of messages) {
@@ -385,17 +349,47 @@ export class NehubaViewerUnit implements OnInit, OnDestroy {
                 this.setLayerTransparency(key, p.payload[key])
               }
             }
+            if (message.type === "updateShader") {
+              const p = message as TNgLayerCtrl<'updateShader'>
+              for (const key in p.payload) {
+                this.setLayerShader(key, p.payload[key])
+              }
+            }
           }
         })
       )
     } else {
       this.log.error(`NG_LAYER_CONTROL not provided`)
     }
-  }
 
-  public numMeshesToBeLoaded: number = 0
+    if (this.injSetMeshesToLoad$) {
+      this.subscriptions.push(
+        this.injSetMeshesToLoad$.pipe(
+          scan(scanFn, []),
+          debounceTime(16),
+          debounce(() => this._nehubaReady
+            ? of(true)
+            : interval(160).pipe(
+              filter(() => this._nehubaReady),
+              take(1),
+            )
+          ),
+        ).subscribe(layersLabelIndex => {
+          let totalMeshes = 0
+          for (const layerLayerIndex of layersLabelIndex) {
+            const { layer, labelIndicies } = layerLayerIndex
+            totalMeshes += labelIndicies.length
+            this.nehubaViewer.setMeshesToLoad(labelIndicies, layer)
+          }
+          // TODO implement total mesh to be loaded and mesh loading UI
+        }),
+      )
+    } else {
+      this.log.error(`SET_MESHES_TO_LOAD not provided`)
+    }
+  }
 
-  public applyPerformanceConfig({ gpuLimit }: Partial<ViewerConfiguration>) {
+  public applyGpuLimit(gpuLimit: number) {
     if (gpuLimit && this.nehubaViewer) {
       const limit = this.nehubaViewer.ngviewer.state.children.get('gpuMemoryLimit')
       if (limit && limit.restoreState) {
@@ -404,15 +398,6 @@ export class NehubaViewerUnit implements OnInit, OnDestroy {
     }
   }
 
-  /* required to check if correct landmarks are loaded */
-  private _templateId: string
-  get templateId() {
-    return this._templateId
-  }
-  set templateId(id: string) {
-    this._templateId = id
-  }
-
   public spatialLandmarkSelectionChanged(labels: number[]) {
     const getCondition = (label: number) => `if(label > ${label - 0.1} && label < ${label + 0.1} ){${FRAGMENT_EMIT_RED}}`
     const newShader = `void main(){ ${labels.map(getCondition).join('else ')}else {${FRAGMENT_EMIT_WHITE}} }`
@@ -432,10 +417,6 @@ export class NehubaViewerUnit implements OnInit, OnDestroy {
     }
   }
 
-  // TODO move region management to another service
-
-  public multiNgIdsLabelIndexMap: Map<string, Map<number, any>> = new Map()
-
   public navPosReal: [number, number, number] = [0, 0, 0]
   public navPosVoxel: [number, number, number] = [0, 0, 0]
 
@@ -453,8 +434,6 @@ export class NehubaViewerUnit implements OnInit, OnDestroy {
     this._multiNgIdColorMap = val
   }
 
-  private loadMeshes$: ReplaySubject<{labelIndicies: number[], layer: { name: string }}> = new ReplaySubject()
-
   public mouseOverSegment: number | null
   public mouseOverLayer: {name: string, url: string}| null
 
@@ -462,12 +441,8 @@ export class NehubaViewerUnit implements OnInit, OnDestroy {
     ? this.exportNehuba.getNgHash()
     : null
 
-  public redraw() {
-    this.nehubaViewer.redraw()
-  }
-
   public loadNehuba() {
-    this.nehubaViewer = this.exportNehuba.createNehubaViewer(this.config, (err) => {
+    this.nehubaViewer = this.exportNehuba.createNehubaViewer(this.config, (err: string) => {
       /* print in debug mode */
       this.log.error(err)
     })
@@ -477,12 +452,9 @@ export class NehubaViewerUnit implements OnInit, OnDestroy {
      * Then show the layers referenced in multiNgIdLabelIndexMap
      */
 
-    this.redraw()
-
     /* creation of the layout is done on next frame, hence the settimeout */
     setTimeout(() => {
       getViewer().display.panels.forEach(patchSliceViewPanel)
-      this.nehubaReady.emit(null)
     })
 
     this.newViewerInit()
@@ -493,52 +465,6 @@ export class NehubaViewerUnit implements OnInit, OnDestroy {
     this.onDestroyCb.push(() => setNehubaViewer(null))
   }
 
-  public ngOnInit() {
-    this.sliceviewLoading$ = fromEvent(this.elementRef.nativeElement, 'sliceRenderEvent').pipe(
-      scan(scanSliceViewRenderFn, [ null, null, null ]),
-      map(arrOfFlags => arrOfFlags.some(flag => flag)),
-      startWith(true),
-    )
-
-    this.subscriptions.push(
-      (this.injSetMeshesToLoad$ || this.loadMeshes$).pipe(
-        scan(scanFn, []),
-        debounceTime(100),
-        switchMap(layerLabelIdx => 
-          /**
-           * sometimes (e.g. when all slice views are minimised), sliceviewlaoding will not emit
-           * so if sliceviewloading does not emit another value (except the initial true value)
-           * force start loading of mesh
-           */
-          race(
-            this.sliceviewLoading$.pipe(
-              skip(1)
-            ),
-            timer(500).pipe(
-              mapTo(false)
-            )
-          ).pipe(
-            filter(flag => !flag),
-            take(1),
-            mapTo(layerLabelIdx),
-          ) 
-        ),
-      ).subscribe(layersLabelIndex => {
-        let totalMeshes = 0
-        for (const layerLayerIndex of layersLabelIndex) {
-          const { layer, labelIndicies } = layerLayerIndex
-          totalMeshes += labelIndicies.length
-          this.nehubaViewer.setMeshesToLoad(labelIndicies, layer)
-        }
-        // TODO implement total mesh to be loaded and mesh loading UI
-        this.numMeshesToBeLoaded = totalMeshes
-      }),
-    )
-
-    const { onInit } = this.lifecycle || {}
-    onInit && onInit.call(this)
-  }
-
   public ngOnDestroy() {
     if (this.nehubaViewer$) {
       this.nehubaViewer$.next(null)
@@ -579,10 +505,12 @@ export class NehubaViewerUnit implements OnInit, OnDestroy {
        * The emitted value does not affect the region selection
        * the region selection is taken care of in nehubaContainer
        */
-      const map = this.multiNgIdsLabelIndexMap.get(this.mouseOverLayer.name)
-      const region = map && map.get(this.mouseOverSegment)
+      
       if (arg === 'select') {
-        this.regionSelectionEmitter.emit({ segment: region, layer: this.mouseOverLayer })
+        this.regionSelectionEmitter.emit({
+          segment: this.mouseOverSegment,
+          layer: this.mouseOverLayer
+        })
       }
     }
 
@@ -662,23 +590,6 @@ export class NehubaViewerUnit implements OnInit, OnDestroy {
     })
   }
 
-  // pos in mm
-  public addSpatialSearch3DLandmarks(geometries: any[], scale?: number, type?: 'icosahedron') {
-    this.workerService.worker.postMessage({
-      type : 'GET_LANDMARKS_VTK',
-      template : this.templateId,
-      scale: Math.min(...this.dim.map(v => v * NG_LANDMARK_CONSTANT)),
-      landmarks : geometries.map(geometry =>
-        geometry === null
-          ? null
-          // gemoetry : [number,number,number] | [ [number,number,number][], [number,number,number][] ]
-          : isNaN(geometry[0])
-            ? [geometry[0].map(triplets => triplets.map(coord => coord * 1e6)), geometry[1]]
-            : geometry.map(coord => coord * 1e6),
-      ),
-    })
-  }
-
   public setLayerVisibility(condition: {name: string|RegExp}, visible: boolean) {
     if (!this.nehubaViewer) {
       return false
@@ -728,28 +639,33 @@ export class NehubaViewerUnit implements OnInit, OnDestroy {
   }
 
   public hideAllSeg() {
-    if (!this.nehubaViewer) { return }
-    Array.from(this.multiNgIdsLabelIndexMap.keys()).forEach(ngId => {
-
-      Array.from(this.multiNgIdsLabelIndexMap.get(ngId).keys()).forEach(idx => {
+    if (!this.nehubaViewer) return
+    for (const ngId in this.ngIdSegmentsMap) {
+      for (const idx of this.ngIdSegmentsMap[ngId]) {
         this.nehubaViewer.hideSegment(idx, {
           name: ngId,
         })
-      })
+      }
+
       this.nehubaViewer.showSegment(0, {
         name: ngId,
       })
-    })
+    }
   }
 
   public showAllSeg() {
     if (!this.nehubaViewer) { return }
-    this.hideAllSeg()
-    Array.from(this.multiNgIdsLabelIndexMap.keys()).forEach(ngId => {
+    for (const ngId in this.ngIdSegmentsMap) {
+      for (const idx of this.ngIdSegmentsMap[ngId]) {
+        this.nehubaViewer.showSegment(idx, {
+          name: ngId,
+        })
+      }
+
       this.nehubaViewer.hideSegment(0, {
         name: ngId,
       })
-    })
+    }
   }
 
   public showSegs(array: (number|string)[]) {
@@ -772,7 +688,7 @@ export class NehubaViewerUnit implements OnInit, OnDestroy {
     const reduceFn: (acc: Map<string, number[]>, curr: string) => Map<string, number[]> = (acc, curr) => {
 
       const newMap = new Map(acc)
-      const { ngId, labelIndex } = deserialiseParcRegionId(curr)
+      const { ngId, label: labelIndex } = deserializeSegment(curr)
       const exist = newMap.get(ngId)
       if (!exist) {
         newMap.set(ngId, [Number(labelIndex)])
@@ -804,7 +720,7 @@ export class NehubaViewerUnit implements OnInit, OnDestroy {
     })
   }
 
-  private vec3(pos: [number, number, number]) {
+  private vec3(pos: number[]) {
     return this.exportNehuba.vec3.fromValues(...pos)
   }
 
@@ -875,7 +791,20 @@ export class NehubaViewerUnit implements OnInit, OnDestroy {
   private setLayerTransparency(layerName: string, alpha: number) {
     const layer = this.nehubaViewer.ngviewer.layerManager.getLayerByName(layerName)
     if (!layer) return
-    layer.layer.displayState.objectAlpha.restoreState(alpha)
+
+    /**
+     * for segmentation layer
+     */
+    if (layer.layer.displayState) layer.layer.displayState.objectAlpha.restoreState(alpha)
+    /**
+     * for image layer
+     */
+    if (layer.layer.opacity) layer.layer.opacity.restoreState(alpha)
+  }
+
+  private setLayerShader(layerName: string, shader: string) {
+    const layer = this.nehubaViewer.ngviewer.layerManager.getLayerByName(layerName)
+    if (layer?.layer?.fragmentMain) layer.layer.fragmentMain.restoreState(shader)
   }
 
   public setMeshTransparency(flag: boolean){
@@ -883,7 +812,7 @@ export class NehubaViewerUnit implements OnInit, OnDestroy {
     /**
      * remove transparency from meshes in current layer(s)
      */
-    for (const layerKey of this.multiNgIdsLabelIndexMap.keys()) {
+    for (const layerKey in this.ngIdSegmentsMap) {
       const layer = this.nehubaViewer.ngviewer.layerManager.getLayerByName(layerKey)
       if (layer) {
         layer.layer.displayState.objectAlpha.restoreState(flag ? 0.2 : 1.0)
@@ -891,6 +820,10 @@ export class NehubaViewerUnit implements OnInit, OnDestroy {
     }
   }
 
+  public redraw(){
+    this.nehubaViewer.redraw()
+  }
+
   private newViewerInit() {
 
     /* isn't this layer specific? */
@@ -906,24 +839,9 @@ export class NehubaViewerUnit implements OnInit, OnDestroy {
       this.initNav = null
     }
 
-    if (this.initRegions && this.initRegions.length > 0) {
-      this.hideAllSeg()
-      this.showSegs(this.initRegions)
-    }
-
-    if (this.initNiftiLayers.length > 0) {
-      this.initNiftiLayers.forEach(layer => this.loadLayer(layer))
-      this.hideAllSeg()
-    }
-
     this._s8$ = this.nehubaViewer.mouseOver.segment.subscribe(({segment: segmentId, layer }) => {
-
-      const {name = 'unnamed'} = layer
-      const map = this.multiNgIdsLabelIndexMap.get(name)
-      const region = map && map.get(segmentId)
       this.mouseoverSegmentEmitter.emit({
         layer,
-        segment: region,
         segmentId,
       })
     })
@@ -1017,7 +935,6 @@ export class NehubaViewerUnit implements OnInit, OnDestroy {
     this._s$.forEach(_s$ => {
       if (_s$) { _s$.unsubscribe() }
     })
-
   }
 
   private setColorMap(map: Map<string, Map<number, {red: number, green: number, blue: number}>>) {
@@ -1053,10 +970,10 @@ const patchSliceViewPanel = (sliceViewPanel: any) => {
 }
 
 export interface ViewerState {
-  orientation: [number, number, number, number]
-  perspectiveOrientation: [number, number, number, number]
+  orientation: number[]
+  perspectiveOrientation: number[]
   perspectiveZoom: number
-  position: [number, number, number]
+  position: number[]
   positionReal: boolean
   zoom: number
 }
diff --git a/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.component.spec.ts b/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.component.spec.ts
index a73f96b59d85b83f00c9dd684257df7e7990ec81..df06e08ba9ffbe21ffe2ae10d10d70e2d2e57247 100644
--- a/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.component.spec.ts
+++ b/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.component.spec.ts
@@ -7,28 +7,22 @@ import { NEVER, Subject } from "rxjs"
 import { ComponentsModule } from "src/components"
 import { ClickInterceptorService } from "src/glue"
 import { LayoutModule } from "src/layouts/layout.module"
-import { PANELS } from "src/services/state/ngViewerState/constants"
-import { ngViewerSelectorOctantRemoval, ngViewerSelectorPanelMode, ngViewerSelectorPanelOrder } from "src/services/state/ngViewerState/selectors"
-import { uiStateMouseOverSegmentsSelector } from "src/services/state/uiState/selectors"
-import { viewerStateSetSelectedRegions } from "src/services/state/viewerState.store.helper"
-import { viewerStateCustomLandmarkSelector, viewerStateNavigationStateSelector, viewerStateSelectedRegionsSelector } from "src/services/state/viewerState/selectors"
-import { Landmark2DModule } from "src/ui/nehubaContainer/2dLandmarks/module"
 import { QuickTourModule } from "src/ui/quickTour"
 import { AngularMaterialModule } from "src/sharedModules/angularMaterial.module"
 import { ClickInterceptor, CLICK_INTERCEPTOR_INJECTOR, UtilModule } from "src/util"
 import { WindowResizeModule } from "src/util/windowResize"
 import { NehubaLayerControlService } from "../layerCtrl.service"
-import { MaximisePanelButton } from "../maximisePanelButton/maximisePanelButton.component"
 import { NehubaMeshService } from "../mesh.service"
 import { NehubaViewerTouchDirective } from "../nehubaViewerInterface/nehubaViewerTouch.directive"
 import { selectorAuxMeshes } from "../store"
-import { TouchSideClass } from "../touchSideClass.directive"
 import { NehubaGlueCmp } from "./nehubaViewerGlue.component"
-import { HarnessLoader } from "@angular/cdk/testing"
 import { AtlasWorkerService } from "src/atlasViewer/atlasViewer.workerService.service"
+import { userInterface, atlasSelection, atlasAppearance, annotation, userInteraction } from "src/state"
+import { SapiAtlasModel, SAPIModule, SapiParcellationModel, SapiRegionModel, SapiSpaceModel } from "src/atlasComponents/sapi"
+import { LayerCtrlEffects } from "../layerCtrl.service/layerCtrl.effects"
+import { NEHUBA_INSTANCE_INJTKN } from "../util"
 import { RouterService } from "src/routerModule/router.service"
 
-
 @Component({
   selector: 'viewer-ctrl-component',
   template: ''
@@ -55,28 +49,31 @@ class MockNehubaViewerContainerDirective{
 
 describe('> nehubaViewerGlue.component.ts', () => {
   let mockStore: MockStore
-  let rootLoader: HarnessLoader
   let fixture: ComponentFixture<NehubaGlueCmp>
+  const selectedATPR$ = new Subject<{
+    atlas: SapiAtlasModel,
+    parcellation: SapiParcellationModel,
+    template: SapiSpaceModel,
+    regions: SapiRegionModel[],
+  }>()
   beforeEach( async () => {
     await TestBed.configureTestingModule({
       imports: [
         CommonModule,
         AngularMaterialModule,
-        LayoutModule,
-        Landmark2DModule,
+        FormsModule,
+        ReactiveFormsModule,
+
         QuickTourModule,
         ComponentsModule,
         UtilModule,
         WindowResizeModule,
-        FormsModule,
-        ReactiveFormsModule,
-        // NehubaModule,
+        LayoutModule,
+        SAPIModule,
       ],
       declarations: [
         NehubaGlueCmp,
-        MaximisePanelButton,
         MockViewerCtrlCmp,
-        TouchSideClass,
 
         // TODO this may introduce a lot more dep
         MockNehubaViewerContainerDirective,
@@ -87,11 +84,7 @@ describe('> nehubaViewerGlue.component.ts', () => {
          * TODO, figureout which dependency is selecting viewerState.parcellationSelected
          * then remove the inital state
          */
-        provideMockStore({
-          initialState: {
-            viewerState: {}
-          }
-        }),
+        provideMockStore(),
         {
           provide: CLICK_INTERCEPTOR_INJECTOR,
           useFactory: (clickIntService: ClickInterceptorService) => {
@@ -110,6 +103,7 @@ describe('> nehubaViewerGlue.component.ts', () => {
             visibleLayer$: new Subject(),
             segmentVis$: new Subject(),
             ngLayersController$: new Subject(),
+            selectedATPR$
           }
         },{
           provide: RouterService,
@@ -133,6 +127,15 @@ describe('> nehubaViewerGlue.component.ts', () => {
               }
             }
           }
+        },{
+          provide: NEHUBA_INSTANCE_INJTKN,
+          useValue: NEVER
+        },
+        {
+          provide: LayerCtrlEffects,
+          useValue: {
+            onATPDebounceNgLayers$: NEVER
+          }
         }
       ]
     }).compileComponents()
@@ -140,13 +143,21 @@ describe('> nehubaViewerGlue.component.ts', () => {
 
   beforeEach(() => {
     mockStore = TestBed.inject(MockStore)
-    mockStore.overrideSelector(ngViewerSelectorPanelMode, PANELS.FOUR_PANEL)
-    mockStore.overrideSelector(ngViewerSelectorPanelOrder, '0123')
-    mockStore.overrideSelector(ngViewerSelectorOctantRemoval, true)
-    mockStore.overrideSelector(viewerStateCustomLandmarkSelector, [])
-    mockStore.overrideSelector(viewerStateSelectedRegionsSelector, [])
-    mockStore.overrideSelector(uiStateMouseOverSegmentsSelector, [])
-    mockStore.overrideSelector(viewerStateNavigationStateSelector, null)
+    mockStore.overrideSelector(userInterface.selectors.panelMode, "FOUR_PANEL")
+    mockStore.overrideSelector(userInterface.selectors.panelOrder, '0123')
+    mockStore.overrideSelector(atlasAppearance.selectors.octantRemoval, true)
+
+    mockStore.overrideSelector(atlasSelection.selectors.selectedAtlas, null)
+    mockStore.overrideSelector(atlasSelection.selectors.selectedTemplate, null)
+    mockStore.overrideSelector(atlasSelection.selectors.selectedParcellation, null)
+    mockStore.overrideSelector(atlasSelection.selectors.selectedRegions, [])
+    mockStore.overrideSelector(atlasSelection.selectors.selectedParcAllRegions, [])
+    mockStore.overrideSelector(userInteraction.selectors.mousingOverRegions, [])
+
+    mockStore.overrideSelector(atlasSelection.selectors.navigation, null)
+    mockStore.overrideSelector(atlasAppearance.selectors.showDelineation, true)
+    mockStore.overrideSelector(atlasAppearance.selectors.customLayers, [])
+    mockStore.overrideSelector(annotation.selectors.annotations, [])
 
     mockStore.overrideSelector(selectorAuxMeshes, [])
   })
@@ -192,7 +203,6 @@ describe('> nehubaViewerGlue.component.ts', () => {
       const testObj1 = 'hello world'
       beforeEach(() => {
         fallbackSpy = spyOn(clickIntServ, 'fallback')
-        mockStore.overrideSelector(uiStateMouseOverSegmentsSelector, [testObj1, testObj0] as any)
         TestBed.createComponent(NehubaGlueCmp)
         clickIntServ.callRegFns(null)
       })
@@ -221,7 +231,6 @@ describe('> nehubaViewerGlue.component.ts', () => {
       }
       beforeEach(() => {
         fallbackSpy = spyOn(clickIntServ, 'fallback')
-        mockStore.overrideSelector(uiStateMouseOverSegmentsSelector, [testObj0, testObj1, testObj2] as any)
 
       })
       afterEach(() => {
@@ -231,11 +240,6 @@ describe('> nehubaViewerGlue.component.ts', () => {
         TestBed.createComponent(NehubaGlueCmp)
         clickIntServ.callRegFns(null)
         const { segment } = testObj1
-        expect(dispatchSpy).toHaveBeenCalledWith(
-          viewerStateSetSelectedRegions({
-            selectRegions: [segment]
-          } as any)
-        )
       })
       it('> fallback called (does not intercept)', () => {
         TestBed.createComponent(NehubaGlueCmp)
@@ -246,14 +250,14 @@ describe('> nehubaViewerGlue.component.ts', () => {
   })
 
   describe('> handleFileDrop', () => {
-    let addNgLayerSpy: jasmine.Spy
-    let removeNgLayersSpy: jasmine.Spy
+    let dispatchSpy: jasmine.Spy
     let workerSendMessageSpy: jasmine.Spy
     let dummyFile1: File
     let dummyFile2: File
     let input: File[]
 
     beforeEach(() => {
+      dispatchSpy = spyOn(mockStore, 'dispatch')
       dummyFile1 = (() => {
         const bl: any = new Blob([], { type: 'text' })
         bl.name = 'filename1.txt'
@@ -271,13 +275,6 @@ describe('> nehubaViewerGlue.component.ts', () => {
       fixture = TestBed.createComponent(NehubaGlueCmp)
       fixture.detectChanges()
 
-      addNgLayerSpy = spyOn(fixture.componentInstance['layerCtrlService'], 'addNgLayer').and.callFake(() => {
-
-      })
-      removeNgLayersSpy = spyOn(fixture.componentInstance['layerCtrlService'], 'removeNgLayers').and.callFake(() => {
-
-      })
-
       workerSendMessageSpy = spyOn(fixture.componentInstance['worker'], 'sendMessage').and.callFake(async () => {
         return {
           result: {
@@ -287,8 +284,7 @@ describe('> nehubaViewerGlue.component.ts', () => {
       })
     })
     afterEach(() => {
-      addNgLayerSpy.calls.reset()
-      removeNgLayersSpy.calls.reset()
+      dispatchSpy.calls.reset()
       workerSendMessageSpy.calls.reset()
     })
 
@@ -311,8 +307,7 @@ describe('> nehubaViewerGlue.component.ts', () => {
           })
 
           it('> should not call addnglayer', () => {
-            expect(removeNgLayersSpy).not.toHaveBeenCalled()
-            expect(addNgLayerSpy).not.toHaveBeenCalled()
+            expect(dispatchSpy).not.toHaveBeenCalled()
           })
 
           // TODO having a difficult time getting snackbar harness
@@ -346,15 +341,28 @@ describe('> nehubaViewerGlue.component.ts', () => {
       })
 
       it('> should call addNgLayer', () => {
-        expect(removeNgLayersSpy).not.toHaveBeenCalled()
-        expect(addNgLayerSpy).toHaveBeenCalledTimes(1)
+        expect(dispatchSpy).toHaveBeenCalledTimes(1)
+        const arg = dispatchSpy.calls.argsFor(0)
+        expect(arg.length).toEqual(1)
+        expect(arg[0].type).toBe(atlasAppearance.actions.addCustomLayer.type)
       })
       it('> on repeated input, both remove nglayer and remove ng layer called', async () => {
         const cmp = fixture.componentInstance
         await cmp.handleFileDrop(input)
 
-        expect(removeNgLayersSpy).toHaveBeenCalledTimes(1)
-        expect(addNgLayerSpy).toHaveBeenCalledTimes(2)
+        expect(dispatchSpy).toHaveBeenCalledTimes(3)
+        
+        const arg0 = dispatchSpy.calls.argsFor(0)
+        expect(arg0.length).toEqual(1)
+        expect(arg0[0].type).toBe(atlasAppearance.actions.addCustomLayer.type)
+
+        const arg1 = dispatchSpy.calls.argsFor(1)
+        expect(arg1.length).toEqual(1)
+        expect(arg1[0].type).toBe(atlasAppearance.actions.removeCustomLayer.type)
+
+        const arg2 = dispatchSpy.calls.argsFor(2)
+        expect(arg2.length).toEqual(1)
+        expect(arg2[0].type).toBe(atlasAppearance.actions.addCustomLayer.type)
       })
     })
   })
diff --git a/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.component.ts b/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.component.ts
index f8df7f66789021d41fe5f0435e6b24ae83a27c39..0c7b084eb8dab41764d88d913578e8eeb843478b 100644
--- a/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.component.ts
+++ b/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.component.ts
@@ -1,29 +1,14 @@
-import { AfterViewInit, Component, ElementRef, EventEmitter, Inject, Input, OnChanges, OnDestroy, Optional, Output, SimpleChanges, TemplateRef, ViewChild } from "@angular/core";
+import { AfterViewInit, ChangeDetectionStrategy, Component, EventEmitter, Inject, OnDestroy, Optional, Output, TemplateRef, ViewChild } from "@angular/core";
 import { select, Store } from "@ngrx/store";
-import { asyncScheduler, combineLatest, fromEvent, merge, NEVER, Observable, of, Subject } from "rxjs";
-import { ngViewerActionCycleViews, ngViewerActionToggleMax } from "src/services/state/ngViewerState/actions";
+import { Subscription } from "rxjs";
 import { ClickInterceptor, CLICK_INTERCEPTOR_INJECTOR } from "src/util";
-import { uiStateMouseOverSegmentsSelector } from "src/services/state/uiState/selectors";
-import { debounceTime, distinctUntilChanged, filter, map, mapTo, scan, shareReplay, startWith, switchMap, switchMapTo, take, tap, throttleTime } from "rxjs/operators";
-import { viewerStateAddUserLandmarks, viewerStateChangeNavigation, viewerStateMouseOverCustomLandmark, viewerStateSelectRegionWithIdDeprecated, viewerStateSetSelectedRegions, viewreStateRemoveUserLandmarks } from "src/services/state/viewerState/actions";
-import { ngViewerSelectorPanelOrder, ngViewerSelectorPanelMode } from "src/services/state/ngViewerState/selectors";
-import { viewerStateCustomLandmarkSelector, viewerStateNavigationStateSelector } from "src/services/state/viewerState/selectors";
-import { serialiseParcellationRegion } from 'common/util'
-import { ARIA_LABELS, IDS, QUICKTOUR_DESC } from 'common/constants'
-import { PANELS } from "src/services/state/ngViewerState/constants";
-import { LoggingService } from "src/logging";
-
-import { getMultiNgIdsRegionsLabelIndexMap, SET_MESHES_TO_LOAD } from "../constants";
+import { distinctUntilChanged, startWith } from "rxjs/operators";
+import { ARIA_LABELS } from 'common/constants'
 import { EnumViewerEvt, IViewer, TViewerEvent } from "../../viewer.interface";
-import { NehubaViewerUnit } from "../nehubaViewer/nehubaViewer.component";
 import { NehubaViewerContainerDirective, TMouseoverEvent } from "../nehubaViewerInterface/nehubaViewerInterface.directive";
-import { cvtNavigationObjToNehubaConfig, getFourPanel, getHorizontalOneThree, getSinglePanel, getVerticalOneThree, scanSliceViewRenderFn, takeOnePipe } from "../util";
-import { API_SERVICE_SET_VIEWER_HANDLE_TOKEN, TSetViewerHandle } from "src/atlasViewer/atlasViewer.apiService.service";
-import { MouseHoverDirective } from "src/mouseoverModule";
 import { NehubaMeshService } from "../mesh.service";
-import { IQuickTourData } from "src/ui/quickTour/constrants";
-import { NehubaLayerControlService, IColorMap, SET_COLORMAP_OBS, SET_LAYER_VISIBILITY } from "../layerCtrl.service";
-import { getExportNehuba, getUuid, switchMapWaitFor } from "src/util/fn";
+import { NehubaLayerControlService, SET_COLORMAP_OBS, SET_LAYER_VISIBILITY } from "../layerCtrl.service";
+import { getExportNehuba, getUuid } from "src/util/fn";
 import { INavObj } from "../navigation.service";
 import { NG_LAYER_CONTROL, SET_SEGMENT_VISIBILITY } from "../layerCtrl.service/layerCtrl.util";
 import { MatSnackBar } from "@angular/material/snack-bar";
@@ -31,6 +16,10 @@ import { getShader } from "src/util/constants";
 import { EnumColorMapName } from "src/util/colorMaps";
 import { MatDialog } from "@angular/material/dialog";
 import { AtlasWorkerService } from "src/atlasViewer/atlasViewer.workerService.service";
+import { SapiRegionModel } from "src/atlasComponents/sapi";
+import { NehubaConfig, getParcNgId, getRegionLabelIndex } from "../config.service";
+import { SET_MESHES_TO_LOAD } from "../constants";
+import { annotation, atlasAppearance, atlasSelection, userInteraction } from "src/state";
 
 export const INVALID_FILE_INPUT = `Exactly one (1) nifti file is required!`
 
@@ -68,241 +57,123 @@ export const INVALID_FILE_INPUT = `Exactly one (1) nifti file is required!`
       useFactory: (layerCtrl: NehubaLayerControlService) => layerCtrl.ngLayersController$,
       deps: [ NehubaLayerControlService ]
     },
-    NehubaLayerControlService
-  ]
+    NehubaLayerControlService,
+    NehubaMeshService,
+  ],
+  changeDetection: ChangeDetectionStrategy.OnPush
 })
 
-export class NehubaGlueCmp implements IViewer<'nehuba'>, OnChanges, OnDestroy, AfterViewInit {
+export class NehubaGlueCmp implements IViewer<'nehuba'>, OnDestroy, AfterViewInit {
 
   @ViewChild('layerCtrlTmpl', { read: TemplateRef }) layerCtrlTmpl: TemplateRef<any>
 
   public ARIA_LABELS = ARIA_LABELS
-  public IDS = IDS
-
-  private currentPanelMode: PANELS
 
   @ViewChild(NehubaViewerContainerDirective, { static: true })
   public nehubaContainerDirective: NehubaViewerContainerDirective
 
-  @ViewChild(MouseHoverDirective, { static: true })
-  private mouseoverDirective: MouseHoverDirective
-
-  public viewerLoaded: boolean = false
-
-  private onhoverSegments = []
+  private onhoverSegments: SapiRegionModel[] = []
   private onDestroyCb: (() => void)[] = []
-  private viewerUnit: NehubaViewerUnit
-  private multiNgIdsRegionsLabelIndexMap: Map<string, Map<number, any>>
-
-  @Input()
-  public selectedParcellation: any
-
-  @Input()
-  public selectedTemplate: any
-
-  private navigation: any
-
-  private newViewer$ = new Subject()
-
-  public showPerpsectiveScreen$: Observable<string>
-  public sliceViewLoadingMain$: Observable<[boolean, boolean, boolean]>
-  private sliceRenderEvent$: Observable<CustomEvent>
-  public perspectiveViewLoading$: Observable<string|null>
-  public hoveredPanelIndices$: Observable<number>
-  private viewPanelWeakMap = new WeakMap<HTMLElement, number>()
-  public viewPanels: [HTMLElement, HTMLElement, HTMLElement, HTMLElement] = [null, null, null, null]
-  private findPanelIndex = (panel: HTMLElement) => this.viewPanelWeakMap.get(panel)
-  public nanometersToOffsetPixelsFn: Array<(...arg) => any> = []
-
-  public quickTourSliceViewSlide: IQuickTourData = {
-    order: 1,
-    description: QUICKTOUR_DESC.SLICE_VIEW,
-  }
-
-  public quickTour3dViewSlide: IQuickTourData = {
-    order: 2,
-    description: QUICKTOUR_DESC.PERSPECTIVE_VIEW,
-  }
+  private multiNgIdsRegionsLabelIndexMap = new Map<string, Map<number, SapiRegionModel>>()
 
-  public quickTourIconsSlide: IQuickTourData = {
-    order: 3,
-    description: QUICKTOUR_DESC.VIEW_ICONS,
-  }
+  public nehubaConfig: NehubaConfig
 
-  public customLandmarks$: Observable<any> = this.store$.pipe(
-    select(viewerStateCustomLandmarkSelector),
-    map(lms => lms.map(lm => ({
-      ...lm,
-      geometry: {
-        position: lm.position
-      }
-    }))),
+  public customLandmarks$ = this.store$.pipe(
+    select(annotation.selectors.annotations),
   )
 
-  public filterCustomLandmark(lm: any){
-    return !!lm['showInSliceView']
-  }
-
-  public panelOrder$ = this.store$.pipe(
-    select(ngViewerSelectorPanelOrder),
-    distinctUntilChanged(),
-    shareReplay(1),
-  )
-
-  ngOnChanges(sc: SimpleChanges){
+  private nehubaContainerSub: Subscription[] = []
+  private setupNehubaEvRelay() {
+    while (this.nehubaContainerSub.length > 0) this.nehubaContainerSub.pop().unsubscribe()
+    
+    if (!this.nehubaContainerDirective) return
     const {
-      selectedParcellation,
-      selectedTemplate
-    } = sc
-    if (selectedTemplate) {
-      if (selectedTemplate?.currentValue?.['@id'] !== selectedTemplate?.previousValue?.['@id']) {
-
-        if (selectedTemplate?.previousValue) {
-          this.unloadTmpl(selectedTemplate?.previousValue)
-        }
-        if (selectedTemplate?.currentValue?.['@id']) {
-          this.loadTmpl(selectedTemplate.currentValue, selectedParcellation.currentValue)
-        }
-      }
-    }else if (selectedParcellation && selectedParcellation.currentValue !== selectedParcellation.previousValue) {
-      this.loadParc(selectedParcellation.currentValue)
-    }
-  }
-
-  ngAfterViewInit(){
-    this.setQuickTourPos()
-
-    const { 
-      mouseOverSegments = NEVER,
-      navigationEmitter = NEVER,
-      mousePosEmitter = NEVER,
-    } = this.nehubaContainerDirective || {}
-    const sub = combineLatest([
       mouseOverSegments,
       navigationEmitter,
       mousePosEmitter,
-    ]).pipe(
-      throttleTime(16, asyncScheduler, { trailing: true })
-    ).subscribe(([ seg, nav, mouse ]: [ TMouseoverEvent [], INavObj, { real: number[], voxel: number[] } ]) => {
-      this.viewerEvent.emit({
-        type: EnumViewerEvt.VIEWER_CTX,
-        data: {
-          viewerType: 'nehuba',
-          payload: {
-            nav,
-            mouse,
-            nehuba: seg.map(v => {
-              return {
-                layerName: v.layer.name,
-                labelIndices: [ Number(v.segmentId) ]
-              }
-            })
-          }
-        }
-      })
-    })
-    this.onDestroyCb.push(
-      () => sub.unsubscribe()
-    )
-  }
+    } = this.nehubaContainerDirective
 
-  ngOnDestroy() {
-    while (this.onDestroyCb.length) this.onDestroyCb.pop()()
-  }
+    this.nehubaContainerSub.push(
 
-  private loadParc(parcellation: any) {
-    /**
-     * parcellaiton may be undefined
-     */
-    if ( !(parcellation && parcellation.regions)) {
-      return
-    }
+      mouseOverSegments.pipe(
+        startWith(null as TMouseoverEvent[])
+      ).subscribe(seg => {
+        this.viewerEvent.emit({
+          type: EnumViewerEvt.VIEWER_CTX,
+          data: {
+            viewerType: 'nehuba',
+            payload: {
+              nehuba: seg && seg.map(v => {
+                return {
+                  layerName: v.layer.name,
+                  labelIndices: [ Number(v.segmentId) ],
+                  regions: (() => {
+                    const map = this.multiNgIdsRegionsLabelIndexMap.get(v.layer.name)
+                    if (!map) return []
+                    return [map.get(Number(v.segmentId))]
+                  })()
+                }
+              })
+            }
+          }
+        })
+      }),
 
-    this.multiNgIdsRegionsLabelIndexMap = getMultiNgIdsRegionsLabelIndexMap(parcellation)
+      navigationEmitter.pipe(
+        startWith(null as INavObj)
+      ).subscribe(nav => {
+        this.viewerEvent.emit({
+          type: EnumViewerEvt.VIEWER_CTX,
+          data: {
+            viewerType: 'nehuba',
+            payload: {
+              nav
+            }
+          }
+        })
+      }),
 
-    this.viewerUnit.multiNgIdsLabelIndexMap = this.multiNgIdsRegionsLabelIndexMap
-    this.viewerUnit.auxilaryMeshIndices = parcellation.auxillaryMeshIndices || []
+      mousePosEmitter.pipe(
+        startWith(null as {
+          voxel: number[]
+          real: number[]
+        })
+      ).subscribe(mouse => {
+        this.viewerEvent.emit({
+          type: EnumViewerEvt.VIEWER_CTX,
+          data: {
+            viewerType: 'nehuba',
+            payload: {
+              mouse
+            }
+          }
+        })
+      })
+    )
 
+    this.onDestroyCb.push(
+      () => {
+        if (this.nehubaContainerSub) {
+          while(this.nehubaContainerSub.length > 0) this.nehubaContainerSub.pop().unsubscribe()
+        }
+      }
+    )
   }
 
-  private unloadTmpl(tmpl: any) {
-    /**
-     * clear existing container
-     */
-    this.viewerUnit = null
-    this.nehubaContainerDirective.clear()
+  ngAfterViewInit(): void {
+    this.setupNehubaEvRelay()
+  }
 
-    /* on selecting of new template, remove additional nglayers */
-    const baseLayerNames = Object.keys(tmpl.nehubaConfig.dataset.initialNgState.layers)
-    this.layerCtrlService.removeNgLayers(baseLayerNames)
+  ngOnDestroy(): void {
+    while (this.onDestroyCb.length) this.onDestroyCb.pop()()
   }
 
-  private async loadTmpl(_template: any, parcellation: any) {
 
-    if (!_template) return
+  private disposeViewer() {
     /**
-     * recalcuate zoom
+     * clear existing container
      */
-    const template = (() => {
-
-      const deepCopiedState = JSON.parse(JSON.stringify(_template))
-      const initialNgState = deepCopiedState.nehubaConfig.dataset.initialNgState
-
-      if (!initialNgState || !this.navigation) {
-        return deepCopiedState
-      }
-      const overwritingInitState = this.navigation
-        ? cvtNavigationObjToNehubaConfig(this.navigation, initialNgState)
-        : {}
-
-      deepCopiedState.nehubaConfig.dataset.initialNgState = {
-        ...initialNgState,
-        ...overwritingInitState,
-      }
-      return deepCopiedState
-    })()
-
-    this.nehubaContainerDirective.createNehubaInstance(template)
-    this.viewerUnit = this.nehubaContainerDirective.nehubaViewerInstance
-    this.sliceRenderEvent$.pipe(
-      takeOnePipe()
-    ).subscribe(ev => {
-
-      for (const idx of [0, 1, 2]) {
-        const e = ev[idx] as CustomEvent
-        const el = e.target as HTMLElement
-        this.viewPanelWeakMap.set(el, idx)
-        this.viewPanels[idx] = el
-        this.nanometersToOffsetPixelsFn[idx] = e.detail.nanometersToOffsetPixels
-      }
-    })
-    const foundParcellation = parcellation
-      && template?.parcellations?.find(p => parcellation.name === p.name)
-    this.loadParc(foundParcellation || template.parcellations[0])
-
-    const nehubaConfig = template.nehubaConfig
-    const initialSpec = nehubaConfig.dataset.initialNgState
-    const {layers} = initialSpec
-
-    const dispatchLayers = Object.keys(layers).map(key => {
-      const layer = {
-        name : key,
-        source : layers[key].source,
-        mixability : layers[key].type === 'image'
-          ? 'base'
-          : 'mixable',
-        visible : typeof layers[key].visible === 'undefined'
-          ? true
-          : layers[key].visible,
-        transform : typeof layers[key].transform === 'undefined'
-          ? null
-          : layers[key].transform,
-      }
-      return layer
-    })
-
-    this.layerCtrlService.addNgLayer(dispatchLayers)
-    this.newViewer$.next(true)
+    this.nehubaContainerDirective && this.nehubaContainerDirective.clear()
   }
 
   @Output()
@@ -310,14 +181,11 @@ export class NehubaGlueCmp implements IViewer<'nehuba'>, OnChanges, OnDestroy, A
 
   constructor(
     private store$: Store<any>,
-    private el: ElementRef,
-    private log: LoggingService,
     private snackbar: MatSnackBar,
     private dialog: MatDialog,
     private worker: AtlasWorkerService,
+    private layerCtrlService: NehubaLayerControlService,
     @Optional() @Inject(CLICK_INTERCEPTOR_INJECTOR) clickInterceptor: ClickInterceptor,
-    @Optional() @Inject(API_SERVICE_SET_VIEWER_HANDLE_TOKEN) setViewerHandle: TSetViewerHandle,
-    @Optional() private layerCtrlService: NehubaLayerControlService,
   ){
 
     /**
@@ -330,346 +198,47 @@ export class NehubaGlueCmp implements IViewer<'nehuba'>, OnChanges, OnDestroy, A
       this.onDestroyCb.push(() => deregister(selOnhoverRegion))
     }
 
+    const onATPClear = this.store$.pipe(
+      atlasSelection.fromRootStore.distinctATP()
+    ).subscribe(this.disposeViewer.bind(this))
+    this.onDestroyCb.push(() => onATPClear.unsubscribe())
+    
     /**
-     * on layout change
+     * subscribe to ngIdtolblIdxToRegion
      */
-    const redrawLayoutSub = combineLatest([
-      this.store$.pipe(
-        select(ngViewerSelectorPanelMode),
-        distinctUntilChanged(),
-        shareReplay(1),
-      ),
-      this.panelOrder$,
-    ]).pipe(
-      switchMap(this.waitForNehuba.bind(this))
-    ).subscribe(([mode, panelOrder]) => {
-      
-      this.currentPanelMode = mode
-
-      const viewPanels = panelOrder.split('').map(v => Number(v)).map(idx => this.viewPanels[idx]) as [HTMLElement, HTMLElement, HTMLElement, HTMLElement]
-
-      /**
-       * TODO smarter with event stream
-       */
-      if (!viewPanels.every(v => !!v)) {
-        this.log.error(`on relayout, not every view panel is populated. This should not occur!`)
-        return
-      }
-
-      switch (mode) {
-      case PANELS.H_ONE_THREE: {
-        const element = this.removeExistingPanels()
-        const newEl = getHorizontalOneThree(viewPanels)
-        element.appendChild(newEl)
-        break;
-      }
-      case PANELS.V_ONE_THREE: {
-        const element = this.removeExistingPanels()
-        const newEl = getVerticalOneThree(viewPanels)
-        element.appendChild(newEl)
-        break;
-      }
-      case PANELS.FOUR_PANEL: {
-        const element = this.removeExistingPanels()
-        const newEl = getFourPanel(viewPanels)
-        element.appendChild(newEl)
-        break;
-      }
-      case PANELS.SINGLE_PANEL: {
-        const element = this.removeExistingPanels()
-        const newEl = getSinglePanel(viewPanels)
-        element.appendChild(newEl)
-        break;
-      }
-      default:
-      }
-      for (const panel of viewPanels) {
-        (panel as HTMLElement).classList.add('neuroglancer-panel')
+    const ngIdSub = this.layerCtrlService.selectedATPR$.subscribe(({ atlas, parcellation, template, regions }) => {
+      this.multiNgIdsRegionsLabelIndexMap.clear()
+      for (const r of regions) {
+        const ngId = getParcNgId(atlas, template, parcellation, r)
+        if (!ngId) continue
+        if (!this.multiNgIdsRegionsLabelIndexMap.has(ngId)) {
+          this.multiNgIdsRegionsLabelIndexMap.set(ngId, new Map())
+        }
+        const labelIndex = getRegionLabelIndex(atlas, template, parcellation, r)
+        if (!labelIndex) continue
+        this.multiNgIdsRegionsLabelIndexMap.get(ngId).set(labelIndex, r)
       }
-
-      // TODO needed to redraw?
-      // see https://trello.com/c/oJOnlc6v/60-enlarge-panel-allow-user-rearrange-panel-position
-      // further investigaation required
-      this.nehubaContainerDirective.nehubaViewerInstance.redraw()
     })
-    this.onDestroyCb.push(() => redrawLayoutSub.unsubscribe())
+    this.onDestroyCb.push(() => ngIdSub.unsubscribe())
 
     /**
      * on hover segment
      */
     const onhovSegSub = this.store$.pipe(
-      select(uiStateMouseOverSegmentsSelector),
+      select(userInteraction.selectors.mousingOverRegions),
       distinctUntilChanged(),
     ).subscribe(arr => {
-      const segments = arr.map(({ segment }) => segment).filter(v => !!v)
-      this.onhoverSegments = segments
+      this.onhoverSegments = arr
     })
     this.onDestroyCb.push(() => onhovSegSub.unsubscribe())
-
-    const perspectiveRenderEvSub = this.newViewer$.pipe(
-      switchMapTo(fromEvent<CustomEvent>(this.el.nativeElement, 'perpspectiveRenderEvent').pipe(
-        take(1)
-      ))
-    ).subscribe(ev => {
-      const perspPanel = ev.target as HTMLElement
-      this.viewPanels[3] = perspPanel
-      this.viewPanelWeakMap.set(perspPanel, 3)
-    })
-    this.onDestroyCb.push(() => perspectiveRenderEvSub.unsubscribe())
-
-    this.sliceRenderEvent$ = fromEvent<CustomEvent>(this.el.nativeElement, 'sliceRenderEvent')
-    this.sliceViewLoadingMain$ = this.sliceRenderEvent$.pipe(
-      scan(scanSliceViewRenderFn, [null, null, null]),
-      startWith([true, true, true] as [boolean, boolean, boolean]),
-      shareReplay(1),
-    )
-
-    this.perspectiveViewLoading$ = fromEvent(this.el.nativeElement, 'perpspectiveRenderEvent').pipe(
-      filter((event: CustomEvent) => event?.detail?.lastLoadedMeshId ),
-      map(event => {
-
-        /**
-         * TODO dig into event detail to see if the exact mesh loaded
-         */
-        const { meshesLoaded, meshFragmentsLoaded: _meshFragmentsLoaded, lastLoadedMeshId: _lastLoadedMeshId } = (event as any).detail
-        return meshesLoaded >= this.nehubaContainerDirective.nehubaViewerInstance.numMeshesToBeLoaded
-          ? null
-          : 'Loading meshes ...'
-      }),
-      distinctUntilChanged()
-    )
-
-    this.showPerpsectiveScreen$ = this.newViewer$.pipe(
-      switchMapTo(this.sliceRenderEvent$.pipe(
-        scan((acc, curr) => {
-
-          /**
-           * if at any point, all chunks have been loaded, always return loaded state
-           */
-          if (acc.every(v => v === 0)) return [0, 0, 0]
-          const { detail = {}, target } = curr || {}
-          const { missingChunks = -1, missingImageChunks = -1 } = detail
-          const idx = this.findPanelIndex(target as HTMLElement)
-          const returnAcc = [...acc]
-          if (idx >= 0) {
-            returnAcc[idx] = missingChunks + missingImageChunks
-          }
-          return returnAcc
-        }, [-1, -1, -1]),
-        map(arr => {
-          let sum = 0
-          let uncertain = false
-          for (const num of arr) {
-            if (num < 0) {
-              uncertain = true
-            } else {
-              sum += num
-            }
-          }
-          return sum > 0
-            ? `Loading ${sum}${uncertain ? '+' : ''} chunks ...`
-            : null
-        }),
-        distinctUntilChanged(),
-        startWith('Loading ...'),
-        throttleTime(100, asyncScheduler, { leading: true, trailing: true }),
-        shareReplay(1),
-      ))
-    )
-
-    this.hoveredPanelIndices$ = fromEvent(this.el.nativeElement, 'mouseover').pipe(
-      switchMap((ev: MouseEvent) => merge(
-        of(this.findPanelIndex(ev.target as HTMLElement)),
-        fromEvent(this.el.nativeElement, 'mouseout').pipe(
-          mapTo(null),
-        ),
-      )),
-      debounceTime(20),
-      shareReplay(1),
-    )
-
-    const setupViewerApiSub = this.newViewer$.pipe(
-      tap(() => {
-        setViewerHandle && setViewerHandle(null)
-      }),
-      switchMap(this.waitForNehuba.bind(this))
-    ).subscribe(() => {
-      setViewerHandle && setViewerHandle({
-        setNavigationLoc : (coord, realSpace?) => this.nehubaContainerDirective.nehubaViewerInstance.setNavigationState({
-          position : coord,
-          positionReal : typeof realSpace !== 'undefined' ? realSpace : true,
-        }),
-        /* TODO introduce animation */
-        moveToNavigationLoc : (coord, _realSpace?) => {
-          this.store$.dispatch(
-            viewerStateChangeNavigation({
-              navigation: {
-                position: coord,
-                animation: {},
-              }
-            })
-          )
-        },
-        setNavigationOri : (quat) => this.nehubaContainerDirective.nehubaViewerInstance.setNavigationState({
-          orientation : quat,
-        }),
-        /* TODO introduce animation */
-        moveToNavigationOri : (quat) => this.nehubaContainerDirective.nehubaViewerInstance.setNavigationState({
-          orientation : quat,
-        }),
-        showSegment : (_labelIndex) => {
-          /**
-           * TODO reenable with updated select_regions api
-           */
-          this.log.warn(`showSegment is temporarily disabled`)
-
-          // if(!this.selectedRegionIndexSet.has(labelIndex))
-          //   this.store.dispatch({
-          //     type : SELECT_REGIONS,
-          //     selectRegions :  [labelIndex, ...this.selectedRegionIndexSet]
-          //   })
-        },
-        add3DLandmarks : landmarks => {
-          // TODO check uniqueness of ID
-          if (!landmarks.every(l => !!l.id)) {
-            throw new Error('every landmarks needs to be identified with the id field')
-          }
-          if (!landmarks.every(l => !!l.position)) {
-            throw new Error('every landmarks needs to have position defined')
-          }
-          if (!landmarks.every(l => l.position.constructor === Array) || !landmarks.every(l => l.position.every(v => !isNaN(v))) || !landmarks.every(l => l.position.length == 3)) {
-            throw new Error('position needs to be a length 3 tuple of numbers ')
-          }
-
-          this.store$.dispatch(viewerStateAddUserLandmarks({
-            landmarks
-          }))
-        },
-        remove3DLandmarks : landmarkIds => {
-          this.store$.dispatch(viewreStateRemoveUserLandmarks({
-            payload: { landmarkIds }
-          }))
-        },
-        hideSegment : (_labelIndex) => {
-          /**
-           * TODO reenable with updated select_regions api
-           */
-          this.log.warn(`hideSegment is temporarily disabled`)
-
-          // if(this.selectedRegionIndexSet.has(labelIndex)){
-          //   this.store.dispatch({
-          //     type :SELECT_REGIONS,
-          //     selectRegions : [...this.selectedRegionIndexSet].filter(num=>num!==labelIndex)
-          //   })
-          // }
-        },
-        showAllSegments : () => {
-          const selectRegionIds = []
-          this.multiNgIdsRegionsLabelIndexMap.forEach((map, ngId) => {
-            Array.from(map.keys()).forEach(labelIndex => {
-              selectRegionIds.push(serialiseParcellationRegion({ ngId, labelIndex }))
-            })
-          })
-          this.store$.dispatch(viewerStateSelectRegionWithIdDeprecated({
-            selectRegionIds
-          }))
-        },
-        hideAllSegments : () => {
-          this.store$.dispatch(viewerStateSelectRegionWithIdDeprecated({
-            selectRegionIds: []
-          }))
-        },
-        getLayersSegmentColourMap: () => {
-          if (!this.layerCtrlService) {
-            throw new Error(`layerCtrlService not injected. Cannot call getLayersSegmentColourMap`)
-          }
-          const newMainMap = new Map()
-          for (const key in this.layerCtrlService.activeColorMap) {
-            const obj = this.layerCtrlService.activeColorMap[key]
-            const m = new Map()
-            newMainMap.set(key, m)
-            for (const labelIndex in obj) {
-              m.set(Number(labelIndex), obj[labelIndex])
-            }
-          }
-          return newMainMap
-        },
-        applyLayersColourMap: (map) => {
-          if (!this.layerCtrlService) {
-            throw new Error(`layerCtrlService not injected. Cannot call getLayersSegmentColourMap`)
-          }
-          const obj: IColorMap = {}
-          for (const [ key, value ] of map.entries()) {
-            const cmap = obj[key] = {}
-            for (const [ labelIdx, rgb ] of value.entries()) {
-              cmap[Number(labelIdx)] = rgb
-            }
-          }
-          this.layerCtrlService.overwriteColorMap$.next(obj)
-        },
-        /**
-         * TODO go via layerCtrl.service
-         */
-        loadLayer : (layerObj) => this.nehubaContainerDirective.nehubaViewerInstance.loadLayer(layerObj),
-        removeLayer : (condition) => this.nehubaContainerDirective.nehubaViewerInstance.removeLayer(condition),
-        setLayerVisibility : (condition, visible) => this.nehubaContainerDirective.nehubaViewerInstance.setLayerVisibility(condition, visible),
-        mouseEvent : merge(
-          fromEvent(this.el.nativeElement, 'click').pipe(
-            map((ev: MouseEvent) => ({eventName : 'click', event: ev})),
-          ),
-          fromEvent(this.el.nativeElement, 'mousemove').pipe(
-            map((ev: MouseEvent) => ({eventName : 'mousemove', event: ev})),
-          ),
-          /**
-           * neuroglancer prevents propagation, so use capture instead
-           */
-          fromEvent(this.el.nativeElement, 'mousedown', { capture: true }).pipe(
-            map((event: MouseEvent) => {
-              return {
-                eventName: 'mousedown',
-                event
-              }
-            })
-          ),
-          fromEvent(this.el.nativeElement, 'mouseup').pipe(
-            map((ev: MouseEvent) => ({eventName : 'mouseup', event: ev})),
-          ),
-        ) ,
-        mouseOverNehuba : of(null).pipe(
-          tap(() => console.warn('mouseOverNehuba observable is becoming deprecated. use mouseOverNehubaLayers instead.')),
-        ),
-        mouseOverNehubaLayers: this.mouseoverDirective.currentOnHoverObs$.pipe(
-          map(({ segments }) => segments)
-        ),
-        mouseOverNehubaUI: this.mouseoverDirective.currentOnHoverObs$.pipe(
-          map(({annotation, landmark, segments, userLandmark: customLandmark }) => ({annotation, segments, landmark, customLandmark })),
-          shareReplay(1),
-        ),
-        getNgHash : this.nehubaContainerDirective.nehubaViewerInstance.getNgHash,
-      })
-    })
-    this.onDestroyCb.push(() => setupViewerApiSub.unsubscribe())
-
-    // listen to navigation change from store
-    const navSub = this.store$.pipe(
-      select(viewerStateNavigationStateSelector)
-    ).subscribe(nav => this.navigation = nav)
-    this.onDestroyCb.push(() => navSub.unsubscribe())
   }
 
-  handleCycleViewEvent(){
-    if (this.currentPanelMode !== PANELS.SINGLE_PANEL) return
-    this.store$.dispatch(
-      ngViewerActionCycleViews()
-    )
-  }
 
-  handleViewerLoadedEvent(flag: boolean) {
+  handleViewerLoadedEvent(flag: boolean): void {
     this.viewerEvent.emit({
       type: EnumViewerEvt.VIEWERLOADED,
       data: flag
     })
-    this.viewerLoaded = flag
   }
 
   private selectHoveredRegion(_ev: any): boolean{
@@ -679,48 +248,13 @@ export class NehubaGlueCmp implements IViewer<'nehuba'>, OnChanges, OnDestroy, A
     const trueOnhoverSegments = this.onhoverSegments && this.onhoverSegments.filter(v => typeof v === 'object')
     if (!trueOnhoverSegments || (trueOnhoverSegments.length === 0)) return true
     this.store$.dispatch(
-      viewerStateSetSelectedRegions({
-        selectRegions: trueOnhoverSegments.slice(0, 1)
+      atlasSelection.actions.selectRegion({
+        region: trueOnhoverSegments[0]
       })
     )
     return true
   }
 
-  private waitForNehuba = switchMapWaitFor({
-    condition: () => !!(this.nehubaContainerDirective?.isReady())
-  }) 
-
-  public toggleMaximiseMinimise(index: number) {
-    this.store$.dispatch(ngViewerActionToggleMax({
-      payload: { index }
-    }))
-  }
-
-  public zoomNgView(panelIndex: number, factor: number) {
-    const ngviewer = this.nehubaContainerDirective?.nehubaViewerInstance?.nehubaViewer?.ngviewer
-    if (!ngviewer) throw new Error(`ngviewer not defined!`)
-
-    /**
-     * panelIndex < 3 === slice view
-     */
-    if (panelIndex < 3) {
-      /**
-       * factor > 1 === zoom out
-       */
-      ngviewer.navigationState.zoomBy(factor)
-    } else {
-      ngviewer.perspectiveNavigationState.zoomBy(factor)
-    }
-  }
-
-  private removeExistingPanels() {
-    const element = this.nehubaContainerDirective.nehubaViewerInstance.nehubaViewer.ngviewer.layout.container.componentValue.element as HTMLElement
-    while (element.childElementCount > 0) {
-      element.removeChild(element.firstElementChild)
-    }
-    return element
-  }
-
   private droppedLayerNames: {
     layerName: string
     resourceUrl: string
@@ -728,11 +262,16 @@ export class NehubaGlueCmp implements IViewer<'nehuba'>, OnChanges, OnDestroy, A
   private dismissAllAddedLayers(){
     while (this.droppedLayerNames.length) {
       const { resourceUrl, layerName } = this.droppedLayerNames.pop()
-      this.layerCtrlService.removeNgLayers([ layerName ])
+      this.store$.dispatch(
+        atlasAppearance.actions.removeCustomLayer({
+          id: layerName
+        })
+      )
+      
       URL.revokeObjectURL(resourceUrl)
     }
   }
-  public async handleFileDrop(files: File[]){
+  public async handleFileDrop(files: File[]): Promise<void> {
     if (files.length !== 1) {
       this.snackbar.open(INVALID_FILE_INPUT, 'Dismiss', {
         duration: 5000
@@ -759,7 +298,7 @@ export class NehubaGlueCmp implements IViewer<'nehuba'>, OnChanges, OnDestroy, A
     }
 
     try {
-      const { result, ...other } = await this.worker.sendMessage({
+      const { result } = await this.worker.sendMessage({
         method: 'PROCESS_NIFTI',
         param: {
           nifti: outbuf
@@ -774,17 +313,21 @@ export class NehubaGlueCmp implements IViewer<'nehuba'>, OnChanges, OnDestroy, A
         layerName: randomUuid,
         resourceUrl: url
       })
-      this.layerCtrlService.addNgLayer([{
-        name: randomUuid,
-        mixability: 'mixable',
-        source: `nifti://${url}`,
-        shader: getShader({
-          colormap: EnumColorMapName.MAGMA,
-          lowThreshold: meta.min || 0,
-          highThreshold: meta.max || 1
-        })
-      }])
 
+      this.store$.dispatch(
+        atlasAppearance.actions.addCustomLayer({
+          customLayer: {
+            id: randomUuid,
+            source: `nifti://${url}`,
+            shader: getShader({
+              colormap: EnumColorMapName.MAGMA,
+              lowThreshold: meta.min || 0,
+              highThreshold: meta.max || 1
+            }),
+            clType: 'customlayer/nglayer'
+          }
+        })
+      )
       this.dialog.open(
         this.layerCtrlTmpl,
         {
@@ -817,65 +360,4 @@ export class NehubaGlueCmp implements IViewer<'nehuba'>, OnChanges, OnDestroy, A
       })
     }
   }
-
-
-  public returnTruePos(quadrant: number, data: any) {
-    const pos = quadrant > 2
-      ? [0, 0, 0]
-      : this.nanometersToOffsetPixelsFn && this.nanometersToOffsetPixelsFn[quadrant]
-        ? this.nanometersToOffsetPixelsFn[quadrant](data.geometry.position.map(n => n * 1e6))
-        : [0, 0, 0]
-    return pos
-  }
-
-  public getPositionX(quadrant: number, data: any) {
-    return this.returnTruePos(quadrant, data)[0]
-  }
-  public getPositionY(quadrant: number, data: any) {
-    return this.returnTruePos(quadrant, data)[1]
-  }
-  public getPositionZ(quadrant: number, data: any) {
-    return this.returnTruePos(quadrant, data)[2]
-  }
-
-  public handleMouseEnterCustomLandmark(lm) {
-    this.store$.dispatch(
-      viewerStateMouseOverCustomLandmark({
-        payload: { userLandmark: lm }
-      })
-    )
-  }
-
-  public handleMouseLeaveCustomLandmark(_lm) {
-    this.store$.dispatch(
-      viewerStateMouseOverCustomLandmark({
-        payload: { userLandmark: null }
-      })
-    )
-  }
-
-  public quickTourOverwritingPos = {
-    'dialog': {
-      left: '0px',
-      top: '0px',
-    },
-    'arrow': {
-      left: '0px',
-      top: '0px',
-    }
-  }
-
-  setQuickTourPos(){
-    const { innerWidth, innerHeight } = window
-    this.quickTourOverwritingPos = {
-      'dialog': {
-        left: `${innerWidth / 2}px`,
-        top: `${innerHeight / 2}px`,
-      },
-      'arrow': {
-        left: `${innerWidth / 2 - 48}px`,
-        top: `${innerHeight / 2 - 48}px`,
-      }
-    }
-  }
 }
diff --git a/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.style.css b/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.style.css
index c859c65655905d25cd4f65fe332d3c3567cc6868..5d085f9333a8ddd06516b3bdebb5f43c6fb80640 100644
--- a/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.style.css
+++ b/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.style.css
@@ -1,25 +1,4 @@
 
-.opacity-crossfade
-{
-  transition: opacity 170ms ease-in-out,
-    transform 250ms ease-in-out;
-}
-
-.opacity-crossfade
-{
-
-  opacity: 0.0;
-  pointer-events: none;
-}
-
-.opacity-crossfade.onHover,
-.opacity-crossfade:hover,
-:host-context([ismobile="true"]) .opacity-crossfade.always-show-touchdevice
-{
-  opacity: 1.0 !important;
-  pointer-events: all !important;
-}
-
 .screen-overlay
 {
   background-color: rgba(255, 255, 255, 0.7);
@@ -33,9 +12,23 @@
 .nehuba-viewer-container-parent
 {
   z-index: 1;
+  display: block;
+  width: 100%;
+  height: 100%;
 }
 
 current-layout
 {
   z-index: 2;
 }
+
+nehuba-layout-overlay
+{
+  display: block;
+  width: 100%;
+  height: 100%;
+  position: absolute;
+  top: 0;
+  left: 0;
+  z-index: 2;
+}
diff --git a/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.template.html b/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.template.html
index 104c5add581a8bcb216d1afe8d15b42002657938..1b24745daa5b9d1234601f736888a472fa7a4705 100644
--- a/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.template.html
+++ b/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.template.html
@@ -1,198 +1,19 @@
-<div class="d-block w-100 h-100 nehuba-viewer-container-parent"
-  (touchmove)="$event.preventDefault()"
-  (drag-drop-file)="handleFileDrop($event)"
+<div class="nehuba-viewer-container-parent"
   iav-viewer-touch-interface
-  [iav-viewer-touch-interface-v-panels]="viewPanels"
-  [iav-viewer-touch-interface-vp-to-data]="iavContainer?.viewportToDatas"
-  [iav-viewer-touch-interface-ngviewer]="iavContainer?.nehubaViewerInstance?.nehubaViewer?.ngviewer"
-  [iav-viewer-touch-interface-nehuba-config]="selectedTemplate?.nehubaConfig">
+  (touchmove)="$event.preventDefault()"
+  (drag-drop-file)="handleFileDrop($event)">
 
-  <div class="d-block"
+  <div class="sxplr-d-block"
     iav-nehuba-viewer-container
     #iavContainer="iavNehubaViewerContainer"
-    iav-mouse-hover
-    [iav-key-listener]="[{ type: 'keydown', key: ' ', target: 'document', capture: true }]"
-    (iav-key-event)="handleCycleViewEvent()"
     (iavNehubaViewerContainerViewerLoading)="handleViewerLoadedEvent($event)">
 
   </div>
 </div>
 
-<current-layout *ngIf="viewerLoaded" class="position-absolute w-100 h-100 d-block pe-none top-0 left-0">
-  <div class="w-100 h-100 position-relative" cell-i
-    iav-window-resize
-    [iav-window-resize-time]="64"
-    (iav-window-resize-event)="setQuickTourPos()"
-    quick-tour
-    [quick-tour-description]="quickTourSliceViewSlide.description"
-    [quick-tour-order]="quickTourSliceViewSlide.order"
-    [quick-tour-overwrite-arrow]="sliceViewArrow"
-    [quick-tour-overwrite-position]="quickTourOverwritingPos">
-    <ng-content *ngTemplateOutlet="ngPanelOverlayTmpl; context: { panelIndex: panelOrder$ | async | getProperty : 0 | parseAsNumber }"></ng-content>
-  </div>
-  <div class="w-100 h-100 position-relative" cell-ii>
-    <ng-content *ngTemplateOutlet="ngPanelOverlayTmpl; context: { panelIndex: panelOrder$ | async | getProperty : 1 | parseAsNumber }"></ng-content>
-  </div>
-  <div class="w-100 h-100 position-relative" cell-iii>
-    <ng-content *ngTemplateOutlet="ngPanelOverlayTmpl; context: { panelIndex: panelOrder$ | async | getProperty : 2 | parseAsNumber }"></ng-content>
-  </div>
-  <div class="w-100 h-100 position-relative" cell-iv
-    quick-tour
-    [quick-tour-description]="quickTour3dViewSlide.description"
-    [quick-tour-order]="quickTour3dViewSlide.order">
-    <ng-content *ngTemplateOutlet="ngPanelOverlayTmpl; context: { panelIndex: panelOrder$ | async | getProperty : 3 | parseAsNumber }"></ng-content>
-  </div>
-</current-layout>
-
-<!-- slice view overlay tmpl -->
-<ng-template #ngPanelOverlayTmpl let-panelIndex="panelIndex">
-
-  <!-- perspective view tmpl -->
-  <ng-template #overlayPerspectiveTmpl>
-    <layout-floating-container landmarkContainer>
-
-      <div class="d-flex flex-column justify-content-center align-items-center w-100 h-100 position-absolute opacity-crossfade screen-overlay pe-none"
-        [ngClass]="{onHover: !!(showPerpsectiveScreen$ | async)}"
-        [attr.id]="IDS.MESH_LOADING_STATUS"
-        role="status">
-
-        <spinner-cmp *ngIf="showPerpsectiveScreen$ | async">
-        </spinner-cmp>
-
-        <mat-list>
-          <mat-list-item>
-            {{ showPerpsectiveScreen$ | async }}
-          </mat-list-item>
-        </mat-list>
-      </div>
-
-      <!-- mesh loading is still weird -->
-      <!-- if the precomputed server does not have the necessary fragment file, then the numberws will not collate -->
-      <div *ngIf="false && (perspectiveViewLoading$ | async)" class="loadingIndicator">
-        <spinner-cmp></spinner-cmp>
-
-        <div *ngIf="false" perspectiveLoadingText>
-          {{ perspectiveViewLoading$ | async }}
-        </div>
-      </div>
-    </layout-floating-container>
-  </ng-template>
-
-  <iav-layout-fourcorners class="w-100 h-100 d-block">
-    <layout-floating-container *ngIf="panelIndex < 3; else overlayPerspectiveTmpl"
-      class="overflow-hidden"
-      iavLayoutFourCornersContent>
-      <!-- TODO add landmarks here -->
-
-
-      <!-- customLandmarks -->
-      <!-- only show landmarks in slice views -->
-      <landmark-2d-flat-cmp *ngFor="let lm of (customLandmarks$ | async | filterArray : filterCustomLandmark)"
-        (mouseenter)="handleMouseEnterCustomLandmark(lm)"
-        (mouseleave)="handleMouseLeaveCustomLandmark(lm)"
-        [color]="lm.color || [255, 255, 255]"
-        [positionX]="getPositionX(panelIndex, lm)"
-        [positionY]="getPositionY(panelIndex, lm)"
-        [positionZ]="getPositionZ(panelIndex, lm)">
-
-      </landmark-2d-flat-cmp>
-    </layout-floating-container>
-
-    <!-- panel controller -->
-    <div iavLayoutFourCornersBottomRight class="position-relative">
-
-      <ng-container *ngTemplateOutlet="panelCtrlTmpl; context: {
-        panelIndex: panelIndex,
-        visible: (hoveredPanelIndices$ | async) === panelIndex
-      }">
-      </ng-container>
-
-      <div *ngIf="(sliceViewLoadingMain$ | async)[panelIndex]"
-        class="position-absolute bottom-0 right-0">
-        <spinner-cmp></spinner-cmp>
-      </div>
-    </div>
-  </iav-layout-fourcorners>
-
-</ng-template>
-
-<!-- panel control template -->
-<ng-template
-  #panelCtrlTmpl
-  let-panelIndex="panelIndex"
-  let-visible="visible">
-
-  <div class="ws-no-wrap opacity-crossfade always-show-touchdevice pe-all overlay-btn-container"
-    [ngClass]="{ onHover: visible }"
-    [attr.data-viewer-controller-visible]="visible"
-    [attr.data-viewer-controller-index]="panelIndex">
-
-    <div class="position-absolute w-100 h-100 pe-none"
-      *ngIf="panelIndex === 1"
-      quick-tour
-      [quick-tour-description]="quickTourIconsSlide.description"
-      [quick-tour-order]="quickTourIconsSlide.order">
-    </div>
-
-    <!-- perspective specific control -->
-    <ng-container *ngIf="panelIndex === 3">
-
-      <button mat-icon-button color="primary"
-        [matMenuTriggerFor]="viewerCtrlMenu">
-        <i class="fas fa-cog"></i>
-      </button>
-
-    </ng-container>
-
-    <!-- factor < 1.0 === zoom in -->
-    <button mat-icon-button color="primary"
-      (click)="zoomNgView(panelIndex, 0.9)"
-      [attr.aria-label]="ARIA_LABELS.ZOOM_IN">
-      <i class="fas fa-search-plus"></i>
-    </button>
-
-    <!-- factor > 1.0 === zoom out -->
-    <button mat-icon-button color="primary"
-      (click)="zoomNgView(panelIndex, 1.1)"
-      [attr.aria-label]="ARIA_LABELS.ZOOM_OUT">
-      <i class="fas fa-search-minus"></i>
-    </button>
-
-    <maximise-panel-button
-      (click)="toggleMaximiseMinimise(panelIndex)"
-      [touch-side-class]="panelIndex">
-    </maximise-panel-button>
-  </div>
-
-</ng-template>
-
-<!-- viewer ctrl -->
-<mat-menu #viewerCtrlMenu>
-  <!-- NB must not lazy load. key listener needs to work even when component is not yet rendered -->
-  <!-- stop propagation is needed, or else click will result in dismiss of menu -->
-  <viewer-ctrl-component class="d-block m-2 ml-3 mr-3"
-    (click)="$event.stopPropagation()">
-  </viewer-ctrl-component>
-</mat-menu>
-
-<ng-template #sliceViewArrow>
-  <svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
-    <path id="quarter_circle" d="M22.6151 96.5C22.6151 96.5 18.5 84.1266 18.5 76.5C18.5001 62 18.1151 59.5 22.6151 47C27.115 34.5 39.3315 27.7229 47.5 25C56.5 22 63 22.5 72.5 24C83.1615 25.6834 83.5 26 91 29" />
-    <g id="arrow_top_left">
-      <path id="arrow_stem" d="M37 40C35.5882 38.5882 17.6863 20.6863 12 15" />
-      <path id="arrow_head" d="M6 24C6.38926 21.7912 6.68496 18.3286 6.71205 16.0803C6.73751 13.9665 6.632 13.6135 6.52632 11.5C6.46368 10.2469 6.52632 11.5 6 8C11 10 9.71916 9.74382 11 9.99999C13.5 10.5 13.743 10.7451 17 11C20 11.2348 21.1276 11 22 11"  stroke-linecap="round" stroke-linejoin="round"/>
-    </g>
-    <g id="arrow_left">
-      <path id="arrow_stem_2" d="M29.4229 78.5771C27.1573 78.5771 18.3177 78.5771 9.19238 78.5771" />
-      <path id="arrow_head_2" d="M13.3137 89.6274C12.0271 87.7903 9.78778 85.1328 8.2171 83.5238C6.74048 82.0112 6.41626 81.8362 4.84703 80.4164C3.91668 79.5747 4.84703 80.4164 2 78.3137C6.94975 76.1924 5.86291 76.9169 6.94974 76.1924C9.07106 74.7782 9.41624 74.7797 11.8995 72.6569C14.1868 70.7016 14.8181 69.7382 15.435 69.1213"  stroke-linecap="round" stroke-linejoin="round"/>
-    </g>
-    <g id="arrow_top">
-      <path id="arrow_stem_3" d="M77.0057 32.0057C77.0057 30.3124 77.0057 16.2488 77.0057 9.42862" />
-      <path id="arrow_head_3" d="M68.4342 11.1429C69.9189 10.1032 72.0665 8.29351 73.3667 7.02421C74.5891 5.83091 74.7305 5.5689 75.8779 4.30076C76.5581 3.54892 75.8779 4.30076 77.5771 2C79.2914 6.00002 78.7059 5.12172 79.2915 6.00002C80.4343 7.71431 80.4331 7.99326 82.1486 10C83.7287 11.8485 84.5072 12.3587 85.0058 12.8572"  stroke-linecap="round" stroke-linejoin="round"/>
-    </g>
-  </svg>
-</ng-template>
+<nehuba-layout-overlay></nehuba-layout-overlay>
 
+<!-- user dropped NIFTI overlay -->
 <ng-template #layerCtrlTmpl let-data>
   <div class="grid grid-col-3">
 
@@ -218,7 +39,7 @@
     </button>
 
     <div *ngIf="data.moreInfoFlag"
-      class="iv-custom-comp darker-bg overflow-hidden grid-wide-3">
+      class="sxplr-custom-cmp darker-bg overflow-hidden grid-wide-3">
       <ng-layer-tune
         advanced-control="true"
         [ngLayerName]="data.layerName"
diff --git a/src/viewerModule/nehuba/nehubaViewerInterface/nehubaViewerInterface.directive.spec.ts b/src/viewerModule/nehuba/nehubaViewerInterface/nehubaViewerInterface.directive.spec.ts
index 84a54b7e24da734f57683c6981203d4516dea27a..9b7a7b09d88e0ad801477b90e13f1c4a5f6e60bb 100644
--- a/src/viewerModule/nehuba/nehubaViewerInterface/nehubaViewerInterface.directive.spec.ts
+++ b/src/viewerModule/nehuba/nehubaViewerInterface/nehubaViewerInterface.directive.spec.ts
@@ -1,27 +1,26 @@
 import { Component } from "@angular/core"
-import { TestBed, async, ComponentFixture, fakeAsync, tick } from "@angular/core/testing"
+import { TestBed, ComponentFixture, fakeAsync, tick } from "@angular/core/testing"
 import { By } from "@angular/platform-browser"
-import { BrowserDynamicTestingModule } from "@angular/platform-browser-dynamic/testing"
 import { MockStore, provideMockStore } from "@ngrx/store/testing"
-import { ngViewerSelectorOctantRemoval } from "src/services/state/ngViewerState/selectors"
 import { NehubaViewerUnit } from "../nehubaViewer/nehubaViewer.component"
 import { NehubaViewerContainerDirective } from "./nehubaViewerInterface.directive"
-import { viewerStateSelectorNavigation, viewerStateStandAloneVolumes } from "src/services/state/viewerState/selectors";
-import { Subject } from "rxjs"
-import { ngViewerActionNehubaReady } from "src/services/state/ngViewerState/actions"
-import { viewerStateMouseOverCustomLandmarkInPerspectiveView } from "src/services/state/viewerState/actions"
-import { selectViewerConfigAnimationFlag } from "src/services/state/viewerConfig/selectors"
+import { NEVER, of, pipe, Subject } from "rxjs"
+import { userPreference, atlasSelection, atlasAppearance } from "src/state"
+import { NehubaNavigationService } from "../navigation.service"
+import { LayerCtrlEffects } from "../layerCtrl.service/layerCtrl.effects"
+import { mapTo } from "rxjs/operators"
 
 describe('> nehubaViewerInterface.directive.ts', () => {
+  let distinctATPSpy: jasmine.Spy
   describe('> NehubaViewerContainerDirective', () => {
-
     @Component({
       template: ''
     })
     class DummyCmp{}
 
-    beforeEach(async(() => {
-      TestBed.configureTestingModule({
+    beforeEach(async () => {
+      distinctATPSpy = spyOn(atlasSelection.fromRootStore, 'distinctATP')
+      await TestBed.configureTestingModule({
         imports: [
           
         ],
@@ -31,7 +30,20 @@ describe('> nehubaViewerInterface.directive.ts', () => {
           NehubaViewerUnit,
         ],
         providers: [
-          provideMockStore({ initialState: {} })
+          provideMockStore(),
+          {
+            provide: NehubaNavigationService,
+            useValue: {
+              viewerNav$: NEVER,
+              storeNav: null
+            }
+          },
+          {
+            provide: LayerCtrlEffects,
+            useValue: {
+              onATPDebounceNgLayers$: of({ parcNgLayers: {} })
+            }
+          }
         ]
       }).overrideComponent(DummyCmp, {
         set: {
@@ -42,14 +54,26 @@ describe('> nehubaViewerInterface.directive.ts', () => {
         }
       }).compileComponents()
 
-    }))
+      distinctATPSpy.and.returnValue(
+        pipe(
+          mapTo({
+            atlas: null,
+            parcellation: null,
+            template: null
+          })
+        )
+      )
 
-    beforeEach(() => {
       const mockStore = TestBed.inject(MockStore)
-      mockStore.overrideSelector(ngViewerSelectorOctantRemoval, true)
-      mockStore.overrideSelector(viewerStateStandAloneVolumes, [])
-      mockStore.overrideSelector(viewerStateSelectorNavigation, null)
-      mockStore.overrideSelector(selectViewerConfigAnimationFlag, false)
+      // mockStore.overrideSelector(atlasSelection.selectors.selectedAtlas, null)
+      // mockStore.overrideSelector(atlasSelection.selectors.selectedTemplate, null)
+      // mockStore.overrideSelector(atlasSelection.selectors.selectedParcellation, null)
+
+      mockStore.overrideSelector(atlasAppearance.selectors.customLayers, [])
+      mockStore.overrideSelector(atlasAppearance.selectors.octantRemoval, true)
+      mockStore.overrideSelector(atlasSelection.selectors.standaloneVolumes, [])
+      mockStore.overrideSelector(atlasSelection.selectors.navigation, null)
+      mockStore.overrideSelector(userPreference.selectors.useAnimation, false)
     })
 
     it('> can be inited', () => {
@@ -89,11 +113,17 @@ describe('> nehubaViewerInterface.directive.ts', () => {
         destroy: jasmine.createSpy('destroy')
       }
 
-      beforeEach(() => {
+      const gpuLimit = 5e8
+      beforeEach(fakeAsync(() => {
+        const mockStore = TestBed.inject(MockStore)
+        mockStore.overrideSelector(userPreference.selectors.gpuLimit, gpuLimit)
+
         fixture = TestBed.createComponent(DummyCmp)
         const directive = fixture.debugElement.query(
           By.directive(NehubaViewerContainerDirective)
         )
+
+        tick(300)
         
         directiveInstance = directive.injector.get(NehubaViewerContainerDirective)
         
@@ -102,29 +132,27 @@ describe('> nehubaViewerInterface.directive.ts', () => {
         // casting return value to any is not perfect, but since only 2 methods and 1 property is used, it's a quick way 
         // rather than allow component to be created
         elCreateComponentSpy = spyOn(directiveInstance['el'], 'createComponent').and.returnValue(spyComRef as any)
-      })
+      }))
 
       describe('> on createNehubaInstance called', () => {
-        const template = {}
-        const lifecycle = {}
-        it('> method el.clear gets called before el.createComponent', () => {
-          directiveInstance.createNehubaInstance(template, lifecycle)
+        const nehubaConfig = {
+          dataset: {
+            initialNgState: {
+              
+            }
+          }
+        }
+        it('> method el.clear gets called before el.createComponent', async () => {
+          await directiveInstance.createNehubaInstance(nehubaConfig)
           expect(elClearSpy).toHaveBeenCalledBefore(elCreateComponentSpy)
         })
 
-        it('> if viewerConfig has gpuLimit, gpuMemoryLimit will be in initialNgSTate', () => {
-          template['nehubaConfig'] = {
-            dataset: {
-              initialNgState: {}
-            }
-          }
-          directiveInstance['viewerConfig'] = {
-            gpuLimit: 5e8
-          }
-          directiveInstance.createNehubaInstance(template, lifecycle)
+        it('> if viewerConfig has gpuLimit, gpuMemoryLimit will be in initialNgSTate', async () => {
+          
+          await directiveInstance.createNehubaInstance(nehubaConfig)
           expect(
             directiveInstance.nehubaViewerInstance?.config?.dataset?.initialNgState?.gpuMemoryLimit
-          ).toEqual(5e8)
+          ).toEqual(gpuLimit)
           expect(
             directiveInstance.nehubaViewerInstance?.config?.dataset?.initialNgState?.gpuLimit
           ).toBeFalsy()
@@ -133,16 +161,7 @@ describe('> nehubaViewerInterface.directive.ts', () => {
     
       describe('> on clear called', () => {
         it('> dispatches nehubaReady: false action', () => {
-          const mockStore = TestBed.inject(MockStore)
-          const mockStoreDispatchSpy = spyOn(mockStore, 'dispatch')
-          directiveInstance.clear()
-          expect(
-            mockStoreDispatchSpy
-          ).toHaveBeenCalledWith(
-            ngViewerActionNehubaReady({
-              nehubaReady: false
-            })
-          )
+
         })
 
         it('> iavNehubaViewerContainerViewerLoading emits false', () => {
@@ -209,82 +228,25 @@ describe('> nehubaViewerInterface.directive.ts', () => {
         })
         it('> single null emits null', fakeAsync(() => {
 
-          directiveInstance.createNehubaInstance(template, lifecycle)
-          spyNehubaViewerInstance.mouseoverUserlandmarkEmitter.next(null)
-
-          tick(200)
-          expect(dispatchSpy).toHaveBeenCalledWith(
-            viewerStateMouseOverCustomLandmarkInPerspectiveView({
-              payload: { label: null }
-            })
-          )
         }))
 
         it('> single value emits value', fakeAsync(() => {
 
-          directiveInstance.createNehubaInstance(template, lifecycle)
-          spyNehubaViewerInstance.mouseoverUserlandmarkEmitter.next("24")
-
-          tick(200)
-          expect(dispatchSpy).toHaveBeenCalledWith(
-            viewerStateMouseOverCustomLandmarkInPerspectiveView({
-              payload: { label: "24" }
-            })
-          )
         }))
 
         describe('> double value in 140ms emits last value', () => {
 
           it('> null - 24 emits 24', fakeAsync(() => {
 
-            directiveInstance.createNehubaInstance(template, lifecycle)
-            spyNehubaViewerInstance.mouseoverUserlandmarkEmitter.next(null)
-            spyNehubaViewerInstance.mouseoverUserlandmarkEmitter.next("24")
-  
-            tick(200)
-            expect(dispatchSpy).toHaveBeenCalledWith(
-              viewerStateMouseOverCustomLandmarkInPerspectiveView({
-                payload: { label: "24" }
-              })
-            )
           }))
           it('> 24 - null emits null', fakeAsync(() => {
 
-            directiveInstance.createNehubaInstance(template, lifecycle)
-            spyNehubaViewerInstance.mouseoverUserlandmarkEmitter.next("24")
-            spyNehubaViewerInstance.mouseoverUserlandmarkEmitter.next(null)
-  
-            tick(200)
-            expect(dispatchSpy).toHaveBeenCalledWith(
-              viewerStateMouseOverCustomLandmarkInPerspectiveView({
-                payload: { label: null }
-              })
-            )
+
           }))
         })
       
         it('> single value outside 140 ms emits separately', fakeAsync(() => {
 
-          directiveInstance.createNehubaInstance(template, lifecycle)
-          spyNehubaViewerInstance.mouseoverUserlandmarkEmitter.next(null)
-          tick(200)
-          spyNehubaViewerInstance.mouseoverUserlandmarkEmitter.next("24")
-
-          tick(200)
-          expect(
-            dispatchSpy.calls.allArgs()
-          ).toEqual([
-            [
-              viewerStateMouseOverCustomLandmarkInPerspectiveView({
-                payload: { label: null }
-              })
-            ],
-            [
-              viewerStateMouseOverCustomLandmarkInPerspectiveView({
-                payload: { label: "24" }
-              })
-            ]
-          ])
         }))
       })
     })
diff --git a/src/viewerModule/nehuba/nehubaViewerInterface/nehubaViewerInterface.directive.ts b/src/viewerModule/nehuba/nehubaViewerInterface/nehubaViewerInterface.directive.ts
index 1b6fb599e73e9808098fe45872860672a26b5742..0b7f185aa8d4530a561d779f45274a9a3b13cff7 100644
--- a/src/viewerModule/nehuba/nehubaViewerInterface/nehubaViewerInterface.directive.ts
+++ b/src/viewerModule/nehuba/nehubaViewerInterface/nehubaViewerInterface.directive.ts
@@ -1,89 +1,20 @@
-import { Directive, ViewContainerRef, ComponentFactoryResolver, ComponentFactory, ComponentRef, OnInit, OnDestroy, Output, EventEmitter, Optional } from "@angular/core";
-import { NehubaViewerUnit, INehubaLifecycleHook } from "../nehubaViewer/nehubaViewer.component";
+import { Directive, ViewContainerRef, ComponentRef, OnDestroy, Output, EventEmitter, Optional, ChangeDetectorRef, ComponentFactoryResolver, ComponentFactory } from "@angular/core";
+import { NehubaViewerUnit } from "../nehubaViewer/nehubaViewer.component";
 import { Store, select } from "@ngrx/store";
-import { Subscription, Observable, fromEvent, asyncScheduler, combineLatest } from "rxjs";
-import { distinctUntilChanged, filter, debounceTime, scan, map, throttleTime, switchMapTo } from "rxjs/operators";
-import { takeOnePipe } from "../util";
-import { ngViewerActionNehubaReady } from "src/services/state/ngViewerState/actions";
-import { viewerStateMouseOverCustomLandmarkInPerspectiveView, viewerStateNehubaLayerchanged } from "src/services/state/viewerState/actions";
-import { viewerStateStandAloneVolumes } from "src/services/state/viewerState/selectors";
-import { ngViewerSelectorOctantRemoval } from "src/services/state/ngViewerState/selectors";
+import { Subscription, Observable, asyncScheduler, combineLatest } from "rxjs";
+import { distinctUntilChanged, filter, debounceTime, scan, map, throttleTime, switchMap, take } from "rxjs/operators";
+import { serializeSegment } from "../util";
 import { LoggingService } from "src/logging";
-import { uiActionMouseoverLandmark, uiActionMouseoverSegments } from "src/services/state/uiState/actions";
-import { IViewerConfigState } from "src/services/state/viewerConfig.store.helper";
 import { arrayOfPrimitiveEqual } from 'src/util/fn'
 import { INavObj, NehubaNavigationService } from "../navigation.service";
+import { NehubaConfig, defaultNehubaConfig, getNehubaConfig } from "../config.service";
+import { atlasAppearance, atlasSelection, userPreference } from "src/state";
+import { SapiAtlasModel, SapiParcellationModel, SapiSpaceModel } from "src/atlasComponents/sapi";
+import { NgLayerCustomLayer } from "src/state/atlasAppearance";
+import { arrayEqual } from "src/util/array";
+import { cvtNavigationObjToNehubaConfig } from "../config.service/util";
+import { LayerCtrlEffects } from "../layerCtrl.service/layerCtrl.effects";
 
-const defaultNehubaConfig = {
-  "configName": "",
-  "globals": {
-    "hideNullImageValues": true,
-    "useNehubaLayout": {
-      "keepDefaultLayouts": false
-    },
-    "useNehubaMeshLayer": true,
-    "rightClickWithCtrlGlobal": false,
-    "zoomWithoutCtrlGlobal": false,
-    "useCustomSegmentColors": true
-  },
-  "zoomWithoutCtrl": true,
-  "hideNeuroglancerUI": true,
-  "rightClickWithCtrl": true,
-  "rotateAtViewCentre": true,
-  "enableMeshLoadingControl": true,
-  "zoomAtViewCentre": true,
-  "restrictUserNavigation": true,
-  "disableSegmentSelection": false,
-  "dataset": {
-    "imageBackground": [
-      1,
-      1,
-      1,
-      1
-    ],
-    "initialNgState": {
-      "showDefaultAnnotations": false,
-      "layers": {},
-    }
-  },
-  "layout": {
-    "views": "hbp-neuro",
-    "planarSlicesBackground": [
-      1,
-      1,
-      1,
-      1
-    ],
-    "useNehubaPerspective": {
-      "enableShiftDrag": false,
-      "doNotRestrictUserNavigation": false,
-      "perspectiveSlicesBackground": [
-        1,
-        1,
-        1,
-        1
-      ],
-      "perspectiveBackground": [
-        1,
-        1,
-        1,
-        1
-      ],
-      "mesh": {
-        "backFaceColor": [
-          1,
-          1,
-          1,
-          1
-        ],
-        "removeBasedOnNavigation": true,
-        "flipRemovedOctant": true
-      },
-      "hideImages": false,
-      "waitForMesh": false,
-    }
-  }
-}
 
 const determineProtocol = (url: string) => {
   const re = /^([a-z0-9_-]{0,}):\/\//.exec(url)
@@ -206,9 +137,7 @@ const accumulatorFn: (
   exportAs: 'iavNehubaViewerContainer',
   providers: [ NehubaNavigationService ]
 })
-export class NehubaViewerContainerDirective implements OnInit, OnDestroy{
-
-  public viewportToDatas: [any, any, any] = [null, null, null]
+export class NehubaViewerContainerDirective implements OnDestroy{
 
   @Output('iav-nehuba-viewer-container-mouseover')
   public mouseOverSegments = new EventEmitter<TMouseoverEvent[]>()
@@ -222,51 +151,77 @@ export class NehubaViewerContainerDirective implements OnInit, OnDestroy{
   @Output()
   public iavNehubaViewerContainerViewerLoading: EventEmitter<boolean> = new EventEmitter()
   
-  private nehubaViewerFactory: ComponentFactory<NehubaViewerUnit>
+  private componentFactory: ComponentFactory<NehubaViewerUnit>
   private cr: ComponentRef<NehubaViewerUnit>
+  private navigation: atlasSelection.AtlasSelectionState['navigation']
   constructor(
     private el: ViewContainerRef,
-    private cfr: ComponentFactoryResolver,
     private store$: Store<any>,
     private navService: NehubaNavigationService,
+    private effect: LayerCtrlEffects,
+    private cdr: ChangeDetectorRef,
+    cfr: ComponentFactoryResolver,
     @Optional() private log: LoggingService,
   ){
-    this.nehubaViewerFactory = this.cfr.resolveComponentFactory(NehubaViewerUnit)
-
-    this.viewerPerformanceConfig$ = this.store$.pipe(
-      select('viewerConfigState'),
-      /**
-       * TODO: this is only a bandaid fix. Technically, we should also implement
-       * logic to take the previously set config to apply oninit
-       */
-      distinctUntilChanged(),
-    )
-
-    this.nehubaViewerPerspectiveOctantRemoval$ = this.store$.pipe(
-      select(ngViewerSelectorOctantRemoval),
-    )
-  }
+    this.componentFactory = cfr.resolveComponentFactory(NehubaViewerUnit)
+    this.cdr.detach()
 
-  private nehubaViewerPerspectiveOctantRemoval$: Observable<boolean>
-
-  private viewerPerformanceConfig$: Observable<IViewerConfigState>
-  private viewerConfig: Partial<IViewerConfigState> = {}
-
-  private nehubaViewerSubscriptions: Subscription[] = []
-  private subscriptions: Subscription[] = []
-
-  ngOnInit(){
     this.subscriptions.push(
       this.nehubaViewerPerspectiveOctantRemoval$.pipe(
         distinctUntilChanged()
       ).subscribe(flag =>{
         this.toggleOctantRemoval(flag)
-      })
-    )
+      }),
+      this.store$.pipe(
+        atlasSelection.fromRootStore.distinctATP(),
+        debounceTime(16),
+        switchMap((ATP: { atlas: SapiAtlasModel, parcellation: SapiParcellationModel, template: SapiSpaceModel }) => this.store$.pipe(
+          select(atlasAppearance.selectors.customLayers),
+          debounceTime(16),
+          map(cl => cl.filter(l => l.clType === "baselayer/nglayer") as NgLayerCustomLayer[]),
+          distinctUntilChanged(arrayEqual((oi, ni) => oi.id === ni.id)),
+          filter(layers => layers.length > 0),
+          map(ngBaseLayers => {
+            return {
+              ATP,
+              ngBaseLayers
+            }
+          })
+        ))
+      ).subscribe(async ({ ATP, ngBaseLayers }) => {
+        const config = getNehubaConfig(ATP.template)
+        for (const baseLayer of ngBaseLayers) {
+          config.dataset.initialNgState.layers[baseLayer.id] = baseLayer
+        }
+    
+        const overwritingInitState = this.navigation
+          ? cvtNavigationObjToNehubaConfig(this.navigation, config.dataset.initialNgState)
+          : {}
+    
+        config.dataset.initialNgState = {
+          ...config.dataset.initialNgState,
+          ...overwritingInitState,
+        }
+
+        const {
+          parcNgLayers,
+        } = await this.effect.onATPDebounceNgLayers$.pipe(
+          take(1)
+        ).toPromise()
+
+        await this.createNehubaInstance(config)
+
+        const ngIdSegmentsMap: Record<string, number[]> = {} 
+  
+        for (const key in parcNgLayers) {
+          ngIdSegmentsMap[key] = parcNgLayers[key].labelIndicies
+        }
+  
+        this.nehubaViewerInstance.ngIdSegmentsMap = ngIdSegmentsMap
+      }),
 
-    this.subscriptions.push(
       this.store$.pipe(
-        select(viewerStateStandAloneVolumes),
+        select(atlasSelection.selectors.standaloneVolumes),
         filter(v => v && Array.isArray(v) && v.length > 0),
         distinctUntilChanged(arrayOfPrimitiveEqual)
       ).subscribe(async volumes => {
@@ -282,33 +237,52 @@ export class NehubaViewerContainerDirective implements OnInit, OnDestroy{
             // TODO catch error
           }
         }
-        function onInit() {
-          this.overrideShowLayers = forceShowLayerNames
-        }
-        this.createNehubaInstance({ nehubaConfig: copiedNehubaConfig }, { onInit })
+        await this.createNehubaInstance(copiedNehubaConfig)
       }),
 
-      this.viewerPerformanceConfig$.pipe(
-        debounceTime(200)
-      ).subscribe(config => {
-        this.viewerConfig = config
+      this.gpuLimit$.pipe(
+        debounceTime(160),
+      ).subscribe(limit => {
+        this.gpuLimit = limit
         if (this.nehubaViewerInstance && this.nehubaViewerInstance.nehubaViewer) {
-          this.nehubaViewerInstance.applyPerformanceConfig(config)
+          this.nehubaViewerInstance.applyGpuLimit(limit)
         }
       }),
       this.navService.viewerNav$.subscribe(v => {
         this.navigationEmitter.emit(v)
-      })
+      }),
+      this.store$.pipe(
+        select(atlasSelection.selectors.navigation)
+      ).subscribe(nav => this.navigation = nav)
     )
   }
 
-  ngOnDestroy(){
+  private nehubaViewerPerspectiveOctantRemoval$ = this.store$.pipe(
+    select(atlasAppearance.selectors.octantRemoval),
+  )
+
+  private gpuLimit$: Observable<number> = this.store$.pipe(
+    select(userPreference.selectors.gpuLimit)
+  )
+  private gpuLimit: number = null
+
+  private nehubaViewerSubscriptions: Subscription[] = []
+  private subscriptions: Subscription[] = []
+
+  redraw(): void{
+    this.nehubaViewerInstance.redraw()
+  }
+
+  ngOnDestroy(): void{
     while(this.subscriptions.length > 0){
       this.subscriptions.pop().unsubscribe()
     }
+    while (this.nehubaViewerSubscriptions.length > 0) {
+      this.nehubaViewerSubscriptions.pop().unsubscribe()
+    }
   }
 
-  public toggleOctantRemoval(flag: boolean){
+  public toggleOctantRemoval(flag: boolean): void{
     if (!this.nehubaViewerInstance) {
       this.log.error(`this.nehubaViewerInstance is not yet available`)
       return
@@ -316,10 +290,13 @@ export class NehubaViewerContainerDirective implements OnInit, OnDestroy{
     this.nehubaViewerInstance.toggleOctantRemoval(flag)
   }
 
-  createNehubaInstance(template: any, lifeCycle: INehubaLifecycleHook = {}){
+  async createNehubaInstance(nehubaConfig: NehubaConfig): Promise<void>{
     this.clear()
+
+    await new Promise(rs => setTimeout(rs, 0))
+
     this.iavNehubaViewerContainerViewerLoading.emit(true)
-    this.cr = this.el.createComponent(this.nehubaViewerFactory)
+    this.cr = this.el.createComponent(this.componentFactory)
 
     if (this.navService.storeNav) {
       this.nehubaViewerInstance.initNav = {
@@ -328,46 +305,18 @@ export class NehubaViewerContainerDirective implements OnInit, OnDestroy{
       }
     }
 
-    const { nehubaConfig, name } = template
+    if (this.gpuLimit) {
+      const initialNgState = nehubaConfig && nehubaConfig.dataset && nehubaConfig.dataset.initialNgState
+      // the correct key is gpuMemoryLimit
+      initialNgState.gpuMemoryLimit = this.gpuLimit
+    }
 
     /**
      * apply viewer config such as gpu limit
      */
-    const { gpuLimit = null } = this.viewerConfig
 
     this.nehubaViewerInstance.config = nehubaConfig
-    this.nehubaViewerInstance.lifecycle = lifeCycle
-
-    if (gpuLimit) {
-      const initialNgState = nehubaConfig && nehubaConfig.dataset && nehubaConfig.dataset.initialNgState
-      // the correct key is gpuMemoryLimit
-      initialNgState.gpuMemoryLimit = gpuLimit
-    }
-
-    /* TODO replace with id from KG */
-    this.nehubaViewerInstance.templateId = name
-
     this.nehubaViewerSubscriptions.push(
-      this.nehubaViewerInstance.errorEmitter.subscribe(e => {
-        console.log(e)
-      }),
-
-      this.nehubaViewerInstance.layersChanged.subscribe(() => {
-        this.store$.dispatch(
-          viewerStateNehubaLayerchanged()
-        )
-      }),
-
-      this.nehubaViewerInstance.nehubaReady.subscribe(() => {
-        /**
-         * TODO when user selects new template, window.viewer
-         */
-        this.store$.dispatch(
-          ngViewerActionNehubaReady({
-            nehubaReady: true,
-          })
-        )
-      }),
 
       this.nehubaViewerInstance.mouseoverSegmentEmitter.pipe(
         scan(accumulatorFn, new Map()),
@@ -377,28 +326,15 @@ export class NehubaViewerContainerDirective implements OnInit, OnDestroy{
       this.nehubaViewerInstance.mouseoverLandmarkEmitter.pipe(
         distinctUntilChanged()
       ).subscribe(label => {
-        this.store$.dispatch(
-          uiActionMouseoverLandmark({
-            landmark: label
-          })
-        )
+        console.warn(`mouseover landmark`, label)
       }),
 
       this.nehubaViewerInstance.mouseoverUserlandmarkEmitter.pipe(
         throttleTime(160, asyncScheduler, {trailing: true}),
       ).subscribe(label => {
-        this.store$.dispatch(
-          viewerStateMouseOverCustomLandmarkInPerspectiveView({
-            payload: { label }
-          })
-        )
-      }),
-
-      this.nehubaViewerInstance.nehubaReady.pipe(
-        switchMapTo(fromEvent(this.nehubaViewerInstance.elementRef.nativeElement, 'viewportToData')),
-        takeOnePipe()
-      ).subscribe((events: CustomEvent[]) => {
-        [0, 1, 2].forEach(idx => this.viewportToDatas[idx] = events[idx].detail.viewportToData)
+        const idx = Number(label.replace('label=', ''))
+        // TODO 
+        // this is exclusive for vtk layer
       }),
 
       combineLatest([
@@ -413,46 +349,35 @@ export class NehubaViewerContainerDirective implements OnInit, OnDestroy{
     )
   }
 
-  clear(){
+  clear(): void{
     while(this.nehubaViewerSubscriptions.length > 0) {
       this.nehubaViewerSubscriptions.pop().unsubscribe()
     }
 
-    this.store$.dispatch(
-      ngViewerActionNehubaReady({
-        nehubaReady: false,
-      })
-    )
-
     this.iavNehubaViewerContainerViewerLoading.emit(false)
     if(this.cr) this.cr.destroy()
     this.el.clear()
     this.cr = null
   }
 
-  get nehubaViewerInstance(){
+  get nehubaViewerInstance(): NehubaViewerUnit{
     return this.cr && this.cr.instance
   }
 
-  isReady() {
+  isReady(): boolean {
     return !!(this.cr?.instance?.nehubaViewer?.ngviewer)
   }
 
-  handleMouseoverSegments(arrOfArr: [string, any][]) {
+  handleMouseoverSegments(arrOfArr: [string, any][]): void {
     const payload = arrOfArr.map( ([ngId, {segment, segmentId}]) => {
       return {
         layer: {
           name: ngId,
         },
-        segment: segment || `${ngId}#${segmentId}`,
+        segment: segment || serializeSegment(ngId, segmentId),
         segmentId
       }
     })
     this.mouseOverSegments.emit(payload)
-    this.store$.dispatch(
-      uiActionMouseoverSegments({
-        segments: payload
-      })
-    )
   }
 }
diff --git a/src/viewerModule/nehuba/nehubaViewerInterface/nehubaViewerTouch.directive.ts b/src/viewerModule/nehuba/nehubaViewerInterface/nehubaViewerTouch.directive.ts
index 28eeda243621049da1342169ef19d70e721eb459..0fbd2562a1d4f50bc39e6157aefe322747afe475 100644
--- a/src/viewerModule/nehuba/nehubaViewerInterface/nehubaViewerTouch.directive.ts
+++ b/src/viewerModule/nehuba/nehubaViewerInterface/nehubaViewerTouch.directive.ts
@@ -1,8 +1,9 @@
-import { Directive, ElementRef, Input, OnDestroy } from "@angular/core";
+import { Directive, ElementRef, Inject, Input, OnDestroy } from "@angular/core";
 import { Observable, fromEvent, merge, Subscription } from "rxjs";
 import { map, filter, shareReplay, switchMap, pairwise, takeUntil, switchMapTo } from "rxjs/operators";
 import { getExportNehuba } from 'src/util/fn'
-import { computeDistance } from "../nehubaViewer/nehubaViewer.component";
+import { computeDistance, NehubaViewerUnit } from "../nehubaViewer/nehubaViewer.component";
+import { NEHUBA_INSTANCE_INJTKN, takeOnePipe } from "../util";
 
 @Directive({
   selector: '[iav-viewer-touch-interface]',
@@ -11,17 +12,12 @@ import { computeDistance } from "../nehubaViewer/nehubaViewer.component";
 
 export class NehubaViewerTouchDirective implements OnDestroy{
 
-  @Input('iav-viewer-touch-interface-v-panels')
-  viewerPanels: [HTMLElement, HTMLElement, HTMLElement, HTMLElement]
-
   @Input('iav-viewer-touch-interface-vp-to-data')
-  viewportToData: [any, any, any, any]
-
-  @Input('iav-viewer-touch-interface-ngviewer')
-  ngViewer: any
+  viewportToData: any[] = []
 
-  @Input('iav-viewer-touch-interface-nehuba-config')
-  nehubaConfig: any
+  get ngViewer(){
+    return this.nehubaUnit?.nehubaViewer?.ngviewer
+  }
 
   private touchMove$: Observable<any>
   private singleTouchStart$: Observable<TouchEvent>
@@ -30,7 +26,22 @@ export class NehubaViewerTouchDirective implements OnDestroy{
 
   public translate$: Observable<any>
 
-  private findPanelIndex = (panel: HTMLElement) => this.viewerPanels.indexOf(panel)
+  private nehubaUnit: NehubaViewerUnit
+  private htmlElementIndexMap = new WeakMap<HTMLElement, number>()
+  private findPanelIndex(panel: HTMLElement){
+    if (!this.nehubaUnit) return null
+    if (!this.htmlElementIndexMap.has(panel)) {
+      Array.from(this.nehubaUnit?.nehubaViewer?.ngviewer?.display?.panels || []).forEach((el, idx) => {
+        if (el['element']) {
+          this.htmlElementIndexMap.set(
+            el['element'] as HTMLElement,
+            idx
+          )
+        }
+      })
+    }
+    return this.htmlElementIndexMap.get(panel)
+  }
 
   private _exportNehuba: any
   private get exportNehuba(){
@@ -41,11 +52,21 @@ export class NehubaViewerTouchDirective implements OnDestroy{
   }
 
   private s: Subscription[] = []
+  private nehubaSub: Subscription[] = []
 
   constructor(
     private el: ElementRef,
+    @Inject(NEHUBA_INSTANCE_INJTKN) nehuba$: Observable<NehubaViewerUnit>
   ){
-
+    if (nehuba$) {
+
+      this.s.push(
+        nehuba$.subscribe(unit => {
+          this.nehubaUnit = unit
+          this.onNewNehubaUnit(unit)
+        })
+      )
+    }
     /**
      * Touchend also needs to be listened to, as user could start
      * with multitouch, and end up as single touch
@@ -199,7 +220,7 @@ export class NehubaViewerTouchDirective implements OnDestroy{
           [ev1.touches[1].screenX, ev1.touches[1].screenY],
         )
         const factor = d1 / d2
-        const { minZoom = null, maxZoom = null } = this.nehubaConfig?.layout?.useNehubaPerspective?.restrictZoomLevel || {}
+        const { minZoom = null, maxZoom = null } = {}
         const { zoomFactor } = this.ngViewer.perspectiveNavigationState
         if (!!minZoom && zoomFactor.value * factor < minZoom) { return }
         if (!!maxZoom && zoomFactor.value * factor > maxZoom) { return }
@@ -249,9 +270,27 @@ export class NehubaViewerTouchDirective implements OnDestroy{
     )
   }
 
-  ngOnDestroy(){
+  private onNewNehubaUnit(nehubaUnit: NehubaViewerUnit) {
+    while (this.nehubaSub.length > 0) this.nehubaSub.pop().unsubscribe()
+
+    if (!nehubaUnit) return
+
+    this.nehubaSub.push(
+      fromEvent<CustomEvent>(
+        nehubaUnit.elementRef.nativeElement,
+        'viewportToData'
+      ).pipe(
+        takeOnePipe()
+      ).subscribe((events: CustomEvent[]) => {
+        [0, 1, 2].forEach(idx => this.viewportToData[idx] = events[idx].detail.viewportToData)
+      })
+    )
+  }
+
+  ngOnDestroy(): void{
     while(this.s.length > 0){
       this.s.pop().unsubscribe()
     }
+    while (this.nehubaSub.length > 0) this.nehubaSub.pop().unsubscribe()
   }
 }
diff --git a/src/viewerModule/nehuba/ngLayerCtl/ngLayerCtrl.component.ts b/src/viewerModule/nehuba/ngLayerCtl/ngLayerCtrl.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f2e75e77f501517ac2f166f44a1b851042458489
--- /dev/null
+++ b/src/viewerModule/nehuba/ngLayerCtl/ngLayerCtrl.component.ts
@@ -0,0 +1,152 @@
+import { ChangeDetectionStrategy, Component, Inject, Input, OnChanges, OnDestroy } from "@angular/core";
+import { Store } from "@ngrx/store";
+import { isMat4 } from "common/util"
+import { Observable } from "rxjs";
+import { atlasAppearance } from "src/state";
+import { NehubaViewerUnit } from "..";
+import { NEHUBA_INSTANCE_INJTKN } from "../util";
+
+type Vec4 = [number, number, number, number]
+type Mat4 = [Vec4, Vec4, Vec4, Vec4]
+
+const _VOL_DETAIL_MAP: Record<string, { shader: string, opacity: number }> = {
+  "PLI Fiber Orientation Red Channel": {
+    shader: "void main(){ float x = toNormalized(getDataValue()); if (x < 0.1) { emitTransparent(); } else { emitRGB(vec3(1.0 * x, x * 0., 0. * x )); } }",
+    opacity: 1
+  },
+  "PLI Fiber Orientation Green Channel": {
+    shader: "void main(){ float x = toNormalized(getDataValue()); if (x < 0.1) { emitTransparent(); } else { emitRGB(vec3(0. * x, x * 1., 0. * x )); } }",
+    opacity: 0.5
+  },
+  "PLI Fiber Orientation Blue Channel": {
+    shader: "void main(){ float x = toNormalized(getDataValue()); if (x < 0.1) { emitTransparent(); } else { emitRGB(vec3(0. * x, x * 0., 1.0 * x )); } }",
+    opacity: 0.25
+  },
+  "Blockface Image": {
+    shader: "void main(){ float x = toNormalized(getDataValue()); if (x < 0.1) { emitTransparent(); } else { emitRGB(vec3(0.8 * x, x * 1., 0.8 * x )); } }",
+    opacity: 1.0
+  },
+  "PLI Transmittance": {
+    shader: "void main(){ float x = toNormalized(getDataValue()); if (x > 0.9) { emitTransparent(); } else { emitRGB(vec3(x * 1., x * 0.8, x * 0.8 )); } }",
+    opacity: 1.0
+  },
+  "T2w MRI": {
+    shader: "void main(){ float x = toNormalized(getDataValue()); if (x < 0.1) { emitTransparent(); } else { emitRGB(vec3(0.8 * x, 0.8 * x, x * 1. )); } }",
+    opacity: 1
+  },
+  "MRI Labels": {
+    shader: null,
+    opacity: 1
+  }
+}
+
+@Component({
+  selector: 'ng-layer-ctl',
+  templateUrl: './ngLayerCtrl.template.html',
+  styleUrls: [
+    './ngLayerCtrl.style.css'
+  ],
+  changeDetection: ChangeDetectionStrategy.OnPush
+})
+
+export class NgLayerCtrlCmp implements OnChanges, OnDestroy{
+
+  private onDestroyCb: (() => void)[] = []
+  private removeLayer: () => void
+
+  public showOpacityCtrl = false
+  public hideNgTuneCtrl = 'lower_threshold,higher_threshold,brightness,contrast,colormap,hide-threshold-checkbox'
+  public defaultOpacity = 1
+
+  @Input('ng-layer-ctl-name')
+  name: string
+
+  @Input('ng-layer-ctl-src')
+  source: string
+
+  @Input('ng-layer-ctl-shader')
+  shader: string
+
+  opacity: number = 1.0
+  @Input('ng-layer-ctl-opacity')
+  set _opacity(val: number | string) {
+    if (typeof val === 'number') {
+      this.opacity = val
+      return
+    }
+    this.opacity = Number(val)
+  }
+  
+  transform: Mat4
+  @Input('ng-layer-ctl-transform')
+  set _transform(xform: string | Mat4) {
+    const parsedResult = typeof xform === "string"
+      ? JSON.parse(xform)
+      : xform
+    if (!isMat4(xform)) {
+      return
+    }
+    this.transform = parsedResult as Mat4
+  }
+
+  visible: boolean = true
+  private viewer: NehubaViewerUnit
+
+  constructor(
+    private store: Store<any>,
+    @Inject(NEHUBA_INSTANCE_INJTKN) nehubaViewer$: Observable<NehubaViewerUnit>
+  ){
+    const sub = nehubaViewer$.subscribe(v => this.viewer = v)
+    this.onDestroyCb.push(
+      () => sub.unsubscribe()
+    )
+  }
+
+  ngOnDestroy(): void {
+    while(this.onDestroyCb.length > 0) this.onDestroyCb.pop()()
+    if (this.removeLayer) {
+      this.removeLayer()
+      this.removeLayer = null
+    }
+  }
+
+  ngOnChanges(): void {
+    if (this.name in _VOL_DETAIL_MAP) {
+      const { shader, opacity } = _VOL_DETAIL_MAP[this.name]
+      this.shader = shader
+      this.opacity = opacity
+    }
+
+    if (this.name && this.source) {
+      const { name } = this
+      if (this.removeLayer) {
+        this.removeLayer()
+        this.removeLayer = null
+      }
+      this.store.dispatch(
+        atlasAppearance.actions.addCustomLayer({
+          customLayer: {
+            id: name,
+            shader: this.shader,
+            transform: this.transform,
+            clType: 'customlayer/nglayer',
+            source: `precomputed://${this.source}`,
+            opacity: this.opacity,
+          }
+        })
+      )
+      this.removeLayer = () => {
+        this.store.dispatch(
+          atlasAppearance.actions.removeCustomLayer({
+            id: name
+          })
+        )
+      }
+    }
+  }
+
+  toggleVisibility(): void{
+    this.visible = !this.visible
+    this.viewer.nehubaViewer.ngviewer.layerManager.getLayerByName(this.name).setVisible(this.visible)
+  }
+}
diff --git a/src/viewerModule/nehuba/ngLayerCtl/ngLayerCtrl.style.css b/src/viewerModule/nehuba/ngLayerCtl/ngLayerCtrl.style.css
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/viewerModule/nehuba/ngLayerCtl/ngLayerCtrl.template.html b/src/viewerModule/nehuba/ngLayerCtl/ngLayerCtrl.template.html
new file mode 100644
index 0000000000000000000000000000000000000000..e3442c7b4323dac373e0b9110549b8c97a206a3e
--- /dev/null
+++ b/src/viewerModule/nehuba/ngLayerCtl/ngLayerCtrl.template.html
@@ -0,0 +1,22 @@
+<div [ngClass]="{ 'text-muted': !visible }">
+
+  <button mat-icon-button (click)="toggleVisibility()">
+    <i [ngClass]="visible ? 'fa-eye' : 'fa-eye-slash'" class="far"></i>
+  </button>
+  
+  <span>
+    {{ name }}
+  </span>
+
+  <button mat-icon-button (click)="showOpacityCtrl = !showOpacityCtrl">
+    <i class="fas fa-cog"></i>
+  </button>
+
+  <ng-template [ngIf]="showOpacityCtrl">
+    <ng-layer-tune
+      [ngLayerName]="name"
+      [hideCtrl]="hideNgTuneCtrl"
+      [opacity]="defaultOpacity">
+    </ng-layer-tune>
+  </ng-template>
+</div>
diff --git a/src/viewerModule/nehuba/statusCard/statusCard.component.spec.ts b/src/viewerModule/nehuba/statusCard/statusCard.component.spec.ts
index 7be4aa437b59c3f92b75a3eb7607f32978d46de5..ce0afa8fa8632b433b5998eed1107654fdc4dfa2 100644
--- a/src/viewerModule/nehuba/statusCard/statusCard.component.spec.ts
+++ b/src/viewerModule/nehuba/statusCard/statusCard.component.spec.ts
@@ -1,9 +1,9 @@
-import { async, ComponentFixture, TestBed } from "@angular/core/testing"
+import { ComponentFixture, TestBed } from "@angular/core/testing"
 import { CommonModule } from "@angular/common"
 import { AngularMaterialModule } from "src/sharedModules"
 import { StatusCardComponent } from "./statusCard.component"
 import { Directive, Component } from "@angular/core"
-import { Observable, of } from "rxjs"
+import { of } from "rxjs"
 import { ShareModule } from "src/share"
 import { StateModule } from "src/state"
 import { MockStore, provideMockStore } from "@ngrx/store/testing"
@@ -12,11 +12,10 @@ import { MatSlideToggle } from "@angular/material/slide-toggle"
 import { NoopAnimationsModule } from "@angular/platform-browser/animations"
 import { FormsModule, ReactiveFormsModule } from "@angular/forms"
 import { UtilModule } from "src/util"
-import { viewerConfigSelectorUseMobileUi } from "src/services/state/viewerConfig.store.helper"
-import { viewerStateNavigationStateSelector, viewerStateSelectedTemplatePureSelector } from "src/services/state/viewerState/selectors"
-import * as util from '../util'
-import { viewerStateChangeNavigation } from "src/services/state/viewerState/actions"
+import * as configSvc from '../config.service'
 import {QuickTourModule} from "src/ui/quickTour/module";
+import { atlasSelection } from "src/state"
+import { SapiSpaceModel } from "src/atlasComponents/sapi"
 
 @Directive({
   selector: '[iav-auth-auth-state]',
@@ -36,8 +35,8 @@ class MockSigninModal{}
 
 describe('> statusCard.component.ts', () => {
   describe('> StatusCardComponent', () => {
-    beforeEach(async(() => {
-      TestBed.configureTestingModule({
+    beforeEach(async () => {
+      await TestBed.configureTestingModule({
         imports: [
           CommonModule,
           AngularMaterialModule,
@@ -64,12 +63,15 @@ describe('> statusCard.component.ts', () => {
           })
         ]
       }).compileComponents()
-    }))
+    })
 
     beforeEach(() => {
 
       const mockStore = TestBed.inject(MockStore)
-      mockStore.overrideSelector(viewerConfigSelectorUseMobileUi, false)
+
+      mockStore.overrideSelector(atlasSelection.selectors.selectedTemplate, {
+        '@id': 'minds/core/referencespace/v1.0.0/a1655b99-82f1-420f-a3c2-fe80fd4c8588'
+      } as SapiSpaceModel)
 
     })
 
@@ -139,24 +141,29 @@ describe('> statusCard.component.ts', () => {
         perspectiveOrientation: [1,0,0,0]
       }
 
-      const mockNavState = {
-        orientation: [0,0,0,1],
-        position: [10,20,30],
-        perspectiveZoom: 1e6,
-        zoom: 1e6,
-        perspectiveOrientation: [0,0,0,1]
+      const mockNehubaConfig = {
+        dataset: {
+          initialNgState: {
+            navigation: {
+              pose: {
+                orientation: [0,0,0,1],
+                position: [10, 20, 30]
+              },
+              zoomFactor: 1e6
+            }
+          }
+        }
       }
-      const mockTemplate = { foo:'bar', nehubaConfig: { foo2: 'bar2' } }
 
-      let getNavigationStateFromConfigSpy: jasmine.Spy = jasmine.createSpy('getNavigationStateFromConfig').and.returnValue(mockNavState)
+      let getNavigationStateFromConfigSpy: jasmine.Spy = jasmine.createSpy('getNavigationStateFromConfig').and.returnValue(mockNehubaConfig)
 
       beforeEach(() => {
         const mockStore = TestBed.inject(MockStore)
-        mockStore.overrideSelector(viewerStateSelectedTemplatePureSelector, mockTemplate)
-        mockStore.overrideSelector(viewerStateNavigationStateSelector, mockCurrNavigation)
-
-        spyOnProperty(util, 'getNavigationStateFromConfig').and.returnValue(getNavigationStateFromConfigSpy)
+        mockStore.overrideSelector(atlasSelection.selectors.selectedTemplate, null)
+        mockStore.overrideSelector(atlasSelection.selectors.navigation, mockCurrNavigation)
 
+        spyOnProperty(configSvc, 'getNehubaConfig').and.returnValue(getNavigationStateFromConfigSpy)
+        
         fixture = TestBed.createComponent(StatusCardComponent)
         fixture.detectChanges()
         fixture.componentInstance.showFull = true
@@ -180,17 +187,17 @@ describe('> statusCard.component.ts', () => {
             fixture.detectChanges()
 
             const overrideObj = {}
-            if (method === 'rotation') overrideObj['orientation'] = mockNavState['orientation']
-            if (method === 'position') overrideObj['position'] = mockNavState['position']
-            if (method === 'zoom') overrideObj['zoom'] = mockNavState['zoom']
+            if (method === 'rotation') overrideObj['orientation'] = mockNehubaConfig.dataset.initialNgState.navigation.pose.orientation
+            if (method === 'position') overrideObj['position'] = mockNehubaConfig.dataset.initialNgState.navigation.pose.position
+            if (method === 'zoom') overrideObj['zoom'] = mockNehubaConfig.dataset.initialNgState.navigation.zoomFactor
             expect(idspatchSpy).toHaveBeenCalledWith(
-              viewerStateChangeNavigation({
+              atlasSelection.actions.navigateTo({
                 navigation: {
                   ...mockCurrNavigation,
                   ...overrideObj,
-                  positionReal: false,
-                  animation: {},
-                }
+                },
+                physical: true,
+                animation: true
               })
             )
           })
diff --git a/src/viewerModule/nehuba/statusCard/statusCard.component.ts b/src/viewerModule/nehuba/statusCard/statusCard.component.ts
index 6ac175a1d93dba044ed00e3a7d5e09bc6857dd31..26d6786e8b7f7ea3723eb47d7a32bc299c46b720 100644
--- a/src/viewerModule/nehuba/statusCard/statusCard.component.ts
+++ b/src/viewerModule/nehuba/statusCard/statusCard.component.ts
@@ -16,11 +16,13 @@ import { MatBottomSheet } from "@angular/material/bottom-sheet";
 import { MatDialog } from "@angular/material/dialog";
 import { ARIA_LABELS, QUICKTOUR_DESC } from 'common/constants'
 import { FormControl } from "@angular/forms";
-import { viewerStateNavigationStateSelector, viewerStateSelectedTemplatePureSelector } from "src/services/state/viewerState/selectors";
 
-import { viewerStateChangeNavigation } from "src/services/state/viewerState/actions";
-import { getNavigationStateFromConfig, NEHUBA_INSTANCE_INJTKN } from '../util'
+import { NEHUBA_INSTANCE_INJTKN } from '../util'
 import { IQuickTourData } from "src/ui/quickTour/constrants";
+import { actions } from "src/state/atlasSelection";
+import { atlasSelection } from "src/state";
+import { SapiSpaceModel } from "src/atlasComponents/sapi";
+import { getNehubaConfig } from "../config.service";
 
 @Component({
   selector : 'iav-cmp-viewer-nehuba-status',
@@ -31,7 +33,7 @@ export class StatusCardComponent implements OnInit, OnChanges{
 
   private _nehubaViewer: NehubaViewerUnit;
 
-  get nehubaViewer(){
+  get nehubaViewer(): NehubaViewerUnit{
     return this._nehubaViewer
   }
   set nehubaViewer(v: NehubaViewerUnit) {
@@ -43,7 +45,7 @@ export class StatusCardComponent implements OnInit, OnChanges{
   public arialabel = ARIA_LABELS.STATUS_PANEL
   public showFull = false
 
-  private selectedTemplatePure: any
+  private selectedTemplate: SapiSpaceModel
   private currentNavigation: any
   private subscriptions: Subscription[] = []
 
@@ -57,9 +59,6 @@ export class StatusCardComponent implements OnInit, OnChanges{
   }
 
   public SHARE_BTN_ARIA_LABEL = ARIA_LABELS.SHARE_BTN
-  public COPY_URL_TO_CLIPBOARD_ARIA_LABEL = ARIA_LABELS.SHARE_COPY_URL_CLIPBOARD
-  public SHARE_CUSTOM_URL_ARIA_LABEL = ARIA_LABELS.SHARE_CUSTOM_URL
-  public SHARE_CUSTOM_URL_DIALOG_ARIA_LABEL = ARIA_LABELS.SHARE_CUSTOM_URL_DIALOG
   public SHOW_FULL_STATUS_PANEL_ARIA_LABEL = ARIA_LABELS.SHOW_FULL_STATUS_PANEL
   public HIDE_FULL_STATUS_PANEL_ARIA_LABEL = ARIA_LABELS.HIDE_FULL_STATUS_PANEL
   constructor(
@@ -90,18 +89,18 @@ export class StatusCardComponent implements OnInit, OnChanges{
 
     this.subscriptions.push(
       this.store$.pipe(
-        select(viewerStateSelectedTemplatePureSelector)
-      ).subscribe(n => this.selectedTemplatePure = n)
+        select(atlasSelection.selectors.selectedTemplate)
+      ).subscribe(n => this.selectedTemplate = n)
     )
 
     this.subscriptions.push(
       this.store$.pipe(
-        select(viewerStateNavigationStateSelector)
+        select(atlasSelection.selectors.navigation)
       ).subscribe(nav => this.currentNavigation = nav)
     )
   }
 
-  ngOnChanges() {
+  ngOnChanges(): void {
     if (this.nehubaViewer?.viewerPosInReal$ && this.nehubaViewer?.viewerPosInVoxel$) {
       this.navVal$ = combineLatest([
         this.statusPanelRealSpace$,
@@ -151,7 +150,7 @@ export class StatusCardComponent implements OnInit, OnChanges{
     startWith(true)
   )
 
-  public textNavigateTo(string: string) {
+  public textNavigateTo(string: string): void {
     if (string.split(/[\s|,]+/).length >= 3 && string.split(/[\s|,]+/).slice(0, 3).every(entry => !isNaN(Number(entry.replace(/mm/, ''))))) {
       const pos = (string.split(/[\s|,]+/).slice(0, 3).map((entry) => Number(entry.replace(/mm/, '')) * (this.statusPanelRealSpace ? 1000000 : 1)))
       this.nehubaViewer.setNavigationState({
@@ -163,7 +162,7 @@ export class StatusCardComponent implements OnInit, OnChanges{
     }
   }
 
-  showBottomSheet(tmpl: TemplateRef<any>){
+  showBottomSheet(tmpl: TemplateRef<any>): void{
     this.bottomSheet.open(tmpl)
   }
 
@@ -176,28 +175,31 @@ export class StatusCardComponent implements OnInit, OnChanges{
    *
    * the info re: nehubaViewer can stay there, too
    */
-  public resetNavigation({rotation: rotationFlag = false, position: positionFlag = false, zoom : zoomFlag = false}: {rotation?: boolean, position?: boolean, zoom?: boolean}) {
+  public resetNavigation({rotation: rotationFlag = false, position: positionFlag = false, zoom : zoomFlag = false}: {rotation?: boolean, position?: boolean, zoom?: boolean}): void {
+    const config = getNehubaConfig(this.selectedTemplate)
     const {
       orientation,
-      position,
-      zoom
-    } = getNavigationStateFromConfig(this.selectedTemplatePure.nehubaConfig)
+      position
+    } = config.dataset.initialNgState.navigation.pose
+    const {
+      zoomFactor: zoom
+    } = config.dataset.initialNgState.navigation
 
     this.store$.dispatch(
-      viewerStateChangeNavigation({
+      actions.navigateTo({
         navigation: {
           ...this.currentNavigation,
           ...(rotationFlag ? { orientation: orientation } : {}),
           ...(positionFlag ? { position: position } : {}),
           ...(zoomFlag ? { zoom: zoom } : {}),
-          positionReal : false,
-          animation : {},
-        }
+        },
+        physical: true,
+        animation: true
       })
     )
   }
 
-  openDialog(tmpl: TemplateRef<any>, options) {
+  openDialog(tmpl: TemplateRef<any>, options: { ariaLabel: string }): void {
     const { ariaLabel } = options
     this.dialog.open(tmpl, {
       ariaLabel
diff --git a/src/viewerModule/nehuba/statusCard/statusCard.template.html b/src/viewerModule/nehuba/statusCard/statusCard.template.html
index d3086513b1c12eb6207f9b142a66777cb59cde88..d4daef878ea439f321f40fe5ebb06f6124e70867 100644
--- a/src/viewerModule/nehuba/statusCard/statusCard.template.html
+++ b/src/viewerModule/nehuba/statusCard/statusCard.template.html
@@ -3,7 +3,7 @@
   [quick-tour-order]="quickTourData.order"
   #statusCardQT="quickTour">
   <mat-card *ngIf="showFull; else showMin"
-    class="expandedContainer p-2 pt-1">
+    class="expandedContainer sxplr-p-2 sxplr-pt-1">
     
     <mat-card-content>
 
@@ -53,7 +53,7 @@
 
         <mat-slide-toggle
           [formControl]="statusPanelFormCtrl"
-          class="pl-2 pr-2">
+          class="sxplr-pl-2 sxplr-pr-2">
         </mat-slide-toggle>
 
         <span class="d-flex align-items center">
@@ -79,7 +79,7 @@
 
         <div class="w-0 position-relative">
           <button
-            (click)="showBottomSheet(shareTmpl)"
+            sxplr-share-view
             [attr.aria-label]="SHARE_BTN_ARIA_LABEL"
             mat-icon-button
             class="position-absolute share-btn">
@@ -106,12 +106,12 @@
 
 <!-- minimised status bar -->
 <ng-template #showMin>
-  <div class="iv-custom-comp text overflow-visible text-nowrap d-inline-flex align-items-center m-1 mt-3"
+  <div class="sxplr-custom-cmp text of-visible text-nowrap d-inline-flex align-items-center m-1 mt-3"
     iav-media-query
     #media="iavMediaQuery">
 
     <i aria-label="viewer navigation" class="fas fa-compass"></i>
-    <span *ngIf="(media.mediaBreakPoint$ | async) < 3" class="pl-2">
+    <span *ngIf="(media.mediaBreakPoint$ | async) < 3" class="sxplr-pl-2">
       {{ navVal$ | async }}
     </span>
 
@@ -124,62 +124,3 @@
     </button>
   </div>
 </ng-template>
-
-<!-- share template -->
-<ng-template #shareTmpl>
-  <h4 class="mat-h4">
-    Share via
-  </h4>
-  <mat-nav-list>
-    <mat-list-item iav-clipboard-copy
-      [attr.aria-label]="COPY_URL_TO_CLIPBOARD_ARIA_LABEL"
-      [attr.tab-index]="10">
-      <mat-icon
-        class="mr-4"
-        fontSet="fas"
-        fontIcon="fa-copy">
-      </mat-icon>
-      <span>
-        Copy link to this view
-      </span>
-    </mat-list-item>
-    <mat-list-item
-      [attr.aria-label]="SHARE_CUSTOM_URL_ARIA_LABEL"
-      [attr.tab-index]="10"
-      (click)="openDialog(shareSaneUrl, { ariaLabel: SHARE_CUSTOM_URL_ARIA_LABEL })"
-      [matTooltip]="SHARE_CUSTOM_URL_ARIA_LABEL"
-      >
-      <mat-icon
-        class="mr-4"
-        fontSet="fas"
-        fontIcon="fa-link">
-      </mat-icon>
-
-      <span>
-        Create custom URL
-      </span>
-
-    </mat-list-item>
-  </mat-nav-list>
-</ng-template>
-
-<ng-template #shareSaneUrl>
-  <h2 mat-dialog-title>
-    Create custom URL
-  </h2>
-
-  <div mat-dialog-content>
-    <iav-sane-url iav-state-aggregator
-      [stateTobeSaved]="stateAggregator.jsonifiedState$ | async"
-      #stateAggregator="iavStateAggregator">
-    </iav-sane-url>
-  </div>
-
-  <div mat-dialog-actions
-    class="d-flex justify-content-center">
-    <button mat-button
-      mat-dialog-close>
-      close
-    </button>
-  </div>
-</ng-template>
diff --git a/src/viewerModule/nehuba/store/actions.ts b/src/viewerModule/nehuba/store/actions.ts
index 38c6572e1259651318bede3684278e71179f3daa..c8392e03b7fa423286c416c9c67fc83ad2cb4dfd 100644
--- a/src/viewerModule/nehuba/store/actions.ts
+++ b/src/viewerModule/nehuba/store/actions.ts
@@ -1,15 +1,8 @@
 import { createAction, props } from "@ngrx/store";
-import { INgLayerInterface } from "src/services/state/ngViewerState.store";
+
 import { NEHUBA_VIEWER_FEATURE_KEY } from "../constants";
 import { IAuxMesh } from "./type";
 
-export const actionAddNgLayer = createAction(
-  `[${NEHUBA_VIEWER_FEATURE_KEY}] [addNgLayer]`,
-  props<{
-    layers: INgLayerInterface[]
-  }>()
-)
-
 export const actionSetAuxMesh = createAction(
   `[${NEHUBA_VIEWER_FEATURE_KEY}] [setAuxMesh]`,
   props<{
@@ -30,7 +23,3 @@ export const actionSetAuxMeshes = createAction(
     payload: IAuxMesh[]
   }>()
 )
-
-export const actionClearAuxMeshes = createAction(
-  `[${NEHUBA_VIEWER_FEATURE_KEY}] [clearAuxMeshes]`
-)
diff --git a/src/viewerModule/nehuba/store/index.ts b/src/viewerModule/nehuba/store/index.ts
index a384638d8bf82bd1b38296d9a061405b552cd7f1..686993e8c27a8dae11574a1355ae53c0e76696f9 100644
--- a/src/viewerModule/nehuba/store/index.ts
+++ b/src/viewerModule/nehuba/store/index.ts
@@ -1,8 +1,6 @@
 export {
-  actionAddNgLayer,
   actionRemoveAuxMesh,
   actionSetAuxMesh,
-  actionClearAuxMeshes,
   actionSetAuxMeshes,
 } from './actions'
 export {
@@ -14,5 +12,4 @@ export {
 export {
   IAuxMesh,
   INehubaFeature,
-  INgLayerInterface
 } from './type'
diff --git a/src/viewerModule/nehuba/store/type.ts b/src/viewerModule/nehuba/store/type.ts
index 4b1d4d3d69ab657aa67edbd18512397cda07d495..d45347a166b1630d9ecc47cebbfaa48a3a95985b 100644
--- a/src/viewerModule/nehuba/store/type.ts
+++ b/src/viewerModule/nehuba/store/type.ts
@@ -1,3 +1,5 @@
+import { atlasAppearance } from "src/state"
+
 export interface IAuxMesh {
   ['@id']: string
   name: string
@@ -8,19 +10,9 @@ export interface IAuxMesh {
   visible: boolean
 }
 
-export interface INgLayerInterface {
-  name: string // displayName
-  source: string
-  mixability: string // base | mixable | nonmixable
-  annotation?: string //
-  id?: string // unique identifier
-  visible?: boolean
-  shader?: string
-  transform?: any
-}
 
 export interface INehubaFeature {
-  layers: INgLayerInterface[]
+  layers: atlasAppearance.NgLayerCustomLayer[]
   panelMode: string
   panelOrder: string
   octantRemoval: boolean
diff --git a/src/viewerModule/nehuba/store/util.ts b/src/viewerModule/nehuba/store/util.ts
new file mode 100644
index 0000000000000000000000000000000000000000..14a59dc1d78250caf449c252eacd8acd80e9524a
--- /dev/null
+++ b/src/viewerModule/nehuba/store/util.ts
@@ -0,0 +1,10 @@
+import { SapiParcellationModel, SapiRegionModel } from "src/atlasComponents/sapi";
+
+export type ParcVolumeSpec = {
+  volumeSrc: string
+  parcellation: SapiParcellationModel
+  regions: {
+    labelIndex: number
+    region: SapiRegionModel
+  }[]
+}
diff --git a/src/viewerModule/nehuba/touchSideClass.directive.ts b/src/viewerModule/nehuba/touchSideClass.directive.ts
deleted file mode 100644
index 44cdac937990acc2b79bb03a70d545fb8030af1b..0000000000000000000000000000000000000000
--- a/src/viewerModule/nehuba/touchSideClass.directive.ts
+++ /dev/null
@@ -1,51 +0,0 @@
-import { Directive, ElementRef, Input, OnDestroy, OnInit } from "@angular/core";
-import { select, Store } from "@ngrx/store";
-import { Observable, Subscription } from "rxjs";
-import { distinctUntilChanged, tap } from "rxjs/operators";
-import { ngViewerSelectorPanelMode } from "src/services/state/ngViewerState/selectors";
-import { IavRootStoreInterface } from "src/services/stateStore.service";
-import { addTouchSideClasses, removeTouchSideClasses } from "src/viewerModule/nehuba/util";
-
-
-@Directive({
-  selector: '[touch-side-class]',
-  exportAs: 'touchSideClass',
-})
-
-export class TouchSideClass implements OnDestroy, OnInit {
-  @Input('touch-side-class')
-  public panelNativeIndex: number
-
-  public panelMode: string
-  private panelMode$: Observable<string>
-
-  private subscriptions: Subscription[] = []
-
-  constructor(
-    private store$: Store<IavRootStoreInterface>,
-    private el: ElementRef,
-  ) {
-
-    this.panelMode$ = this.store$.pipe(
-      select(ngViewerSelectorPanelMode),
-      distinctUntilChanged(),
-      tap(mode => this.panelMode = mode),
-    )
-  }
-
-  public ngOnInit() {
-    this.subscriptions.push(
-
-      this.panelMode$.subscribe(panelMode => {
-        removeTouchSideClasses(this.el.nativeElement)
-        addTouchSideClasses(this.el.nativeElement, this.panelNativeIndex, panelMode)
-      }),
-    )
-  }
-
-  public ngOnDestroy() {
-    while (this.subscriptions.length > 0) {
-      this.subscriptions.pop().unsubscribe()
-    }
-  }
-}
diff --git a/src/viewerModule/nehuba/types.ts b/src/viewerModule/nehuba/types.ts
index 35cca295acae535d68ba6bb98ce282650c830e45..6fd0dddab5fce72039cd81a92ac3b377c294f8fe 100644
--- a/src/viewerModule/nehuba/types.ts
+++ b/src/viewerModule/nehuba/types.ts
@@ -1,3 +1,4 @@
+import { SapiRegionModel } from "src/atlasComponents/sapi";
 import { INavObj } from "./navigation.service";
 
 export type TNehubaContextInfo = {
@@ -9,5 +10,6 @@ export type TNehubaContextInfo = {
   nehuba: {
     layerName: string
     labelIndices: number[]
+    regions: SapiRegionModel[]
   }[]
 }
diff --git a/src/viewerModule/nehuba/util.spec.ts b/src/viewerModule/nehuba/util.spec.ts
index 0b022b7281e319318a0ae261ef8827b77334e7c1..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644
--- a/src/viewerModule/nehuba/util.spec.ts
+++ b/src/viewerModule/nehuba/util.spec.ts
@@ -1,141 +0,0 @@
-import { cvtNavigationObjToNehubaConfig } from './util'
-
-const currentNavigation = {
-  position: [4, 5, 6],
-  orientation: [0, 0, 0, 1],
-  perspectiveOrientation: [ 0, 0, 0, 1],
-  perspectiveZoom: 2e5,
-  zoom: 1e5
-}
-
-const defaultPerspectiveZoom = 1e6
-const defaultZoom = 1e6
-
-const defaultNavigationObject = {
-  orientation: [0, 0, 0, 1],
-  perspectiveOrientation: [0 , 0, 0, 1],
-  perspectiveZoom: defaultPerspectiveZoom,
-  zoom: defaultZoom,
-  position: [0, 0, 0],
-  positionReal: true
-}
-
-const defaultNehubaConfigObject = {
-  perspectiveOrientation: [0, 0, 0, 1],
-  perspectiveZoom: 1e6,
-  navigation: {
-    pose: {
-      position: {
-        voxelCoordinates: [0, 0, 0],
-        voxelSize: [1,1,1]
-      },
-      orientation: [0, 0, 0, 1],
-    },
-    zoomFactor: defaultZoom
-  }
-}
-
-const bigbrainNehubaConfig = {
-  "showDefaultAnnotations": false,
-  "layers": {
-  },
-  "navigation": {
-    "pose": {
-      "position": {
-        "voxelSize": [
-          21166.666015625,
-          20000,
-          21166.666015625
-        ],
-        "voxelCoordinates": [
-          -21.8844051361084,
-          16.288618087768555,
-          28.418994903564453
-        ]
-      }
-    },
-    "zoomFactor": 350000
-  },
-  "perspectiveOrientation": [
-    0.3140767216682434,
-    -0.7418519854545593,
-    0.4988985061645508,
-    -0.3195493221282959
-  ],
-  "perspectiveZoom": 1922235.5293810747
-}
-
-describe('> util.ts', () => {
-  
-  describe('> cvtNavigationObjToNehubaConfig', () => {
-    const validNavigationObj = currentNavigation
-    describe('> if inputs are malformed', () => {
-      describe('> if navigation object is malformed, uses navigation default object', () => {
-        it('> if navigation object is null', () => {
-          const v1 = cvtNavigationObjToNehubaConfig(null, bigbrainNehubaConfig)
-          const v2 = cvtNavigationObjToNehubaConfig(defaultNavigationObject, bigbrainNehubaConfig)
-          expect(v1).toEqual(v2)
-        })
-        it('> if navigation object is undefined', () => {
-          const v1 = cvtNavigationObjToNehubaConfig(undefined, bigbrainNehubaConfig)
-          const v2 = cvtNavigationObjToNehubaConfig(defaultNavigationObject, bigbrainNehubaConfig)
-          expect(v1).toEqual(v2)
-        })
-
-        it('> if navigation object is otherwise malformed', () => {
-          const v1 = cvtNavigationObjToNehubaConfig({foo: 'bar'}, bigbrainNehubaConfig)
-          const v2 = cvtNavigationObjToNehubaConfig(defaultNavigationObject, bigbrainNehubaConfig)
-          expect(v1).toEqual(v2)
-
-          const v3 = cvtNavigationObjToNehubaConfig({}, bigbrainNehubaConfig)
-          const v4 = cvtNavigationObjToNehubaConfig(defaultNavigationObject, bigbrainNehubaConfig)
-          expect(v3).toEqual(v4)
-        })
-      })
-
-      describe('> if nehubaConfig object is malformed, use default nehubaConfig obj', () => {
-        it('> if nehubaConfig is null', () => {
-          const v1 = cvtNavigationObjToNehubaConfig(validNavigationObj, null)
-          const v2 = cvtNavigationObjToNehubaConfig(validNavigationObj, defaultNehubaConfigObject)
-          expect(v1).toEqual(v2)
-        })
-
-        it('> if nehubaConfig is undefined', () => {
-          const v1 = cvtNavigationObjToNehubaConfig(validNavigationObj, undefined)
-          const v2 = cvtNavigationObjToNehubaConfig(validNavigationObj, defaultNehubaConfigObject)
-          expect(v1).toEqual(v2)
-        })
-
-        it('> if nehubaConfig is otherwise malformed', () => {
-          const v1 = cvtNavigationObjToNehubaConfig(validNavigationObj, {})
-          const v2 = cvtNavigationObjToNehubaConfig(validNavigationObj, defaultNehubaConfigObject)
-          expect(v1).toEqual(v2)
-
-          const v3 = cvtNavigationObjToNehubaConfig(validNavigationObj, {foo: 'bar'})
-          const v4 = cvtNavigationObjToNehubaConfig(validNavigationObj, defaultNehubaConfigObject)
-          expect(v3).toEqual(v4)
-        })
-      })
-    })
-    it('> converts navigation object and reference nehuba config object to navigation object', () => {
-      const convertedVal = cvtNavigationObjToNehubaConfig(validNavigationObj, bigbrainNehubaConfig)
-      const { perspectiveOrientation, orientation, zoom, perspectiveZoom, position } = validNavigationObj
-      
-      expect(convertedVal).toEqual({
-        navigation: {
-          pose: {
-            position: {
-              voxelSize: bigbrainNehubaConfig.navigation.pose.position.voxelSize,
-              voxelCoordinates: [0, 1, 2].map(idx => position[idx] / bigbrainNehubaConfig.navigation.pose.position.voxelSize[idx])
-            },
-            orientation
-          },
-          zoomFactor: zoom
-        },
-        perspectiveOrientation: perspectiveOrientation,
-        perspectiveZoom: perspectiveZoom
-      })
-    })
-  })
-
-})
diff --git a/src/viewerModule/nehuba/util.ts b/src/viewerModule/nehuba/util.ts
index 398ad2d82aafbbdafc6c8b8c50d72239a91ded92..71d7a985a09c1ecfa3dd79fdac4c75f3c380bef9 100644
--- a/src/viewerModule/nehuba/util.ts
+++ b/src/viewerModule/nehuba/util.ts
@@ -1,9 +1,9 @@
 import { InjectionToken } from '@angular/core'
 import { Observable, pipe } from 'rxjs'
 import { filter, scan, take } from 'rxjs/operators'
-import { PANELS } from 'src/services/state/ngViewerState.store.helper'
-import { getViewer } from 'src/util/fn'
+import { getExportNehuba, getViewer } from 'src/util/fn'
 import { NehubaViewerUnit } from './nehubaViewer/nehubaViewer.component'
+import { userInterface } from 'src/state'
 
 const flexContCmnCls = ['w-100', 'h-100', 'd-flex', 'justify-content-center', 'align-items-stretch']
 
@@ -37,49 +37,48 @@ const left = true
 const right = true
 const bottom = true
 
-const mapModeIdxClass = new Map()
+const mapModeIdxClass = new Map<
+  userInterface.PanelMode, Map<number,{
+    top?: boolean
+    bottom?: boolean
+    left?: boolean
+    right?: boolean
+  }>
+>()
 
-mapModeIdxClass.set(PANELS.FOUR_PANEL, new Map([
+mapModeIdxClass.set("FOUR_PANEL", new Map([
   [0, { top, left }],
   [1, { top, right }],
   [2, { bottom, left }],
   [3, { right, bottom }],
 ]))
 
-mapModeIdxClass.set(PANELS.SINGLE_PANEL, new Map([
+mapModeIdxClass.set("SINGLE_PANEL", new Map([
   [0, { top, left, right, bottom }],
   [1, {}],
   [2, {}],
   [3, {}],
 ]))
 
-mapModeIdxClass.set(PANELS.H_ONE_THREE, new Map([
+mapModeIdxClass.set("H_ONE_THREE", new Map([
   [0, { top, left, bottom }],
   [1, { top, right }],
   [2, { right }],
   [3, { bottom, right }],
 ]))
 
-mapModeIdxClass.set(PANELS.V_ONE_THREE, new Map([
+mapModeIdxClass.set("V_ONE_THREE", new Map([
   [0, { top, left, right }],
   [1, { bottom, left }],
   [2, { bottom }],
   [3, { bottom, right }],
 ]))
 
-export const removeTouchSideClasses = (panel: HTMLElement) => {
-  panel.classList.remove(
-    `touch-top`,
-    `touch-left`,
-    `touch-right`,
-    `touch-bottom`)
-  return panel
-}
-
+type PanelTouchSide = 'left' | 'right' | 'top' | 'bottom'
 /**
  * gives a clue of the approximate location of the panel, allowing position of checkboxes/scale bar to be placed in unobtrustive places
  */
-export const panelTouchSide = (panel: HTMLElement, { top: touchTop, left: touchLeft, right: touchRight, bottom: touchBottom }: any) => {
+export const panelTouchSide = (panel: HTMLElement, { top: touchTop, left: touchLeft, right: touchRight, bottom: touchBottom }: Partial<Record<PanelTouchSide, boolean>>): HTMLElement => {
   if (touchTop) { panel.classList.add(`touch-top`) }
   if (touchLeft) { panel.classList.add(`touch-left`) }
   if (touchRight) { panel.classList.add(`touch-right`) }
@@ -87,7 +86,7 @@ export const panelTouchSide = (panel: HTMLElement, { top: touchTop, left: touchL
   return panel
 }
 
-export const addTouchSideClasses = (panel: HTMLElement, actualOrderIndex: number, panelMode: string) => {
+export const addTouchSideClasses = (panel: HTMLElement, actualOrderIndex: number, panelMode: userInterface.PanelMode): HTMLElement => {
 
   if (actualOrderIndex < 0) { return panel }
 
@@ -100,10 +99,10 @@ export const addTouchSideClasses = (panel: HTMLElement, actualOrderIndex: number
   return panelTouchSide(panel, classArg)
 }
 
-export const getHorizontalOneThree = (panels: [HTMLElement, HTMLElement, HTMLElement, HTMLElement]) => {
+export const getHorizontalOneThree = (panels: [HTMLElement, HTMLElement, HTMLElement, HTMLElement]): HTMLElement => {
   washPanels(panels)
 
-  panels.forEach((panel, idx) => addTouchSideClasses(panel, idx, PANELS.H_ONE_THREE))
+  panels.forEach((panel, idx) => addTouchSideClasses(panel, idx, "H_ONE_THREE"))
 
   const majorContainer = makeCol(panels[0])
   const minorContainer = makeCol(panels[1], panels[2], panels[3])
@@ -114,10 +113,10 @@ export const getHorizontalOneThree = (panels: [HTMLElement, HTMLElement, HTMLEle
   return makeRow(majorContainer, minorContainer)
 }
 
-export const getVerticalOneThree = (panels: [HTMLElement, HTMLElement, HTMLElement, HTMLElement]) => {
+export const getVerticalOneThree = (panels: [HTMLElement, HTMLElement, HTMLElement, HTMLElement]): HTMLDivElement => {
   washPanels(panels)
 
-  panels.forEach((panel, idx) => addTouchSideClasses(panel, idx, PANELS.V_ONE_THREE))
+  panels.forEach((panel, idx) => addTouchSideClasses(panel, idx, "V_ONE_THREE"))
 
   const majorContainer = makeRow(panels[0])
   const minorContainer = makeRow(panels[1], panels[2], panels[3])
@@ -128,10 +127,10 @@ export const getVerticalOneThree = (panels: [HTMLElement, HTMLElement, HTMLEleme
   return makeCol(majorContainer, minorContainer)
 }
 
-export const getFourPanel = (panels: [HTMLElement, HTMLElement, HTMLElement, HTMLElement]) => {
+export const getFourPanel = (panels: [HTMLElement, HTMLElement, HTMLElement, HTMLElement]): HTMLDivElement => {
   washPanels(panels)
 
-  panels.forEach((panel, idx) => addTouchSideClasses(panel, idx, PANELS.FOUR_PANEL))
+  panels.forEach((panel, idx) => addTouchSideClasses(panel, idx, "FOUR_PANEL"))
 
   const majorContainer = makeRow(panels[0], panels[1])
   const minorContainer = makeRow(panels[2], panels[3])
@@ -142,10 +141,10 @@ export const getFourPanel = (panels: [HTMLElement, HTMLElement, HTMLElement, HTM
   return makeCol(majorContainer, minorContainer)
 }
 
-export const getSinglePanel = (panels: [HTMLElement, HTMLElement, HTMLElement, HTMLElement]) => {
+export const getSinglePanel = (panels: [HTMLElement, HTMLElement, HTMLElement, HTMLElement]): HTMLDivElement => {
   washPanels(panels)
 
-  panels.forEach((panel, idx) => addTouchSideClasses(panel, idx, PANELS.SINGLE_PANEL))
+  panels.forEach((panel, idx) => addTouchSideClasses(panel, idx, "SINGLE_PANEL"))
 
   const majorContainer = makeRow(panels[0])
   const minorContainer = makeRow(panels[1], panels[2], panels[3])
@@ -158,60 +157,15 @@ export const getSinglePanel = (panels: [HTMLElement, HTMLElement, HTMLElement, H
   return makeRow(majorContainer, minorContainer)
 }
 
-export const isIdentityQuat = ori => Math.abs(ori[0]) < 1e-6
+export const isIdentityQuat = (ori: number[]): boolean => Math.abs(ori[0]) < 1e-6
   && Math.abs(ori[1]) < 1e-6
   && Math.abs(ori[2]) < 1e-6
   && Math.abs(ori[3] - 1) < 1e-6
 
-export const getNavigationStateFromConfig = nehubaConfig => {
-  const {
-    navigation = {},
-    perspectiveOrientation = [0, 0, 0, 1],
-    perspectiveZoom = 1e7
-  } = (nehubaConfig && nehubaConfig.dataset && nehubaConfig.dataset.initialNgState) || {}
-
-  const {
-    zoomFactor = 3e5,
-    pose = {}
-  } = navigation || {}
-
-  const {
-    voxelSize = [1e6, 1e6, 1e6],
-    voxelCoordinates = [0, 0, 0]
-  } = (pose && pose.position) || {}
-
-  const {
-    orientation = [0, 0, 0, 1]
-  } = pose || {}
-
-  return {
-    orientation,
-    perspectiveOrientation,
-    perspectiveZoom,
-    position: [0, 1, 2].map(idx => voxelSize[idx] * voxelCoordinates[idx]),
-    zoom: zoomFactor
-  }
-}
-
-export const calculateSliceZoomFactor = (originalZoom) => originalZoom
-  ? 700 * originalZoom / Math.min(window.innerHeight, window.innerWidth)
-  : 1e7
-
-export const singleLmUnchanged = (lm: {id: string, position: [number, number, number]}, map: Map<string, [number, number, number]>) =>
-  map.has(lm.id) && map.get(lm.id).every((value, idx) => value === lm.position[idx])
-
-export const userLmUnchanged = (oldlms, newlms) => {
-  const oldmap = new Map(oldlms.map(lm => [lm.id, lm.position]))
-  const newmap = new Map(newlms.map(lm => [lm.id, lm.position]))
-
-  return oldlms.every(lm => singleLmUnchanged(lm, newmap as Map<string, [number, number, number]>))
-    && newlms.every(lm => singleLmUnchanged(lm, oldmap as Map<string, [number, number, number]>))
-}
-
-export const importNehubaFactory = appendSrc => {
-  let pr: Promise<any>
+export const importNehubaFactory = (appendSrc: (src: string) => Promise<void>): () => Promise<void> => {
+  let pr: Promise<void>
   return () => {
-    if ((window as any).export_nehuba) return Promise.resolve()
+    if (getExportNehuba()) return Promise.resolve()
 
     if (pr) return pr
     pr = appendSrc('main.bundle.js')
@@ -220,42 +174,6 @@ export const importNehubaFactory = appendSrc => {
   }
 }
 
-
-export const isFirstRow = (cell: HTMLElement) => {
-  const { parentElement: row } = cell
-  const { parentElement: container } = row
-  return container.firstElementChild === row
-}
-
-export const isFirstCell = (cell: HTMLElement) => {
-  const { parentElement: row } = cell
-  return row.firstElementChild === cell
-}
-
-export const scanSliceViewRenderFn: (acc: [boolean, boolean, boolean], curr: CustomEvent) => [boolean, boolean, boolean] = (acc, curr) => {
-  
-  const target = curr.target as HTMLElement
-  const targetIsFirstRow = isFirstRow(target)
-  const targetIsFirstCell = isFirstCell(target)
-  const idx = targetIsFirstRow
-    ? targetIsFirstCell
-      ? 0
-      : 1
-    : targetIsFirstCell
-      ? 2
-      : null
-
-  const returnAcc = [...acc]
-  const num1 = typeof curr.detail.missingChunks === 'number' ? curr.detail.missingChunks : 0
-  const num2 = typeof curr.detail.missingImageChunks === 'number' ? curr.detail.missingImageChunks : 0
-  if (num1 < 0 && num2 < 0) {
-    returnAcc[idx] = true
-  } else {
-    returnAcc[idx] = Math.max(num1, num2) > 0
-  }
-  return returnAcc as [boolean, boolean, boolean]
-}
-
 export const takeOnePipe = () => {
 
   return pipe(
@@ -290,40 +208,20 @@ export const takeOnePipe = () => {
 
 export const NEHUBA_INSTANCE_INJTKN = new InjectionToken<Observable<NehubaViewerUnit>>('NEHUBA_INSTANCE_INJTKN')
 
-export function cvtNavigationObjToNehubaConfig(navigationObj, nehubaConfigObj){
-  const {
-    orientation = [0, 0, 0, 1],
-    perspectiveOrientation = [0, 0, 0, 1],
-    perspectiveZoom = 1e6,
-    zoom = 1e6,
-    position = [0, 0, 0],
-    positionReal = true,
-  } = navigationObj || {}
-
-  const voxelSize = (() => {
-    const {
-      navigation = {}
-    } = nehubaConfigObj || {}
-    const { pose = {} } = navigation
-    const { position = {} } = pose
-    const { voxelSize = [1, 1, 1] } = position
-    return voxelSize
-  })()
+export function serializeSegment(ngId: string, label: number | string): string{
+  return `${ngId}#${label}`
+}
 
+export function deserializeSegment(id: string): {ngId: string, label: number} {
+  const split = id.split('#')
+  if (split.length !== 2) {
+    throw new Error(`deserializeSegment error at ${id}. expecting splitting # to result in length 2, got ${split.length}`)
+  }
+  if (isNaN(Number(split[1]))) {
+    throw new Error(`deserializeSegment error at ${id}. expecting second element to be numberable. It was not.`)
+  }
   return {
-    perspectiveOrientation,
-    perspectiveZoom,
-    navigation: {
-      pose: {
-        position: {
-          voxelCoordinates: positionReal
-            ? [0, 1, 2].map(idx => position[idx] / voxelSize[idx])
-            : position,
-          voxelSize
-        },
-        orientation,
-      },
-      zoomFactor: zoom
-    }
+    ngId: split[0],
+    label: Number(split[1])
   }
 }
diff --git a/src/viewerModule/nehuba/viewerCtrl/change-perspective-orientation/changePerspectiveOrientation.component.html b/src/viewerModule/nehuba/viewerCtrl/change-perspective-orientation/changePerspectiveOrientation.component.html
index c194ea9659faa473dbf30de56ace6b5e6e83901d..4aa94fa5d1bce357ba7caba1c6a1b3f901cdc30c 100644
--- a/src/viewerModule/nehuba/viewerCtrl/change-perspective-orientation/changePerspectiveOrientation.component.html
+++ b/src/viewerModule/nehuba/viewerCtrl/change-perspective-orientation/changePerspectiveOrientation.component.html
@@ -4,7 +4,7 @@
 
 <mat-menu #perspectiveOrientationMenu="matMenu">
 
-    <div class="d-flex align-items-center iv-custom-comp text">
+    <div class="d-flex align-items-center sxplr-custom-cmp text">
         <button mat-button color="basic" class="flex-grow-1 text-left font-weight-normal"
                 (click)="set3DViewPoint('coronal', 'first')">
             Coronal view
@@ -15,7 +15,7 @@
         </button>
     </div>
 
-    <div class="d-flex align-items-center iv-custom-comp text"> <!--mat-menu-item-->
+    <div class="d-flex align-items-center sxplr-custom-cmp text"> <!--mat-menu-item-->
         <button mat-button color="basic" class="flex-grow-1 text-left font-weight-normal"
                 (click)="set3DViewPoint('sagittal', 'first')">
             Sagittal view
@@ -26,7 +26,7 @@
         </button>
     </div>
 
-    <div class="d-flex align-items-center iv-custom-comp text"> <!--mat-menu-item-->
+    <div class="d-flex align-items-center sxplr-custom-cmp text"> <!--mat-menu-item-->
         <button mat-button color="basic" class="flex-grow-1 text-left font-weight-normal"
                 (click)="set3DViewPoint('axial', 'first')">
             Axial view
diff --git a/src/viewerModule/nehuba/viewerCtrl/change-perspective-orientation/changePerspectiveOrientation.component.ts b/src/viewerModule/nehuba/viewerCtrl/change-perspective-orientation/changePerspectiveOrientation.component.ts
index 0c4bdb1300fb0e7bc094a81d2dcac2d39f67c0d1..9f9d10f7b6467a186111d749e22b17dbebeb3129 100644
--- a/src/viewerModule/nehuba/viewerCtrl/change-perspective-orientation/changePerspectiveOrientation.component.ts
+++ b/src/viewerModule/nehuba/viewerCtrl/change-perspective-orientation/changePerspectiveOrientation.component.ts
@@ -1,6 +1,6 @@
 import { Component } from '@angular/core';
-import {viewerStateChangeNavigation} from "src/services/state/viewerState/actions";
 import {Store} from "@ngrx/store";
+import { actions } from 'src/state/atlasSelection';
 
 @Component({
   selector: 'app-change-perspective-orientation',
@@ -22,10 +22,11 @@ export class ChangePerspectiveOrientationComponent {
     const orientation = this.viewOrientations[plane][view === 'first'? 0 : 1]
 
     this.store$.dispatch(
-      viewerStateChangeNavigation({
+      actions.navigateTo({
         navigation: {
           perspectiveOrientation: orientation,
-        }
+        },
+        animation: true
       })
     )
   }
diff --git a/src/viewerModule/nehuba/viewerCtrl/viewerCtrlCmp/viewerCtrlCmp.component.spec.ts b/src/viewerModule/nehuba/viewerCtrl/viewerCtrlCmp/viewerCtrlCmp.component.spec.ts
index 54b6e08fa3d70dea75042b7372251e8be51e2de7..c5042ecfc22e3134ab6af2c967bdad86bf5d4933 100644
--- a/src/viewerModule/nehuba/viewerCtrl/viewerCtrlCmp/viewerCtrlCmp.component.spec.ts
+++ b/src/viewerModule/nehuba/viewerCtrl/viewerCtrlCmp/viewerCtrlCmp.component.spec.ts
@@ -4,16 +4,16 @@ import { FormsModule, ReactiveFormsModule } from "@angular/forms"
 import { MockStore, provideMockStore } from "@ngrx/store/testing"
 import { BehaviorSubject, of } from "rxjs"
 import { ComponentsModule } from "src/components"
-import { ngViewerSelectorOctantRemoval } from "src/services/state/ngViewerState.store.helper"
-import { viewerStateCustomLandmarkSelector, viewerStateSelectedTemplatePureSelector } from "src/services/state/viewerState/selectors"
 import { AngularMaterialModule } from "src/sharedModules"
-import {PureContantService, UtilModule} from "src/util"
+import { UtilModule } from "src/util"
 import { actionSetAuxMeshes, selectorAuxMeshes } from "../../store"
 import { NEHUBA_INSTANCE_INJTKN } from "../../util"
 import { ViewerCtrlCmp } from "./viewerCtrlCmp.component"
 import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'
 import { HarnessLoader } from "@angular/cdk/testing"
 import { MatSlideToggleHarness } from '@angular/material/slide-toggle/testing'
+import { atlasAppearance, atlasSelection } from "src/state"
+
 
 describe('> viewerCtrlCmp.component.ts', () => {
   describe('> ViewerCtrlCmp', () => {
@@ -65,14 +65,6 @@ describe('> viewerCtrlCmp.component.ts', () => {
             useFactory: () => {
               return new BehaviorSubject(mockNehubaViewer).asObservable()
             }
-          },
-          {
-            provide: PureContantService,
-            useFactory: () => {
-              return {
-                getViewerConfig: jasmine.createSpy('getViewerConfig')
-              }
-            }
           }
         ]
       }).compileComponents()
@@ -80,9 +72,8 @@ describe('> viewerCtrlCmp.component.ts', () => {
     })
     beforeEach(() => {
       mockStore = TestBed.inject(MockStore)
-      mockStore.overrideSelector(viewerStateSelectedTemplatePureSelector, {})
-      mockStore.overrideSelector(ngViewerSelectorOctantRemoval, true)
-      mockStore.overrideSelector(viewerStateCustomLandmarkSelector, [])
+      mockStore.overrideSelector(atlasSelection.selectors.selectedTemplate, {} as any)
+      mockStore.overrideSelector(atlasAppearance.selectors.octantRemoval, true)
       mockStore.overrideSelector(selectorAuxMeshes, [])
     })
 
@@ -140,39 +131,6 @@ describe('> viewerCtrlCmp.component.ts', () => {
           ).toHaveBeenCalledWith(!wasChecked)
         })
       })
-  
-      describe('> toggle delineation', () => {
-        
-        let toggleDelination: jasmine.Spy
-        const toggleName = 'toggle-delineation'
-        beforeEach(() => {
-          toggleDelination = spyOn<any>(fixture.componentInstance, 'toggleParcVsbl')
-        })
-        afterEach(() => {
-          toggleDelination.calls.reset()
-        })
-  
-        it('> toggleslider should exist', async () => {
-          const slideToggle = await loader.getAllHarnesses(
-            MatSlideToggleHarness.with({
-              name: toggleName,
-            })
-          )
-          expect(slideToggle.length).toBe(1)
-        })
-  
-        it('> toggling it should result in setOctantRemoval to be called', async () => {
-          const slideToggle = await loader.getAllHarnesses(
-            MatSlideToggleHarness.with({
-              name: toggleName,
-            })
-          )
-          await slideToggle[0].toggle()
-          expect(
-            toggleDelination
-          ).toHaveBeenCalled()
-        })
-      })
     })
 
     describe('> UI aux meshes', () => {
@@ -236,120 +194,5 @@ describe('> viewerCtrlCmp.component.ts', () => {
         )
       })
     })
-
-    describe('> flagDelin', () => {
-      let toggleParcVsblSpy: jasmine.Spy
-      beforeEach(() => {
-        fixture = TestBed.createComponent(ViewerCtrlCmp)
-        toggleParcVsblSpy = spyOn(fixture.componentInstance as any, 'toggleParcVsbl')
-        fixture.detectChanges()
-      })
-      it('> calls toggleParcVsbl', () => {
-        toggleParcVsblSpy.and.callFake(() => {})
-        fixture.componentInstance.flagDelin = false
-        expect(toggleParcVsblSpy).toHaveBeenCalled()
-      })
-    })
-    describe('> toggleParcVsbl', () => {
-      let getViewerConfigSpy: jasmine.Spy
-      let getLayerByNameSpy: jasmine.Spy
-      beforeEach(() => {
-        const pureCstSvc = TestBed.inject(PureContantService)
-        getLayerByNameSpy = mockNehubaViewer.nehubaViewer.ngviewer.layerManager.getLayerByName
-        getViewerConfigSpy = pureCstSvc.getViewerConfig as jasmine.Spy
-        fixture = TestBed.createComponent(ViewerCtrlCmp)
-        fixture.detectChanges()
-      })
-
-      it('> calls pureSvc.getViewerConfig', async () => {
-        getViewerConfigSpy.and.returnValue({})
-        await fixture.componentInstance['toggleParcVsbl']()
-        expect(getViewerConfigSpy).toHaveBeenCalled()
-      })
-
-      describe('> if _flagDelin is true', () => {
-        beforeEach(() => {
-          fixture.componentInstance['_flagDelin'] = true
-          fixture.componentInstance['hiddenLayerNames'] = [
-            'foo',
-            'bar',
-            'baz'
-          ]
-        })
-        it('> go through all hideen layer names and set them to true', async () => {
-          const setVisibleSpy = jasmine.createSpy('setVisible')
-          getLayerByNameSpy.and.returnValue({
-            setVisible: setVisibleSpy
-          })
-          await fixture.componentInstance['toggleParcVsbl']()
-          expect(getLayerByNameSpy).toHaveBeenCalledTimes(3)
-          for (const arg of ['foo', 'bar', 'baz']) {
-            expect(getLayerByNameSpy).toHaveBeenCalledWith(arg)
-          }
-          expect(setVisibleSpy).toHaveBeenCalledTimes(3)
-          expect(setVisibleSpy).toHaveBeenCalledWith(true)
-          expect(setVisibleSpy).not.toHaveBeenCalledWith(false)
-        })
-        it('> hiddenLayerNames resets', async () => {
-          await fixture.componentInstance['toggleParcVsbl']()
-          expect(fixture.componentInstance['hiddenLayerNames']).toEqual([])
-        })
-      })
-
-      describe('> if _flagDelin is false', () => {
-        let managedLayerSpyProp: jasmine.Spy
-        let setVisibleSpy: jasmine.Spy
-        beforeEach(() => {
-          fixture.componentInstance['_flagDelin'] = false
-          setVisibleSpy = jasmine.createSpy('setVisible')
-          getLayerByNameSpy.and.returnValue({
-            setVisible: setVisibleSpy
-          })
-          getViewerConfigSpy.and.resolveTo({
-            'foo': {},
-            'bar': {},
-            'baz': {}
-          })
-          managedLayerSpyProp = spyOnProperty(mockNehubaViewer.nehubaViewer.ngviewer.layerManager, 'managedLayers')
-          managedLayerSpyProp.and.returnValue([{
-            visible: true,
-            name: 'foo'
-          }, {
-            visible: false,
-            name: 'bar'
-          }, {
-            visible: true,
-            name: 'baz'
-          }])
-        })
-
-        afterEach(() => {
-          managedLayerSpyProp.calls.reset()
-        })
-
-        it('> calls schedulRedraw', async () => {
-          await fixture.componentInstance['toggleParcVsbl']()
-          await new Promise(rs => requestAnimationFrame(rs))
-          expect(mockNehubaViewer.nehubaViewer.ngviewer.display.scheduleRedraw).toHaveBeenCalled()
-        })
-
-        it('> only calls setVisible false on visible layers', async () => {
-          await fixture.componentInstance['toggleParcVsbl']()
-          expect(getLayerByNameSpy).toHaveBeenCalledTimes(2)
-          
-          for (const arg of ['foo', 'baz']) {
-            expect(getLayerByNameSpy).toHaveBeenCalledWith(arg)
-          }
-          expect(setVisibleSpy).toHaveBeenCalledTimes(2)
-          expect(setVisibleSpy).toHaveBeenCalledWith(false)
-          expect(setVisibleSpy).not.toHaveBeenCalledWith(true)
-        })
-
-        it('> sets hiddenLayerNames correctly', async () => {
-          await fixture.componentInstance['toggleParcVsbl']()
-          expect(fixture.componentInstance['hiddenLayerNames']).toEqual(['foo', 'baz'])
-        })
-      })
-    })
   })
 })
diff --git a/src/viewerModule/nehuba/viewerCtrl/viewerCtrlCmp/viewerCtrlCmp.component.ts b/src/viewerModule/nehuba/viewerCtrl/viewerCtrlCmp/viewerCtrlCmp.component.ts
index d46a84cfab7d129e94d0c542fd718fa7390e0b6c..2a44980c9fd4639d227129dc84005ce306323d64 100644
--- a/src/viewerModule/nehuba/viewerCtrl/viewerCtrlCmp/viewerCtrlCmp.component.ts
+++ b/src/viewerModule/nehuba/viewerCtrl/viewerCtrlCmp/viewerCtrlCmp.component.ts
@@ -1,16 +1,13 @@
-import { Component, HostBinding, Inject, Optional } from "@angular/core";
+import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, OnInit, Optional } from "@angular/core";
 import { select, Store } from "@ngrx/store";
-import { combineLatest, merge, Observable, of, Subscription } from "rxjs";
-import {filter, map, pairwise, withLatestFrom} from "rxjs/operators";
-import { ngViewerActionSetPerspOctantRemoval } from "src/services/state/ngViewerState/actions";
-import { ngViewerSelectorOctantRemoval } from "src/services/state/ngViewerState/selectors";
-import { viewerStateCustomLandmarkSelector, viewerStateGetSelectedAtlas, viewerStateSelectedTemplatePureSelector } from "src/services/state/viewerState/selectors";
+import { merge, Observable, of, Subscription } from "rxjs";
+import { pairwise, withLatestFrom} from "rxjs/operators";
 import { NehubaViewerUnit } from "src/viewerModule/nehuba";
 import { NEHUBA_INSTANCE_INJTKN } from "src/viewerModule/nehuba/util";
 import { ARIA_LABELS } from 'common/constants'
 import { actionSetAuxMeshes, selectorAuxMeshes } from "../../store";
 import { FormBuilder, FormControl, FormGroup } from "@angular/forms";
-import {PureContantService} from "src/util";
+import { atlasAppearance } from "src/state";
 
 @Component({
   selector: 'viewer-ctrl-component',
@@ -18,105 +15,40 @@ import {PureContantService} from "src/util";
   styleUrls: [
     './viewerCtrlCmp.style.css'
   ],
-  exportAs: 'viewerCtrlCmp'
+  exportAs: 'viewerCtrlCmp',
+  changeDetection: ChangeDetectionStrategy.OnPush,
 })
 
-export class ViewerCtrlCmp{
+export class ViewerCtrlCmp implements OnInit{
 
   public ARIA_LABELS = ARIA_LABELS
 
-  @HostBinding('attr.darktheme')
-  darktheme = false
-
-  private selectedAtlasId: string
-  private selectedTemplateId: string
-
-  private _flagDelin = true
-  get flagDelin(){
-    return this._flagDelin
-  }
-  set flagDelin(flag){
-    this._flagDelin = flag
-    this.toggleParcVsbl()
-  }
-
   private sub: Subscription[] = []
-  private hiddenLayerNames: string[] = []
 
-  private _removeOctantFlag: boolean
-  get removeOctantFlag(){
+  private _removeOctantFlag: boolean = true
+  get removeOctantFlag(): boolean{
     return this._removeOctantFlag
   }
-  set removeOctantFlag(val){
+  set removeOctantFlag(val: boolean){
     if (val === this._removeOctantFlag) return
     this._removeOctantFlag = val
     this.setOctantRemoval(this._removeOctantFlag)
+    this.cdr.detectChanges()
   }
 
   public nehubaViewerPerspectiveOctantRemoval$ = this.store$.pipe(
-    select(ngViewerSelectorOctantRemoval),
+    select(atlasAppearance.selectors.octantRemoval),
   )
 
-  public customLandmarks$: Observable<any> = this.store$.pipe(
-    select(viewerStateCustomLandmarkSelector),
-    map(lms => lms.map(lm => ({
-      ...lm,
-      geometry: {
-        position: lm.position
-      }
-    }))),
-  )
-
-  public auxMeshFormGroup: FormGroup
+  public auxMeshFormGroup: FormGroup = this.formBuilder.group({})
   private auxMeshesNamesSet: Set<string> = new Set()
   public auxMeshes$ = this.store$.pipe(
     select(selectorAuxMeshes),
   )
 
-  private nehubaInst: NehubaViewerUnit
-
-  get ngViewer() {
-    return this.nehubaInst?.nehubaViewer.ngviewer || (window as any).viewer
-  }
-
-  constructor(
-    private store$: Store<any>,
-    formBuilder: FormBuilder,
-    private pureConstantService: PureContantService,
-    @Optional() @Inject(NEHUBA_INSTANCE_INJTKN) private nehubaInst$: Observable<NehubaViewerUnit>,
-  ){
-
-    this.auxMeshFormGroup = formBuilder.group({})
-  
-
-    if (this.nehubaInst$) {
-      this.sub.push(
-        combineLatest([
-          this.customLandmarks$,
-          this.nehubaInst$,
-        ]).pipe(
-          filter(([_, nehubaInst]) => !!nehubaInst),
-        ).subscribe(([landmarks, nehubainst]) => {
-          this.setOctantRemoval(landmarks.length === 0)
-          nehubainst.updateUserLandmarks(landmarks)
-        }),
-        this.nehubaInst$.subscribe(nehubaInst => this.nehubaInst = nehubaInst)
-      )
-    } else {
-      console.warn(`NEHUBA_INSTANCE_INJTKN not provided`)
-    }
+  ngOnInit(): void {
 
     this.sub.push(
-      this.store$.select(viewerStateGetSelectedAtlas)
-        .pipe(filter(a => !!a))
-        .subscribe(sa => this.selectedAtlasId = sa['@id']),
-      this.store$.pipe(
-        select(viewerStateSelectedTemplatePureSelector)
-      ).subscribe(tmpl => {
-        this.selectedTemplateId = tmpl['@id']
-        const { useTheme } = tmpl || {}
-        this.darktheme = useTheme === 'dark'
-      }),
 
       this.nehubaViewerPerspectiveOctantRemoval$.subscribe(
         flag => this.removeOctantFlag = flag
@@ -141,6 +73,7 @@ export class ViewerCtrlCmp{
           this.auxMeshesNamesSet.add(mesh.ngId)
           this.auxMeshFormGroup.addControl(mesh['@id'], new FormControl(mesh.visible))
         }
+        this.cdr.detectChanges()
       }),
 
       this.auxMeshFormGroup.valueChanges.pipe(
@@ -165,48 +98,29 @@ export class ViewerCtrlCmp{
             })
           )
         }
+        this.cdr.detectChanges()
       })
     )
   }
 
-  private async toggleParcVsbl(){
-    const viewerConfig = await this.pureConstantService.getViewerConfig(this.selectedAtlasId, this.selectedTemplateId, null)
-
-    if (this.flagDelin) {
-      for (const name of this.hiddenLayerNames) {
-        const l = this.ngViewer.layerManager.getLayerByName(name)
-        l && l.setVisible(true)
-      }
-      this.hiddenLayerNames = []
-    } else {
-      this.hiddenLayerNames = []
-      const segLayerNames: string[] = []
-      for (const layer of this.ngViewer.layerManager.managedLayers) {
-        if (layer.visible && layer.name in viewerConfig) {
-          segLayerNames.push(layer.name)
-        }
-      }
-      for (const name of segLayerNames) {
-        const l = this.ngViewer.layerManager.getLayerByName(name)
-        l && l.setVisible(false)
-        this.hiddenLayerNames.push( name )
-      }
-    }
-
-    requestAnimationFrame(() => {
-      this.ngViewer.display.scheduleRedraw()
-    })
+  constructor(
+    private store$: Store<any>,
+    private formBuilder: FormBuilder,
+    private cdr: ChangeDetectorRef,
+    @Optional() @Inject(NEHUBA_INSTANCE_INJTKN) private nehubaInst$: Observable<NehubaViewerUnit>,
+  ){
+
   }
 
-  public setOctantRemoval(octantRemovalFlag: boolean) {
+  public setOctantRemoval(octantRemovalFlag: boolean): void {
     this.store$.dispatch(
-      ngViewerActionSetPerspOctantRemoval({
-        octantRemovalFlag
+      atlasAppearance.actions.setOctantRemoval({
+        flag: octantRemovalFlag
       })
     )
   }
 
-  public trackByAtId(_idx: number, obj: { ['@id']: string }) {
+  public trackByAtId(_idx: number, obj: { ['@id']: string }): string {
     return obj['@id']
   }
 }
diff --git a/src/viewerModule/nehuba/viewerCtrl/viewerCtrlCmp/viewerCtrlCmp.template.html b/src/viewerModule/nehuba/viewerCtrl/viewerCtrlCmp/viewerCtrlCmp.template.html
index 76aa348a8f659039b6c0ece363db4a79921be756..50f326abfe12df716ca25a14c9b73043cb53b004 100644
--- a/src/viewerModule/nehuba/viewerCtrl/viewerCtrlCmp/viewerCtrlCmp.template.html
+++ b/src/viewerModule/nehuba/viewerCtrl/viewerCtrlCmp/viewerCtrlCmp.template.html
@@ -1,28 +1,11 @@
-<h3 class="iv-custom-comp text mat-h3">
-  Volumes
-</h3>
-
-<mat-slide-toggle [(ngModel)]="flagDelin"
-  #delinToggle="matSlideToggle"
-  [iav-key-listener]="[{ type: 'keydown', key: 'q', target: 'document', capture: true }]"
-  (iav-key-event)="delinToggle.toggle()"
-  name="toggle-delineation">
-
-  <markdown-dom class="d-inline-block iv-custom-comp text"
-    markdown="Show delineations `[q]`">
-  </markdown-dom>
-</mat-slide-toggle>
-
-<mat-divider class="mt-2 mb-2"></mat-divider>
-
-<h3 class="iv-custom-comp text mat-h3">
+<h3 class="sxplr-custom-cmp text mat-h3">
   Perspective View
 </h3>
 
 <mat-slide-toggle [(ngModel)]="removeOctantFlag"
   [aria-label]="ARIA_LABELS.TOGGLE_FRONTAL_OCTANT"
   name="remove-frontal-octant">
-  <span class="iv-custom-comp text">
+  <span class="sxplr-custom-cmp text">
     Remove frontal octant
   </span>
 </mat-slide-toggle>
@@ -34,11 +17,12 @@
       [formControlName]="auxMesh['@id']"
       class="d-block"
       [name]="'toggle-aux-mesh-' + auxMesh['@id']">
-      <span class="iv-custom-comp text">
+      <span class="sxplr-custom-cmp text">
         {{ auxMesh.displayName || auxMesh.name }}
       </span>
     </mat-slide-toggle>
   </form>
 </ng-container>
 
-<app-change-perspective-orientation></app-change-perspective-orientation>
+<!-- TODO menu no longer showing, likely something to do with detaching cdr on nehubaGlue.component -->
+<app-change-perspective-orientation *ngIf="false"></app-change-perspective-orientation>
diff --git a/src/viewerModule/pipes/nehubaVCtxToBbox.pipe.ts b/src/viewerModule/pipes/nehubaVCtxToBbox.pipe.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e3ceaad4673e198e11eba7ca257ed0a334bb1ac6
--- /dev/null
+++ b/src/viewerModule/pipes/nehubaVCtxToBbox.pipe.ts
@@ -0,0 +1,37 @@
+import { TContextArg } from './../viewer.interface';
+import { Pipe, PipeTransform } from "@angular/core";
+
+type Point = [number, number, number]
+type BBox = [Point, Point]
+
+const MAGIC_RADIUS = 256
+
+@Pipe({
+  name: "nehubaVCtxToBbox",
+  pure: true
+})
+
+export class NehubaVCtxToBbox implements PipeTransform{
+  public transform(event: TContextArg<'nehuba' | 'threeSurfer'>, unit: string = "mm"): BBox{
+    if (!event) {
+      return null
+    }
+    if (event.viewerType === 'threeSurfer') {
+      return null
+    }
+    let divisor = 1
+    if (unit === "mm") {
+      divisor = 1e6
+    }
+    const { payload } = event as TContextArg<'nehuba'>
+    
+    if (!payload.nav) return null
+
+    const { position, zoom } = payload.nav
+    // position is in nm
+    // zoom can be directly applied as a multiple
+    const min = position.map(v => (v - (MAGIC_RADIUS * zoom)) / divisor) as Point
+    const max = position.map(v => (v + (MAGIC_RADIUS * zoom)) / divisor) as Point
+    return [min, max]
+  }
+}
diff --git a/src/viewerModule/threeSurfer/module.ts b/src/viewerModule/threeSurfer/module.ts
index 21f11580befb07553f6f613b3b0f86fa2366e8d6..7a1f1c3e3c908f64451cebc7db7e513d7a1f9e40 100644
--- a/src/viewerModule/threeSurfer/module.ts
+++ b/src/viewerModule/threeSurfer/module.ts
@@ -1,11 +1,14 @@
 import { CommonModule } from "@angular/common";
 import { NgModule } from "@angular/core";
 import { FormsModule } from "@angular/forms";
+import { StoreModule } from "@ngrx/store";
 import { ComponentsModule } from "src/components";
 import { AngularMaterialModule } from "src/sharedModules";
 import { UtilModule } from "src/util";
 import { ThreeSurferGlueCmp } from "./threeSurferGlue/threeSurfer.component";
 import { ThreeSurferViewerConfig } from "./tsViewerConfig/tsViewerConfig.component";
+import { nameSpace, reducer, ThreeSurferEffects } from "./store"
+import { EffectsModule } from "@ngrx/effects";
 
 @NgModule({
   imports: [
@@ -14,6 +17,13 @@ import { ThreeSurferViewerConfig } from "./tsViewerConfig/tsViewerConfig.compone
     UtilModule,
     FormsModule,
     ComponentsModule,
+    StoreModule.forFeature(
+      nameSpace,
+      reducer
+    ),
+    EffectsModule.forFeature([
+      ThreeSurferEffects,
+    ])
   ],
   declarations: [
     ThreeSurferGlueCmp,
diff --git a/src/viewerModule/threeSurfer/store/actions.ts b/src/viewerModule/threeSurfer/store/actions.ts
new file mode 100644
index 0000000000000000000000000000000000000000..613fcbbfc7b943dfc412717987314a2b34fb5fb6
--- /dev/null
+++ b/src/viewerModule/threeSurfer/store/actions.ts
@@ -0,0 +1,9 @@
+import { createAction, props } from "@ngrx/store";
+import { nameSpace } from "./const"
+
+export const selectVolumeById = createAction(
+  `${nameSpace} selectVolumeById`,
+  props<{
+    id: string
+  }>()
+)
diff --git a/src/viewerModule/threeSurfer/store/const.ts b/src/viewerModule/threeSurfer/store/const.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5643484e1b0628d986eb70dec222903a1fbfe6cd
--- /dev/null
+++ b/src/viewerModule/threeSurfer/store/const.ts
@@ -0,0 +1 @@
+export const nameSpace = `[threeSurfer]`
\ No newline at end of file
diff --git a/src/viewerModule/threeSurfer/store/effects.ts b/src/viewerModule/threeSurfer/store/effects.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4fa4c9f190783d18fac844ec69019352bfb6c855
--- /dev/null
+++ b/src/viewerModule/threeSurfer/store/effects.ts
@@ -0,0 +1,156 @@
+import { Injectable } from "@angular/core";
+import { createEffect } from "@ngrx/effects";
+import { select, Store } from "@ngrx/store";
+import { EMPTY, forkJoin, merge, Observable, of, pipe, throwError } from "rxjs";
+import { debounceTime, map, switchMap, withLatestFrom, filter, shareReplay, distinctUntilChanged } from "rxjs/operators";
+import { SAPI, SapiAtlasModel, SapiParcellationModel, SapiSpaceModel } from "src/atlasComponents/sapi";
+import { atlasAppearance, atlasSelection } from "src/state";
+import { ThreeSurferCustomLabelLayer, ThreeSurferCustomLayer } from "src/state/atlasAppearance/const";
+import * as selectors from "./selectors"
+import * as actions from "./actions"
+
+export const fromATP = {
+  getThreeSurfaces: (sapi: SAPI) => {
+    return pipe(
+      filter(
+        ({ atlas, template, parcellation }: {atlas: SapiAtlasModel, template: SapiSpaceModel, parcellation: SapiParcellationModel}) => !!atlas && !!template && !!parcellation
+      ),
+      switchMap(({ atlas, template, parcellation }: {atlas: SapiAtlasModel, template: SapiSpaceModel, parcellation: SapiParcellationModel}) => 
+        forkJoin({
+          surfaces: sapi.getSpace(atlas["@id"], template["@id"]).getVolumes().pipe(
+            map(
+              volumes => volumes.filter(vol => vol.data.type === "gii")
+            )
+          ),
+          labels: sapi.getParcellation(atlas["@id"], parcellation["@id"]).getVolumes().pipe(
+            map(
+              volumes => volumes.filter(vol =>
+                vol.data.type === "gii-label" &&
+                vol.data.space["@id"] === template["@id"]
+              )
+            )
+          )
+        })
+      )
+    )
+  }
+}
+
+@Injectable()
+export class ThreeSurferEffects {
+
+  private onATP$ = this.store.pipe(
+    atlasSelection.fromRootStore.distinctATP()
+  )
+
+  private selectedSurfaceId$ = this.store.pipe(
+    select(selectors.getSelectedVolumeId),
+    distinctUntilChanged()
+  )
+
+  private threeSurferBaseCustomLayers$: Observable<ThreeSurferCustomLayer[]> = this.store.pipe(
+    select(atlasAppearance.selectors.customLayers),
+    map(
+      cl => cl.filter(layer => layer.clType === "baselayer/threesurfer") as ThreeSurferCustomLayer[]
+    )
+  )
+
+  onATPClearBaseLayers = createEffect(() => merge(
+    this.onATP$,
+    this.selectedSurfaceId$,
+  ).pipe(
+    withLatestFrom(
+      this.threeSurferBaseCustomLayers$
+    ),
+    switchMap(([_, layers]) => 
+      of(
+        ...layers.map(layer => 
+          atlasAppearance.actions.removeCustomLayer({
+            id: layer.id
+          })  
+        )
+      )
+    )
+  ))
+
+  public onATPDebounceThreeSurferLayers$ = this.onATP$.pipe(
+    debounceTime(16),
+    fromATP.getThreeSurfaces(this.sapi),
+    shareReplay(1),
+  )
+
+  onATPDebounceHasSurfaceVolumes = createEffect(() => this.onATPDebounceThreeSurferLayers$.pipe(
+    switchMap(({ surfaces }) => {
+      const defaultSurface = surfaces.find(s => s.metadata.shortName === "pial") || surfaces[0]
+      if (!defaultSurface) return EMPTY
+      return of(
+        actions.selectVolumeById({
+          id: defaultSurface["@id"]
+        })
+      )
+    })
+  ))
+
+  onSurfaceSelected = createEffect(() => this.selectedSurfaceId$.pipe(
+    switchMap(id => this.onATPDebounceThreeSurferLayers$.pipe(
+      switchMap(({ surfaces }) => {
+        if (surfaces.length === 0) return EMPTY
+  
+        const layers: ThreeSurferCustomLayer[] = []
+        /**
+         * select the pial or first one by default
+         */
+        const selectedSrc = surfaces.find(s => s["@id"] === id)
+  
+        if (!(selectedSrc.data?.url_map)) {
+          return throwError(`Expecting surfaces[0].data.url_map to be defined, but is not.`)
+        }
+  
+        for (const key in selectedSrc.data.url_map) {
+          layers.push({
+            clType: 'baselayer/threesurfer',
+            id: `${selectedSrc["@id"]}-${key}`,
+            name: `${selectedSrc["@id"]}-${key}`,
+            laterality: key as 'left' | 'right',
+            source: selectedSrc.data.url_map[key]
+          })
+        }
+        return of(...[
+          ...layers.map(customLayer => 
+            atlasAppearance.actions.addCustomLayer({
+              customLayer
+            })
+          )
+        ])
+      })
+    ))
+  ))
+
+  onATPDebounceAddBaseLayers$ = createEffect(() => this.onATPDebounceThreeSurferLayers$.pipe(
+    switchMap(({ labels }) => {
+      const labelMaps: ThreeSurferCustomLabelLayer[] = []
+      for (const label of labels) {
+        labelMaps.push({
+          clType: 'baselayer/threesurfer-label',
+          id: `${label["@id"]}-${label.metadata.shortName}`,
+          laterality: label.metadata.shortName as 'left' | 'right',
+          source: label.data.url
+        })
+      }
+      return of(
+        ...labelMaps.map(customLayer => 
+          atlasAppearance.actions.addCustomLayer({
+            customLayer
+          })  
+        )
+      )
+    })
+  ))
+
+  constructor(
+    private store: Store,
+    private sapi: SAPI,
+  ){
+
+  }
+}
\ No newline at end of file
diff --git a/src/viewerModule/threeSurfer/store/index.ts b/src/viewerModule/threeSurfer/store/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e84f687f54017d80038471ae103fa2f4131e3626
--- /dev/null
+++ b/src/viewerModule/threeSurfer/store/index.ts
@@ -0,0 +1,5 @@
+export { reducer, Store, defaultStore } from "./store"
+export * as actions from "./actions"
+export * as selectors from "./selectors"
+export { nameSpace } from "./const"
+export { ThreeSurferEffects } from "./effects"
\ No newline at end of file
diff --git a/src/viewerModule/threeSurfer/store/selectors.ts b/src/viewerModule/threeSurfer/store/selectors.ts
new file mode 100644
index 0000000000000000000000000000000000000000..82c32604a57a26efac7881f9345fdb6c38cf9c2c
--- /dev/null
+++ b/src/viewerModule/threeSurfer/store/selectors.ts
@@ -0,0 +1,10 @@
+import { createSelector } from "@ngrx/store"
+import { nameSpace } from "./const"
+import { Store } from "./store"
+
+const selectStore = state => state[nameSpace] as Store
+
+export const getSelectedVolumeId = createSelector(
+  selectStore,
+  ({ selectedVolumeId }) => selectedVolumeId
+)
diff --git a/src/viewerModule/threeSurfer/store/store.ts b/src/viewerModule/threeSurfer/store/store.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b830d4278e88bfea95ca9a5f0697fb68f7db2171
--- /dev/null
+++ b/src/viewerModule/threeSurfer/store/store.ts
@@ -0,0 +1,24 @@
+import { createReducer, on } from "@ngrx/store";
+import * as actions from "./actions"
+
+export type Store = {
+  selectedVolumeId: string
+}
+
+
+export const defaultStore: Store = {
+  selectedVolumeId: null
+}
+
+export const reducer = createReducer(
+  defaultStore,
+  on(
+    actions.selectVolumeById,
+    (state, { id }) => {
+      return {
+        ...state,
+        selectedVolumeId: id
+      }
+    }
+  )
+)
diff --git a/src/viewerModule/threeSurfer/threeSurferGlue/threeSurfer.component.ts b/src/viewerModule/threeSurfer/threeSurferGlue/threeSurfer.component.ts
index a00b376604e92ed7e4328b4289c227813f475836..f04ad6cc1b0a5b85875c6bc247150b737eb5605c 100644
--- a/src/viewerModule/threeSurfer/threeSurferGlue/threeSurfer.component.ts
+++ b/src/viewerModule/threeSurfer/threeSurferGlue/threeSurfer.component.ts
@@ -1,21 +1,21 @@
-import { Component, Input, Output, EventEmitter, ElementRef, OnChanges, OnDestroy, AfterViewInit, Inject, Optional } from "@angular/core";
+import { Component, Output, EventEmitter, ElementRef, OnDestroy, AfterViewInit, Inject, Optional, ChangeDetectionStrategy } from "@angular/core";
 import { EnumViewerEvt, IViewer, TViewerEvent } from "src/viewerModule/viewer.interface";
-import { TThreeSurferConfig, TThreeSurferMode } from "../types";
-import { parseContext } from "../util";
-import { retry, flattenRegions } from 'common/util'
-import { Observable, Subject } from "rxjs";
-import { debounceTime, filter, switchMap } from "rxjs/operators";
+import { combineLatest, Observable, Subject } from "rxjs";
+import { debounceTime, distinctUntilChanged, filter, map, shareReplay } from "rxjs/operators";
 import { ComponentStore } from "src/viewerModule/componentStore";
 import { select, Store } from "@ngrx/store";
-import { viewerStateChangeNavigation, viewerStateSetSelectedRegions } from "src/services/state/viewerState/actions";
-import { viewerStateSelectorNavigation } from "src/services/state/viewerState/selectors";
 import { ClickInterceptor, CLICK_INTERCEPTOR_INJECTOR } from "src/util";
-import { REGION_OF_INTEREST } from "src/util/interfaces";
 import { MatSnackBar } from "@angular/material/snack-bar";
 import { CONST } from 'common/constants'
-import { API_SERVICE_SET_VIEWER_HANDLE_TOKEN, TSetViewerHandle } from "src/atlasViewer/atlasViewer.apiService.service";
-import { getUuid, switchMapWaitFor } from "src/util/fn";
+import { getUuid } from "src/util/fn";
 import { AUTO_ROTATE, TInteralStatePayload, ViewerInternalStateSvc } from "src/viewerModule/viewerInternalState.service";
+import { atlasAppearance, atlasSelection } from "src/state";
+import { ThreeSurferCustomLabelLayer, ThreeSurferCustomLayer, ColorMapCustomLayer } from "src/state/atlasAppearance/const";
+import { SapiRegionModel, SapiVolumeModel } from "src/atlasComponents/sapi";
+import { getRegionLabelIndex } from "src/viewerModule/nehuba/config.service";
+import { arrayEqual } from "src/util/array";
+import { ThreeSurferEffects } from "../store/effects";
+import { selectors, actions  } from "../store"
 
 const viewerType = 'ThreeSurfer'
 type TInternalState = {
@@ -28,10 +28,10 @@ type TInternalState = {
   hemisphere: 'left' | 'right' | 'both'
 }
 const pZoomFactor = 5e3
-const preferredFsMode = 'pial'
 
 type THandlingCustomEv = {
-  regions: ({ name?: string, error?: string })[]
+  regions: SapiRegionModel[]
+  error?: string
   evMesh?: {
     faceIndex: number
     verticesIndicies: number[]
@@ -39,20 +39,31 @@ type THandlingCustomEv = {
 }
 
 type TCameraOrientation = {
-  perspectiveOrientation: [number, number, number, number]
+  perspectiveOrientation: number[]
   perspectiveZoom: number
 }
 
-const threshold = 1e-3
-
-function getHemisphereKey(region: { name: string }){
-  return /left/.test(region.name)
-    ? 'left'
-    : /right/.test(region.name)
-      ? 'right'
-      : null
+type TThreeGeometry = {
+  visible: boolean
+}
+type GiiInstance = unknown
+type TThreeSurfer = {
+  loadMesh: (url: string) => Promise<TThreeGeometry>
+  unloadMesh: (geom: TThreeGeometry) => void
+  redraw: (geom: TThreeGeometry) => void
+  applyColorMap: (geom: TThreeGeometry, idxMap?: number[], custom?: { usePreset?: any, custom?: Map<number, number[]> }) => void
+  loadColormap: (url: string) => Promise<GiiInstance>
+  setupAnimation: () => void
+  dispose: () => void
+  control: any
+  camera: any
+  customColormap: WeakMap<TThreeGeometry, any>
 }
 
+type LateralityRecord<T> = Record<string, T>
+
+const threshold = 1e-3
+
 function cameraNavsAreSimilar(c1: TCameraOrientation, c2: TCameraOrientation){
   if (c1 === c2) return true
   if (!!c1 && !!c2) return true
@@ -75,46 +86,120 @@ function cameraNavsAreSimilar(c1: TCameraOrientation, c2: TCameraOrientation){
   styleUrls: [
     './threeSurfer.style.css'
   ],
-  providers: [ ComponentStore ]
+  providers: [ ComponentStore ],
+  changeDetection: ChangeDetectionStrategy.OnPush
 })
 
-export class ThreeSurferGlueCmp implements IViewer<'threeSurfer'>, OnChanges, AfterViewInit, OnDestroy {
+export class ThreeSurferGlueCmp implements IViewer<'threeSurfer'>, AfterViewInit, OnDestroy {
 
-  private loanedColorMap = new WeakSet()
-
-  @Input()
-  selectedTemplate: any
-
-  @Input()
-  selectedParcellation: any
   
   @Output()
   viewerEvent = new EventEmitter<TViewerEvent<'threeSurfer'>>()
 
   private domEl: HTMLElement
-  private config: TThreeSurferConfig
-  public modes: TThreeSurferMode[] = []
-  public selectedMode: string
-
   private mainStoreCameraNav: TCameraOrientation = null
   private localCameraNav: TCameraOrientation = null
 
-  public allKeys: {name: string, checked: boolean}[] = []
+  public lateralityMeshRecord: LateralityRecord<{
+    visible: boolean
+    meshLayer: ThreeSurferCustomLayer
+    mesh: TThreeGeometry
+  }> = {}
+
+  public latLblIdxRecord: LateralityRecord<{
+    indexLayer: ThreeSurferCustomLabelLayer
+    labelIndices: number[]
+  }> = {}
   private internalStateNext: (arg: TInteralStatePayload<TInternalState>) => void
 
-  private regionMap: Map<string, Map<number, any>> = new Map()
-  private mouseoverRegions = []
+  private mouseoverRegions: SapiRegionModel[] = []
   
-  private raf: number
+  private selectedRegions$ = this.store$.pipe(
+    select(atlasSelection.selectors.selectedRegions)
+  )
+
+  private customLayers$ = this.store$.pipe(
+    select(atlasAppearance.selectors.customLayers),
+    distinctUntilChanged(arrayEqual((o, n) => o.id === n.id)),
+    shareReplay(1)
+  )
+  public meshLayers$: Observable<ThreeSurferCustomLayer[]> = this.customLayers$.pipe(
+    map(layers => layers.filter(l => l.clType === "baselayer/threesurfer") as ThreeSurferCustomLayer[]),
+    distinctUntilChanged(arrayEqual((o, n) => o.id === n.id)),
+  )
+
+  private vertexIndexLayers$: Observable<ThreeSurferCustomLabelLayer[]> = this.customLayers$.pipe(
+    map(layers => layers.filter(l => l.clType === "baselayer/threesurfer-label") as ThreeSurferCustomLabelLayer[]),
+    distinctUntilChanged(arrayEqual((o, n) => o.id === n.id)),
+  )
+
+  /**
+   * maps laterality to label index to sapi region
+   */
+  private latLblIdxToRegionRecord: LateralityRecord<Record<number, SapiRegionModel>> = {}
+  private latLblIdxToRegionRecord$: Observable<LateralityRecord<Record<number, SapiRegionModel>>> = combineLatest([
+    this.store$.pipe(
+      atlasSelection.fromRootStore.distinctATP()
+    ),
+    this.store$.pipe(
+      select(atlasSelection.selectors.selectedParcAllRegions),
+    )
+  ]).pipe(
+    map(([ { atlas, parcellation, template },  regions]) => {
+      const returnObj = {
+        'left': {} as Record<number, SapiRegionModel>,
+        'right': {} as Record<number, SapiRegionModel>
+      }
+      
+      for (const region of regions) {
+        const idx = getRegionLabelIndex(atlas, template, parcellation, region)
+        if (idx) {
+          let key : 'left' | 'right'
+          if ( /left/i.test(region.name) ) key = 'left'
+          if ( /right/i.test(region.name) ) key = 'right'
+          if (!key) {
+            /**
+             * TODO
+             * there are ... more regions than expected, which has label index without laterality
+             */
+            continue
+          }
+          returnObj[key][idx] = region
+        }
+      }
+      return returnObj
+    })
+  )
+
+  /**
+   * colormap in use (both base & custom)
+   */
+
+  private colormapInUse: ColorMapCustomLayer
+  private colormaps$: Observable<ColorMapCustomLayer[]> = this.customLayers$.pipe(
+    map(layers => layers.filter(l => l.clType === "baselayer/colormap" || l.clType === "customlayer/colormap") as ColorMapCustomLayer[]),
+  )
+
+  /**
+   * show delination map
+   */
+  private showDelineation: boolean = true
+
+  public threeSurferSurfaceLayers$ = this.effect.onATPDebounceThreeSurferLayers$.pipe(
+    map(({ surfaces }) => surfaces)
+  )
+  public selectedSurfaceLayerId$ = this.store$.pipe(
+    select(selectors.getSelectedVolumeId)
+  )
+
   constructor(
+    private effect: ThreeSurferEffects,
     private el: ElementRef,
-    private store$: Store<any>,
+    private store$: Store,
     private navStateStoreRelay: ComponentStore<{ perspectiveOrientation: [number, number, number, number], perspectiveZoom: number }>,
     private snackbar: MatSnackBar,
     @Optional() intViewerStateSvc: ViewerInternalStateSvc,
-    @Optional() @Inject(REGION_OF_INTEREST) private roi$: Observable<any>,
     @Optional() @Inject(CLICK_INTERCEPTOR_INJECTOR) clickInterceptor: ClickInterceptor,
-    @Optional() @Inject(API_SERVICE_SET_VIEWER_HANDLE_TOKEN) setViewerHandle: TSetViewerHandle,
   ){
     if (intViewerStateSvc) {
       const {
@@ -144,108 +229,6 @@ export class ThreeSurferGlueCmp implements IViewer<'threeSurfer'>, OnChanges, Af
       this.onDestroyCb.push(() => done())
     }
 
-    // set viewer handle
-    // the API won't be 100% compatible with ngviewer
-    if (setViewerHandle) {
-      const nyi = () => {
-        throw new Error(`Not yet implemented`)
-      }
-      setViewerHandle({
-        add3DLandmarks: nyi,
-        loadLayer: nyi,
-        applyLayersColourMap: (map: Map<string, Map<number, { red: number, green: number, blue: number }>>) => {
-          if (this.loanedColorMap.has(map)) {
-            this.externalHemisphLblColorMap = null
-          } else {
-
-            const applyCm = new Map()
-            for (const [hem, m] of map.entries()) {
-              const nMap = new Map()
-              applyCm.set(hem, nMap)
-              for (const [lbl, vals] of m.entries()) {
-                const { red, green, blue } = vals
-                nMap.set(lbl, [red/255, green/255, blue/255])
-              }
-            }
-            this.externalHemisphLblColorMap = applyCm
-          }
-          this.applyColorMap()
-        },
-        getLayersSegmentColourMap: () => {
-          const map = this.getColormapCopy()
-          const outmap = new Map<string, Map<number, { red: number, green: number, blue: number }>>()
-          for (const [ hem, m ] of map.entries()) {
-            const nMap = new Map<number, {red: number, green: number, blue: number}>()
-            outmap.set(hem, nMap)
-            for (const [ lbl, vals ] of m.entries()) {
-              nMap.set(lbl, {
-                red: vals[0] * 255,
-                green: vals[1] * 255,
-                blue: vals[2] * 255,
-              })
-            }
-          }
-          this.loanedColorMap.add(outmap)
-          return outmap
-        },
-        getNgHash: nyi,
-        hideAllSegments: nyi,
-        hideSegment: nyi,
-        mouseEvent: null, 
-        mouseOverNehuba: null,
-        mouseOverNehubaLayers: null,
-        mouseOverNehubaUI: null,
-        moveToNavigationLoc: null,
-        moveToNavigationOri: null,
-        remove3DLandmarks: null,
-        removeLayer: null,
-        setLayerVisibility: null,
-        setNavigationLoc: null,
-        setNavigationOri: null,
-        showAllSegments: nyi,
-        showSegment: nyi,
-      })
-    }
-
-    this.onDestroyCb.push(
-      () => setViewerHandle(null)
-    )
-
-    if (this.roi$) {
-      const sub = this.roi$.pipe(
-        switchMap(switchMapWaitFor({
-          condition: () => this.colormapLoaded
-        }))
-      ).subscribe(r => {
-        try {
-          if (!r) throw new Error(`No region selected.`)
-          const cmap = this.getColormapCopy()
-          const hemisphere = getHemisphereKey(r)
-          if (!hemisphere) {
-            this.snackbar.open(CONST.CANNOT_DECIPHER_HEMISPHERE, 'Dismiss', {
-              duration: 3000
-            })
-            throw new Error(CONST.CANNOT_DECIPHER_HEMISPHERE)
-          }
-          for (const [ hem, m ] of cmap.entries()) {
-            for (const lbl of m.keys()) {
-              if (hem !== hemisphere || lbl !== r.labelIndex) {
-                m.set(lbl, [1, 1, 1])
-              }
-            }
-          }
-          this.internalHemisphLblColorMap = cmap
-        } catch (e) {
-          this.internalHemisphLblColorMap = null
-        }
-
-        this.applyColorMap()
-      })
-      this.onDestroyCb.push(
-        () => sub.unsubscribe()
-      )
-    }
-
     /**
      * intercept click and act
      */
@@ -264,10 +247,10 @@ export class ThreeSurferGlueCmp implements IViewer<'threeSurfer'>, OnChanges, Af
           })
           return true
         }
+
+        const regions = this.mouseoverRegions.slice(0, 1) as any[]
         this.store$.dispatch(
-          viewerStateSetSelectedRegions({
-            selectRegions: this.mouseoverRegions
-          })
+          atlasSelection.actions.setSelectedRegions({ regions })
         )
         return true
       }
@@ -294,11 +277,21 @@ export class ThreeSurferGlueCmp implements IViewer<'threeSurfer'>, OnChanges, Af
       const t = new THREE.Vector3()
       const s = new THREE.Vector3()
 
+      /**
+       * ThreeJS interpretes the scene differently to neuroglancer in subtle ways. 
+       * At [0, 0, 0, 1] decomposed camera quaternion, for example,
+       * - ThreeJS: view from superior -> inferior, anterior as top, right hemisphere as right
+       * - NG: view from from inferior -> superior, posterior as top, left hemisphere as right
+       * 
+       * multiplying the exchange factor [-1, 0, 0, 0] converts ThreeJS convention to NG convention
+       */
       const cameraM = this.tsRef.camera.matrix
       cameraM.decompose(t, q, s)
+      const exchangeFactor = new THREE.Quaternion(-1, 0, 0, 0)
+
       try {
         this.navStateStoreRelay.setState({
-          perspectiveOrientation: q.toArray(),
+          perspectiveOrientation: q.multiply(exchangeFactor).toArray(),
           perspectiveZoom: t.length()
         })
       } catch (_e) {
@@ -315,11 +308,11 @@ export class ThreeSurferGlueCmp implements IViewer<'threeSurfer'>, OnChanges, Af
      */
     const navStateSub = this.navStateStoreRelay.select(s => s).subscribe(v => {
       this.store$.dispatch(
-        viewerStateChangeNavigation({
+        atlasSelection.actions.setNavigation({
           navigation: {
             position: [0, 0, 0],
             orientation: [0, 0, 0, 1],
-            zoom: 1,
+            zoom: 1e6,
             perspectiveOrientation: v.perspectiveOrientation,
             perspectiveZoom: v.perspectiveZoom * pZoomFactor
           }
@@ -335,7 +328,8 @@ export class ThreeSurferGlueCmp implements IViewer<'threeSurfer'>, OnChanges, Af
      * subscribe to main store and negotiate with relay to set camera
      */
     const navSub = this.store$.pipe(
-      select(viewerStateSelectorNavigation)
+      select(atlasSelection.selectors.navigation),
+      filter(v => !!v),
     ).subscribe(nav => {
       const { perspectiveOrientation, perspectiveZoom } = nav
       this.mainStoreCameraNav = {
@@ -349,6 +343,18 @@ export class ThreeSurferGlueCmp implements IViewer<'threeSurfer'>, OnChanges, Af
         
         const cameraQuat = new THREE.Quaternion(...this.mainStoreCameraNav.perspectiveOrientation)
         const cameraPos = new THREE.Vector3(0, 0, this.mainStoreCameraNav.perspectiveZoom / pZoomFactor)
+        
+        /**
+         * ThreeJS interpretes the scene differently to neuroglancer in subtle ways. 
+         * At [0, 0, 0, 1] decomposed camera quaternion, for example,
+         * - ThreeJS: view from superior -> inferior, anterior as top, right hemisphere as right
+         * - NG: view from from inferior -> superior, posterior as top, left hemisphere as right
+         * 
+         * multiplying the exchange factor [-1, 0, 0, 0] converts ThreeJS convention to NG convention
+         */
+        const exchangeFactor = new THREE.Quaternion(-1, 0, 0, 0)
+        cameraQuat.multiply(exchangeFactor)
+
         cameraPos.applyQuaternion(cameraQuat)
         this.toTsRef(tsRef => {
           tsRef.camera.position.copy(cameraPos)
@@ -362,23 +368,9 @@ export class ThreeSurferGlueCmp implements IViewer<'threeSurfer'>, OnChanges, Af
     )
   }
 
-  tsRef: any
-  loadedMeshes: {
-    threeSurfer: any
-    mesh: string
-    colormap: string
-    hemisphere: string
-    vIdxArr: number[]
-  }[] = []
-  private hemisphLblColorMap: Map<string, Map<number, [number, number, number]>> = new Map()
-  private internalHemisphLblColorMap: Map<string, Map<number, [number, number, number]>>
-  private externalHemisphLblColorMap: Map<string, Map<number, [number, number, number]>>
-  
-  get activeColorMap() {
-    if (this.externalHemisphLblColorMap) return this.externalHemisphLblColorMap
-    if (this.internalHemisphLblColorMap) return this.internalHemisphLblColorMap
-    return this.hemisphLblColorMap
-  }
+  private tsRef: TThreeSurfer
+  private selectedRegions: SapiRegionModel[] = []
+
   private relayStoreLock: () => void = null
   private tsRefInitCb: ((tsRef: any) => void)[] = []
   private toTsRef(callback: (tsRef: any) => void) {
@@ -389,84 +381,47 @@ export class ThreeSurferGlueCmp implements IViewer<'threeSurfer'>, OnChanges, Af
     this.tsRefInitCb.push(callback)
   }
 
-  private unloadAllMeshes() {
-    this.allKeys = []
-    while(this.loadedMeshes.length > 0) {
-      const m = this.loadedMeshes.pop()
-      this.tsRef.unloadMesh(m.threeSurfer)
-    }
-    this.hemisphLblColorMap.clear()
-    this.colormapLoaded = false
-  }
+  private async loadMeshes(layers: ThreeSurferCustomLayer[]) {
+    if (!this.tsRef) throw new Error(`loadMeshes error: this.tsRef is not defined!!`)
 
-  public async loadMode(mode: TThreeSurferMode) {
-    
-    this.unloadAllMeshes()
-
-    this.selectedMode = mode.name
-    const { meshes } = mode
-    await retry(async () => {
-      for (const singleMesh of meshes) {
-        const { hemisphere } = singleMesh
-        if (!this.regionMap.has(hemisphere)) throw new Error(`regionmap does not have hemisphere defined!`)
-      }
-    }, {
-      timeout: 32,
-      retries: 10
-    })
-    for (const singleMesh of meshes) {
-      const { mesh, colormap, hemisphere } = singleMesh
-      this.allKeys.push({name: hemisphere, checked: true})
-
-      const tsM = await this.tsRef.loadMesh(
-        parseContext(mesh, [this.config['@context']])
-      )
-
-      if (!this.regionMap.has(hemisphere)) continue
-      const rMap = this.regionMap.get(hemisphere)
-      const applyCM = new Map()
-      for (const [ lblIdx, region ] of rMap.entries()) {
-        applyCM.set(lblIdx, (region.rgb || [200, 200, 200]).map(v => v/255))
+    /**
+     * remove the layers... 
+     */
+    for (const layer of layers) {
+      if (!!this.lateralityMeshRecord[layer.laterality]) {
+        this.tsRef.unloadMesh(this.lateralityMeshRecord[layer.laterality].mesh)
       }
+    }
 
-      const tsC = await this.tsRef.loadColormap(
-        parseContext(colormap, [this.config['@context']])
-      )
-      
-      let colorIdx = tsC[0].getData()
-      if (tsC[0].attributes.DataType === 'NIFTI_TYPE_INT16') {
-        colorIdx = (window as any).ThreeSurfer.GiftiBase.castF32UInt16(colorIdx)
+    for (const layer of layers) {
+      const threeMesh = await this.tsRef.loadMesh(layer.source)
+      this.lateralityMeshRecord[layer.laterality] = {
+        visible: true,
+        meshLayer: layer,
+        mesh: threeMesh
       }
-
-      this.loadedMeshes.push({
-        threeSurfer: tsM,
-        colormap,
-        mesh,
-        hemisphere,
-        vIdxArr: colorIdx
-      })
-
-      this.hemisphLblColorMap.set(hemisphere, applyCM)
     }
-    this.colormapLoaded = true
-    this.applyColorMap()
+    this.applyColor()
   }
 
-  private colormapLoaded = false
+  private async loadVertexIndexMap(layers: ThreeSurferCustomLabelLayer[]) {
+    if (!this.tsRef) throw new Error(`loadVertexIndexMap error: this.tsRef is not defined!!`)
+    for (const layer of layers) {
+      const giiInstance = await this.tsRef.loadColormap(layer.source)
 
-  private getColormapCopy(): Map<string, Map<number, [number, number, number]>> {
-    const outmap = new Map()
-    for (const [key, value] of this.hemisphLblColorMap.entries()) {
-      outmap.set(key, new Map(value))
+      let labelIndices: number[] = giiInstance[0].getData()
+      if (giiInstance[0].attributes.DataType === 'NIFTI_TYPE_INT16') {
+        labelIndices = (window as any).ThreeSurfer.GiftiBase.castF32UInt16(labelIndices)
+      }
+      this.latLblIdxRecord[layer.laterality] = {
+        indexLayer: layer,
+        labelIndices
+      }
     }
-    return outmap
+    this.applyColor()
   }
 
-  /**
-   * TODO perhaps debounce calls to applycolormap
-   * so that the colormap doesn't "flick"
-   */
-  private applyColorMap(){
+  private applyColor() {
     /**
      * on apply color map, reset mesh visibility
      * this issue is more difficult to solve than first anticiplated.
@@ -476,72 +431,36 @@ export class ThreeSurferGlueCmp implements IViewer<'threeSurfer'>, OnChanges, Af
      * 2/ hide hemisphere, select region, unhide hemisphere
      * 3/ select region, hide hemisphere, deselect region
      */
-    for (const key of this.allKeys) {
-      key.checked = true
-    }
-    for (const mesh of this.loadedMeshes) {
-      const { hemisphere, threeSurfer, vIdxArr } = mesh
-      const applyCM = this.activeColorMap.get(hemisphere)
-      this.tsRef.applyColorMap(threeSurfer, vIdxArr, 
-        {
-          custom: applyCM
-        }
-      )
-    }
-  }
+    if (!this.colormapInUse) return
+    if (!this.tsRef) return
+    
+    const isBaseCM = this.colormapInUse?.clType === "baselayer/colormap"
 
-  async ngOnChanges(){
-    if (this.tsRef) {
-      this.ngOnDestroy()
-      this.ngAfterViewInit()
-    }
-    if (this.selectedTemplate) {
+    for (const laterality in this.lateralityMeshRecord) {
+      const { mesh } = this.lateralityMeshRecord[laterality]
+      if (!this.latLblIdxRecord[laterality]) continue
+      const { labelIndices } = this.latLblIdxRecord[laterality]
 
-      /**
-       * wait until threesurfer is defined in window
-       */
-      await retry(async () => {
-        if (typeof (window as any).ThreeSurfer === 'undefined') throw new Error('ThreeSurfer not yet defined')
-      }, {
-        timeout: 160,
-        retries: 10,
-      })
-      
-      this.config = this.selectedTemplate['three-surfer']
-      // somehow curv ... cannot be parsed properly by gifti parser... something about points missing
-      this.modes = this.config.modes.filter(m => !/curv/.test(m.name))
-      if (!this.tsRef) {
-        this.tsRef = new (window as any).ThreeSurfer(this.domEl, {highlightHovered: true})
-        this.onDestroyCb.push(
-          () => {
-            this.tsRef.dispose()
-            this.tsRef = null
-          }
-        )
-        this.tsRef.control.enablePan = false
-        while (this.tsRefInitCb.length > 0) this.tsRefInitCb.pop()(this.tsRef)
+      const lblIdxToRegionRecord = this.latLblIdxToRegionRecord[laterality]
+      if (!lblIdxToRegionRecord) {
+        this.tsRef.applyColorMap(mesh, labelIndices)
+        continue
       }
-
-      const flattenedRegions = flattenRegions(this.selectedParcellation.regions)
-      for (const region of flattenedRegions) {
-        if (region.labelIndex) {
-          const hemisphere = getHemisphereKey(region)
-          if (!hemisphere) throw new Error(`region ${region.name} does not have hemisphere defined`)
-          if (!this.regionMap.has(hemisphere)) {
-            this.regionMap.set(hemisphere, new Map())
-          }
-          const rMap = this.regionMap.get(hemisphere)
-          rMap.set(region.labelIndex, region)
+      const map = new Map<number, number[]>()
+      for (const lblIdx in lblIdxToRegionRecord) {
+        const region = lblIdxToRegionRecord[lblIdx]
+        let color: number[]
+        if (!this.showDelineation) {
+          color = [1,1,1]
+        } else if (isBaseCM && this.selectedRegions.length > 0 && !this.selectedRegions.includes(region)) {
+          color = [1,1,1]
+        } else {
+          color = (this.colormapInUse.colormap.get(region) || [255, 255, 255]).map(v => v/255)
         }
+        map.set(Number(lblIdx), color)
       }
-      
-      // load preferredFsMode or mode0 by default
-      const loadMode = this.config.modes.find(m => m.name === preferredFsMode) || this.config.modes[0]
-      this.loadMode(loadMode)
-
-      this.viewerEvent.emit({
-        type: EnumViewerEvt.VIEWERLOADED,
-        data: true
+      this.tsRef.applyColorMap(mesh, labelIndices, {
+        custom: map
       })
     }
   }
@@ -561,59 +480,66 @@ export class ThreeSurferGlueCmp implements IViewer<'threeSurfer'>, OnChanges, Af
       return this.handleMouseoverEvent(custEv)
     }
 
-    const evGeom = detail.mesh.geometry
-    const evVertIdx = detail.mesh.verticesIdicies
-    const found = this.loadedMeshes.find(({ threeSurfer }) => threeSurfer === evGeom)
-    if (!found) return this.handleMouseoverEvent(custEv)
-
-    /**
-     * check if the mesh is toggled off
-     * if so, do not proceed
-     */
-    const checkKey = this.allKeys.find(key => key.name === found.hemisphere)
-    if (checkKey && !checkKey.checked) return
-
-    const { hemisphere: key, vIdxArr } = found
-
-    if (!key || !evVertIdx) {
-      return this.handleMouseoverEvent(custEv)
-    }
+    const {
+      geometry: evGeometry,
+      // typo in three-surfer
+      verticesIdicies: evVerticesIndicies,
+    } = detail.mesh as { geometry: TThreeGeometry, verticesIdicies: number[] }
 
-    const labelIdxSet = new Set<number>()
-    
-    for (const vIdx of evVertIdx) {
-      labelIdxSet.add(
-        vIdxArr[vIdx]
-      )
-    }
-    if (labelIdxSet.size === 0) {
-      return this.handleMouseoverEvent(custEv)
-    }
+    for (const laterality in this.lateralityMeshRecord) {
+      const meshRecord = this.lateralityMeshRecord[laterality]
+      if (meshRecord.mesh !== evGeometry) {
+        continue
+      }
+      /**
+       * if either labelindex record or colormap record is undefined for this laterality, emit empty event
+       */
+      if (!this.latLblIdxRecord[laterality] || !this.latLblIdxToRegionRecord[laterality]) {
+        return this.handleMouseoverEvent(custEv)
+      }
+      const labelIndexRecord = this.latLblIdxRecord[laterality]
+      const regionRecord = this.latLblIdxToRegionRecord[laterality]
 
-    const hemisphereMap = this.regionMap.get(key)
+      /**
+       * check if the mesh is toggled off
+       * if so, do not proceed
+       */
+      if (!meshRecord.visible) {
+        return
+      }
 
-    if (!hemisphereMap) {
-      custEv.regions = Array.from(labelIdxSet).map(v => {
-        return {
-          error: `unknown#${v}`
+      /**
+       * translate vertex indices to label indicies via set, to remove duplicates
+       */
+      const labelIndexSet = new Set<number>()
+      for (const idx of evVerticesIndicies){
+        const labelOfInterest = labelIndexRecord.labelIndices[idx]
+        if (!labelOfInterest) {
+          continue
         }
-      })
-      return this.handleMouseoverEvent(custEv)
-    }
+        labelIndexSet.add(labelOfInterest)
+      }
 
-    custEv.regions =  Array.from(labelIdxSet)
-      .map(lblIdx => {
-        const ontoR = hemisphereMap.get(lblIdx)
-        if (ontoR) {
-          return ontoR
-        } else {
-          return {
-            error: `unkonwn#${lblIdx}`
-          }
+      /**
+       * decode label index to region
+       */
+      if (labelIndexSet.size === 0) {
+        return this.handleMouseoverEvent(custEv)
+      }
+      for (const labelIndex of Array.from(labelIndexSet)) {
+        if (!regionRecord[labelIndex]) {
+          custEv.error = `${custEv.error || ''} Cannot decode label index ${labelIndex}`
+          continue
         }
-      })
-    return this.handleMouseoverEvent(custEv)
+        const region = regionRecord[labelIndex]
+        custEv.regions.push(region)
+      }
 
+      /**
+       * return handle event
+       */
+      return this.handleMouseoverEvent(custEv)
+    }
   }
 
   private cameraEv$ = new Subject<{ position: { x: number, y: number, z: number }, zoom: number }>()
@@ -624,7 +550,7 @@ export class ThreeSurferGlueCmp implements IViewer<'threeSurfer'>, OnChanges, Af
         "@type": 'TViewerInternalStateEmitterEvent',
         viewerType,
         payload: {
-          mode: this.selectedMode,
+          mode: '',
           camera: detail.position,
           hemisphere: 'both'
         }
@@ -633,7 +559,7 @@ export class ThreeSurferGlueCmp implements IViewer<'threeSurfer'>, OnChanges, Af
     this.cameraEv$.next(detail)
   }
 
-  ngAfterViewInit(){
+  ngAfterViewInit(): void{
     const customEvHandler = (ev: CustomEvent) => {
       const { type, data } = ev.detail
       if (type === 'mouseover') {
@@ -647,53 +573,117 @@ export class ThreeSurferGlueCmp implements IViewer<'threeSurfer'>, OnChanges, Af
     this.onDestroyCb.push(
       () => this.domEl.removeEventListener((window as any).ThreeSurfer.CUSTOM_EVENTNAME_UPDATED, customEvHandler)
     )
+    this.tsRef = new (window as any).ThreeSurfer(this.domEl, {highlightHovered: true})
+
+    this.onDestroyCb.push(
+      () => {
+        this.tsRef.dispose()
+        this.tsRef = null
+      }
+    )
+    this.tsRef.control.enablePan = false
+    while (this.tsRefInitCb.length > 0) {
+      const tsCb = this.tsRefInitCb.pop()
+      tsCb(this.tsRef)
+    }
+
+    const meshSub = this.meshLayers$.pipe(
+      distinctUntilChanged(),
+      debounceTime(16),
+    ).subscribe(layers => {
+      this.loadMeshes(layers)
+    })
+    const vertexIdxSub = this.vertexIndexLayers$.subscribe(layers => this.loadVertexIndexMap(layers))
+    const roiSelectedSub = this.selectedRegions$.subscribe(regions => {
+      this.selectedRegions = regions
+      this.applyColor()
+    })
+    const colormapSub = this.colormaps$.subscribe(cm => {
+      this.colormapInUse = cm[0] || null
+      this.applyColor()
+    })
+    const recordToRegionSub = this.latLblIdxToRegionRecord$.subscribe(val => this.latLblIdxToRegionRecord = val)
+    const hideDelineationSub = this.store$.pipe(
+      select(atlasAppearance.selectors.showDelineation)
+    ).subscribe(flag => {
+      this.showDelineation = flag
+      this.applyColor()
+      /**
+       * apply color resets mesh visibility
+       */
+      this.updateMeshVisibility()
+    })
+
+    this.onDestroyCb.push(() => {
+      meshSub.unsubscribe()
+      vertexIdxSub.unsubscribe()
+      roiSelectedSub.unsubscribe()
+      colormapSub.unsubscribe()
+      recordToRegionSub.unsubscribe()
+      hideDelineationSub.unsubscribe()
+    })
+
+    this.viewerEvent.emit({
+      type: EnumViewerEvt.VIEWERLOADED,
+      data: true
+    })
   }
 
   public mouseoverText: string
   private handleMouseoverEvent(ev: THandlingCustomEv){
-    const { regions: mouseover, evMesh } = ev
+    const { regions: mouseover, evMesh, error } = ev
     this.mouseoverRegions = mouseover
     this.viewerEvent.emit({
       type: EnumViewerEvt.VIEWER_CTX,
       data: {
         viewerType: 'threeSurfer',
         payload: {
-          fsversion: this.selectedMode,
+          fsversion: '',
           faceIndex: evMesh?.faceIndex,
           vertexIndices: evMesh?.verticesIndicies,
           position: [],
-          _mouseoverRegion: mouseover.filter(el => !el.error)
+          regions: mouseover,
+          error
         }
       }
     })
-    this.mouseoverText = mouseover.length === 0 ?
-      null :
-      mouseover.map(
-        el => el.name || el.error
-      ).join(' / ')
+    this.mouseoverText = ''
+    if (mouseover.length > 0) {
+      this.mouseoverText += mouseover.map(el => el.name).join(' / ')
+    }
+    if (error) {
+      this.mouseoverText += `::error: ${error}`
+    }
+    if (this.mouseoverText === '') this.mouseoverText = null
   }
 
-  public handleCheckBox(key: { name: string, checked: boolean }, flag: boolean){
-    const foundMesh = this.loadedMeshes.find(m => m.hemisphere === key.name)
-    if (!foundMesh) {
-      throw new Error(`Cannot find mesh with name: ${key.name}`)
-    }
-    const meshObj = this.tsRef.customColormap.get(foundMesh.threeSurfer)
-    if (!meshObj) {
-      throw new Error(`mesh obj not found!`)
+  public updateMeshVisibility(): void{
+
+    for (const key in this.lateralityMeshRecord) {
+
+      const latMeshRecord = this.lateralityMeshRecord[key]
+      if (!latMeshRecord) {
+        return
+      }
+      const meshObj = this.tsRef.customColormap.get(latMeshRecord.mesh)
+      if (!meshObj) {
+        throw new Error(`mesh obj not found!`)
+      }
+      meshObj.mesh.visible = latMeshRecord.visible
     }
-    meshObj.mesh.visible = flag
+  }
+
+  switchSurfaceLayer(layer: SapiVolumeModel): void{
+    this.store$.dispatch(
+      actions.selectVolumeById({
+        id: layer["@id"]
+      })
+    )
   }
 
   private onDestroyCb: (() => void) [] = []
 
-  ngOnDestroy() {
+  ngOnDestroy(): void {
     while (this.onDestroyCb.length > 0) this.onDestroyCb.pop()()
   }
-
-  toggleMode(){
-    const currIdx = this.modes.findIndex(m => m.name === this.selectedMode)
-    const newIdx = (currIdx + 1) % this.modes.length
-    this.loadMode(this.modes[newIdx])
-  }
 }
diff --git a/src/viewerModule/threeSurfer/threeSurferGlue/threeSurfer.template.html b/src/viewerModule/threeSurfer/threeSurferGlue/threeSurfer.template.html
index 31f59c7293be5be213c6634d42722f4cb852bf45..2a9703618ce87100f39bbd6470f88d4cd5bd5a57 100644
--- a/src/viewerModule/threeSurfer/threeSurferGlue/threeSurfer.template.html
+++ b/src/viewerModule/threeSurfer/threeSurferGlue/threeSurfer.template.html
@@ -1,5 +1,5 @@
 <span *ngIf="mouseoverText"
-  class="mouseover iv-custom-comp text">
+  class="mouseover sxplr-custom-cmp text">
   {{ mouseoverText }}
 </span>
 
@@ -7,8 +7,6 @@
 
   <!-- selector & configurator -->
   <button mat-icon-button
-    [iav-key-listener]="[{ type: 'keydown', key: 'q', target: 'document' }]"
-    (iav-key-event)="toggleMode()"
     color="primary"
     class="pe-all"
     [matMenuTriggerFor]="fsModeSelMenu">
@@ -20,30 +18,27 @@
 <!-- selector/configurator menu -->
 <mat-menu #fsModeSelMenu="matMenu">
 
-  <div class="iv-custom-comp text pl-2 m-2">
-    <mat-checkbox *ngFor="let key of allKeys"
+  <div class="sxplr-custom-cmp text sxplr-pl-2 m-2">
+    <mat-checkbox *ngFor="let item of lateralityMeshRecord | keyvalue"
       class="d-block"
       iav-stop="click"
-      (ngModelChange)="handleCheckBox(key, $event)"
-      [(ngModel)]="key.checked">
-      {{ key.name }}
+      (change)="updateMeshVisibility()"
+      [(ngModel)]="item.value.visible">
+      {{ item.key }}
     </mat-checkbox>
   </div>
   <mat-divider></mat-divider>
-  <button *ngFor="let mode of modes"
+
+  <button *ngFor="let surfaceLayer of threeSurferSurfaceLayers$ | async"
     mat-menu-item
-    (click)="loadMode(mode)"
+    (click)="switchSurfaceLayer(surfaceLayer)"
     color="primary">
     <mat-icon
       fontSet="fas"
-      [fontIcon]="mode.name === selectedMode ? 'fa-circle' : 'fa-none'">
+      [fontIcon]="surfaceLayer['@id'] === (selectedSurfaceLayerId$ | async) ? 'fa-circle' : 'fa-none'">
     </mat-icon>
     <span>
-      {{ mode.name }}
+      {{ surfaceLayer.metadata.shortName }}
     </span>
-    <markdown-dom *ngIf="mode.name === selectedMode"
-      class="d-inline-block"
-      markdown="`[q]`">
-    </markdown-dom>
   </button>
 </mat-menu>
diff --git a/src/viewerModule/threeSurfer/types.ts b/src/viewerModule/threeSurfer/types.ts
index d6f987870b2f1f6a628f69a0742c8e4177b4b38a..9a15c5642d7c04e5c5057a412532eaabe51be8fe 100644
--- a/src/viewerModule/threeSurfer/types.ts
+++ b/src/viewerModule/threeSurfer/types.ts
@@ -1,4 +1,4 @@
-import { IContext } from './util'
+import { SapiRegionModel } from 'src/atlasComponents/sapi'
 
 export type TThreeSurferMesh = {
   colormap: string
@@ -6,20 +6,11 @@ export type TThreeSurferMesh = {
   hemisphere: 'left' | 'right'
 }
 
-export type TThreeSurferMode = {
-  name: string
-  meshes: TThreeSurferMesh[]
-}
-
-export type TThreeSurferConfig = {
-  ['@context']: IContext
-  modes: TThreeSurferMode[]
-}
-
 export type TThreeSurferContextInfo = {
   position: number[]
   faceIndex: number
   vertexIndices: number[]
   fsversion: string
-  _mouseoverRegion?: any[]
+  regions: SapiRegionModel[]
+  error?: string
 }
diff --git a/src/viewerModule/threeSurfer/util.ts b/src/viewerModule/threeSurfer/util.ts
index 34a0d85065cf7104d57faf08790dd943804f7b26..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644
--- a/src/viewerModule/threeSurfer/util.ts
+++ b/src/viewerModule/threeSurfer/util.ts
@@ -1,14 +0,0 @@
-export interface IContext {
-  [key: string]: string
-}
-
-export function parseContext(input: string, contexts: IContext[]){
-  let output = input
-  for (const context of contexts) {
-    for (const key in context) {
-      const re = new RegExp(`${key}:`, 'g')
-      output = output.replace(re, context[key])
-    }
-  }
-  return output
-}
diff --git a/src/viewerModule/viewer.interface.ts b/src/viewerModule/viewer.interface.ts
index 195ee958adbbeb7af67ff27000745d0cc8a67376..cbc85aca0e3e2620a0903b4e332dcb92386e8db0 100644
--- a/src/viewerModule/viewer.interface.ts
+++ b/src/viewerModule/viewer.interface.ts
@@ -1,4 +1,5 @@
 import { EventEmitter } from "@angular/core";
+import { RecursivePartial } from "./nehuba/config.service/type";
 import { TNehubaContextInfo } from "./nehuba/types";
 import { TThreeSurferContextInfo } from "./threeSurfer/types";
 
@@ -36,7 +37,7 @@ export interface IViewerCtx {
 
 export type TContextArg<K extends keyof IViewerCtx> = ({
   viewerType: K
-  payload: IViewerCtx[K]
+  payload: RecursivePartial<IViewerCtx[K]>
 })
 
 export enum EnumViewerEvt {
@@ -58,9 +59,6 @@ export type TViewerEvent<T extends keyof IViewerCtx> = TViewerEventViewerLoaded
 export type TSupportedViewers = keyof IViewerCtx
 
 export interface IViewer<K extends keyof IViewerCtx> {
-  
-  selectedTemplate: any
-  selectedParcellation: any
   viewerCtrlHandler?: IViewerCtrl
   viewerEvent: EventEmitter<TViewerEvent<K>>
 }
diff --git a/src/viewerModule/viewerCmp/viewerCmp.component.spec.ts b/src/viewerModule/viewerCmp/viewerCmp.component.spec.ts
index c67f26f8dcb0be058be3f01c97138a0371dfc4d6..10bb20122d19b73aadd5f7f55b6f065320d055e9 100644
--- a/src/viewerModule/viewerCmp/viewerCmp.component.spec.ts
+++ b/src/viewerModule/viewerCmp/viewerCmp.component.spec.ts
@@ -1,9 +1,6 @@
 import { TestBed } from "@angular/core/testing"
 import { MockStore, provideMockStore } from "@ngrx/store/testing"
-import { hot } from "jasmine-marbles"
-import { Observable, of, throwError } from "rxjs"
-import { viewerStateContextedSelectedRegionsSelector } from "src/services/state/viewerState/selectors"
-import { ROIFactory } from "./viewerCmp.component"
+
 
 describe('> viewerCmp.component.ts', () => {
   let mockStore: MockStore
@@ -15,111 +12,4 @@ describe('> viewerCmp.component.ts', () => {
     })
     mockStore = TestBed.inject(MockStore)
   })
-  describe('> ROIFactory', () => {
-    const mockDetail = {
-      foo: 'bar'
-    }
-    class MockPCSvc {
-      getRegionDetail(){
-        return of(mockDetail)
-      }
-    }
-    const pcsvc = new MockPCSvc()
-    let getRegionDetailSpy:  jasmine.Spy
-
-    beforeEach(() => {
-      getRegionDetailSpy = spyOn(pcsvc, 'getRegionDetail')
-      mockStore.overrideSelector(viewerStateContextedSelectedRegionsSelector, [])
-    })
-    
-    afterEach(() => {
-      getRegionDetailSpy.calls.reset()
-    })
-
-    describe('> if regoinselected is empty array', () => {
-      let returnVal: Observable<any>
-      beforeEach(() => {
-        getRegionDetailSpy.and.callThrough()
-        returnVal = ROIFactory(mockStore, pcsvc as any)
-      })
-      it('> returns null', () => {
-        expect(
-          returnVal
-        ).toBeObservable(hot('a', {
-          a: null
-        }))
-      })
-
-      it('> regionDetail not called', () => {
-        expect(getRegionDetailSpy).not.toHaveBeenCalled()
-      })
-    })
-
-    describe('> if regionselected is nonempty', () => {
-      const mockRegion = {
-        context: {
-          template: {
-            '@id': 'template-id'
-          },
-          parcellation: {
-            '@id': 'parcellation-id'
-          },
-          atlas: {
-            '@id': 'atlas-id'
-          }
-        },
-        ngId: 'foo-bar',
-        labelIndex: 123
-      }
-      const returnDetail = {
-        map: {
-          hello: 'world'
-        }
-      }
-      let returnVal: Observable<any>
-      beforeEach(() => {
-        getRegionDetailSpy.and.callFake(() => of(returnDetail))
-        mockStore.overrideSelector(viewerStateContextedSelectedRegionsSelector, [mockRegion])
-        returnVal = ROIFactory(mockStore, pcsvc as any)
-      })
-
-      // TODO check why marble is acting weird
-      // and that null is not emitted
-      it('> returns as expected', () => {
-        expect(returnVal).toBeObservable(
-          hot('(ab)', {
-            a: null,
-            b: {
-              ...mockRegion,
-              ...returnDetail
-            }
-          })
-        )
-        const { context } = mockRegion
-        expect(getRegionDetailSpy).toHaveBeenCalledWith(
-          context.atlas["@id"],
-          context.parcellation["@id"],
-          context.template["@id"],
-          mockRegion
-        )
-      })
-
-      it('> if getRegionDetail throws, at least return original region', () => {
-        getRegionDetailSpy.and.callFake(() => throwError('blabla'))
-        expect(returnVal).toBeObservable(
-          hot('(ab)', {
-            a: null,
-            b: mockRegion
-          })
-        )
-        const { context } = mockRegion
-        expect(getRegionDetailSpy).toHaveBeenCalledWith(
-          context.atlas["@id"],
-          context.parcellation["@id"],
-          context.template["@id"],
-          mockRegion
-        )
-      })
-    })
-  })
 })
diff --git a/src/viewerModule/viewerCmp/viewerCmp.component.ts b/src/viewerModule/viewerCmp/viewerCmp.component.ts
index 5afa255d58cb368762f8e40a4b14f3235a300cae..f0b32efea3c8233a648e0277bfd38ddeaebe2074 100644
--- a/src/viewerModule/viewerCmp/viewerCmp.component.ts
+++ b/src/viewerModule/viewerCmp/viewerCmp.component.ts
@@ -1,62 +1,20 @@
-import { Subject } from "rxjs"
-import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ComponentFactory, ComponentFactoryResolver, Inject, Injector, Input, OnDestroy, Optional, TemplateRef, ViewChild, ViewContainerRef } from "@angular/core";
+import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, TemplateRef, ViewChild, ViewContainerRef } from "@angular/core";
 import { select, Store } from "@ngrx/store";
-import { combineLatest, merge, NEVER, Observable, of, Subscription } from "rxjs";
-import {catchError, debounceTime, distinctUntilChanged, map, shareReplay, startWith, switchMap, mapTo, filter } from "rxjs/operators";
-import { viewerStateSetSelectedRegions } from "src/services/state/viewerState/actions";
-import {
-  viewerStateContextedSelectedRegionsSelector,
-  viewerStateGetSelectedAtlas,
-  viewerStateSelectedParcellationSelector,
-  viewerStateSelectedTemplateSelector,
-  viewerStateStandAloneVolumes,
-  viewerStateViewerModeSelector
-} from "src/services/state/viewerState/selectors"
+import { combineLatest, Observable, of, Subscription } from "rxjs";
+import { debounceTime, distinctUntilChanged, map, shareReplay, startWith, switchMap } from "rxjs/operators";
 import { CONST, ARIA_LABELS, QUICKTOUR_DESC } from 'common/constants'
-import { OVERWRITE_SHOW_DATASET_DIALOG_TOKEN, REGION_OF_INTEREST } from "src/util/interfaces";
 import { animate, state, style, transition, trigger } from "@angular/animations";
 import { IQuickTourData } from "src/ui/quickTour";
-import { PureContantService } from "src/util";
 import { EnumViewerEvt, TContextArg, TSupportedViewers, TViewerEvent } from "../viewer.interface";
-import { getGetRegionFromLabelIndexId, switchMapWaitFor } from "src/util/fn";
 import { ContextMenuService, TContextMenuReg } from "src/contextMenuModule";
-import { ComponentStore } from "../componentStore";
-import { MAT_DIALOG_DATA } from "@angular/material/dialog";
-import { GenericInfoCmp } from "src/atlasComponents/regionalFeatures/bsFeatures/genericInfo";
-import { _PLI_VOLUME_INJ_TOKEN, _TPLIVal } from "src/glue";
-import { uiActionSetPreviewingDatasetFiles } from "src/services/state/uiState.store.helper";
-import { viewerStateSetViewerMode } from "src/services/state/viewerState.store.helper";
 import { DialogService } from "src/services/dialogService.service";
-import { RouterService } from "src/routerModule/router.service";
-
-type TCStoreViewerCmp = {
-  overlaySideNav: any
-}
-
-export function ROIFactory(store: Store<any>, svc: PureContantService){
-  return store.pipe(
-    select(viewerStateContextedSelectedRegionsSelector),
-    switchMap(r => {
-      if (!r[0]) return of(null)
-      const { context } = r[0]
-      const { atlas, template, parcellation } = context || {}
-      return merge(
-        of(null),
-        svc.getRegionDetail(atlas['@id'], parcellation['@id'], template['@id'], r[0]).pipe(
-          map(det => {
-            return {
-              ...r[0],
-              ...det,
-            }
-          }),
-          // in case detailed requests fails
-          catchError((_err, _obs) => of(r[0])),
-        )
-      )
-    }),
-    shareReplay(1)
-  )
-}
+import { SAPI, SapiRegionModel } from "src/atlasComponents/sapi";
+import { atlasSelection, userInteraction, } from "src/state";
+import { SapiSpatialFeatureModel, SapiFeatureModel, SapiParcellationModel, SapiSpaceModel } from "src/atlasComponents/sapi/type";
+import { getUuid } from "src/util/fn";
+import { environment } from "src/environments/environment"
+import { SapiViewsFeaturesVoiQuery } from "src/atlasComponents/sapiViews/features";
+import { SapiViewsCoreSpaceBoundingBox } from "src/atlasComponents/sapiViews/core";
 
 @Component({
   selector: 'iav-cmp-viewer-container',
@@ -98,44 +56,26 @@ export function ROIFactory(store: Store<any>, svc: PureContantService){
     ]),
   ],
   providers: [
-    {
-      provide: REGION_OF_INTEREST,
-      useFactory: ROIFactory,
-      deps: [ Store, PureContantService ]
-    },
-    {
-      provide: OVERWRITE_SHOW_DATASET_DIALOG_TOKEN,
-      useFactory: (cStore: ComponentStore<TCStoreViewerCmp>) => {
-        return function overwriteShowDatasetDialog( arg: any ){
-          
-          cStore.setState({
-            overlaySideNav: arg
-          })
-        }
-      },
-      deps: [ ComponentStore ]
-    },
-    ComponentStore,
     DialogService
   ],
   changeDetection: ChangeDetectionStrategy.OnPush
 })
 
 export class ViewerCmp implements OnDestroy {
-  public _pliTitle = "Fiber structures of a human hippocampus based on joint DMRI, 3D-PLI, and TPFM acquisitions"
-  public _pliDesc = "The collected datasets provide real multimodal, multiscale structural connectivity insights into the human hippocampus. One post mortem hippocampus was scanned with Anatomical and Diffusion MRI (dMRI) [1], 3D Polarized Light Imaging (3D-PLI) [2], and Two-Photon Fluorescence Microscopy (TPFM) [3] using protocols specifically developed during SGA1 and SGA2, rendering joint tissue imaging possible. MRI scanning was performed with a 11.7 T Preclinical MRI system (gradients: 760 mT/m, slew rate: 9500 T/m/s) yielding T1-w and T2-w maps at 200 µm and dMRI-based maps at 300 µm resolution. During tissue sectioning (60 µm thickness) blockface (en-face) images were acquired from the surface of the frozen brain block, serving as reference for data integration/co-alignment. 530 brain sections were scanned with 3D-PLI. HPC-based image analysis provided transmittance, retardation, and fiber orientation maps at 1.3 µm in-plane resolution. TPFM was finally applied to selected brain sections utilizing autofluorescence properties of the fibrous tissue which appears after PBS washing (MAGIC protocol). The TPFM measurements provide a resolution of 0.44 µm x 0.44 µm x 1 µm."
-  public _pliLink = "https://doi.org/10.25493/JQ30-E08"
-  
-  public _1umTitle = `Cellular level 3D reconstructed volumes at 1µm resolution within the BigBrain occipital cortex`
-  public _1umDesc = `This dataset contains two 6x6x6 mm3 volumes sampled from the occipital cortex of a human postmortem brain. The volumes were acquired by rescanning the original histological sections of the BigBrain model at an isotropic resolution of 1µm. The sections were linearly 3D reconstructed by aligning matched pairs of bisected cells. Subsequently, both reconstructed stacks were anchored into the 20µm 3D BigBrain space using a 3D similarity transformation.`
-  public _1umLink = null // `https://search.kg.ebrains.eu/instances/d71d369a-c401-4d7e-b97a-3fb78eed06c5`
 
   public CONST = CONST
   public ARIA_LABELS = ARIA_LABELS
+  public VOI_QUERY_FLAG = environment.EXPERIMENTAL_FEATURE_FLAG
 
   @ViewChild('genericInfoVCR', { read: ViewContainerRef })
   genericInfoVCR: ViewContainerRef
 
+  @ViewChild('voiFeatures', { read: SapiViewsFeaturesVoiQuery })
+  voiQueryDirective: SapiViewsFeaturesVoiQuery
+
+  @ViewChild('bbox', { read: SapiViewsCoreSpaceBoundingBox })
+  boundingBoxDirective: SapiViewsCoreSpaceBoundingBox
+
   public quickTourRegionSearch: IQuickTourData = {
     order: 7,
     description: QUICKTOUR_DESC.REGION_SEARCH,
@@ -145,81 +85,91 @@ export class ViewerCmp implements OnDestroy {
     description: QUICKTOUR_DESC.ATLAS_SELECTOR,
   }
 
-  @Input() ismobile = false
-
   private subscriptions: Subscription[] = []
   private onDestroyCb: (() => void)[]  = []
   public viewerLoaded: boolean = false
 
-  public templateSelected$ = this.store$.pipe(
-    select(viewerStateSelectedTemplateSelector),
-    distinctUntilChanged(),
+  private selectedATP = this.store$.pipe(
+    atlasSelection.fromRootStore.distinctATP(),
+    shareReplay(1)
+  )
+
+  public selectedAtlas$ = this.selectedATP.pipe(
+    map(({ atlas }) => atlas)
+  )
+  public templateSelected$ = this.selectedATP.pipe(
+    map(({ template }) => template)
   )
-  public parcellationSelected$ = this.store$.pipe(
-    select(viewerStateSelectedParcellationSelector),
-    distinctUntilChanged(),
+  public parcellationSelected$ = this.selectedATP.pipe(
+    map(({ parcellation }) => parcellation)
+  )
+
+  public allAvailableParcellations$ = this.store$.pipe(
+    atlasSelection.fromRootStore.allAvailParcs(this.sapi)
   )
 
   public selectedRegions$ = this.store$.pipe(
-    select(viewerStateContextedSelectedRegionsSelector),
-    distinctUntilChanged(),
+    select(atlasSelection.selectors.selectedRegions),
+  )
+
+  public allAvailableRegions$ = this.store$.pipe(
+    select(atlasSelection.selectors.selectedParcAllRegions)
   )
 
   public isStandaloneVolumes$ = this.store$.pipe(
-    select(viewerStateStandAloneVolumes),
+    select(atlasSelection.selectors.standaloneVolumes),
     map(v => v.length > 0)
   )
 
   public viewerMode$: Observable<string> = this.store$.pipe(
-    select(viewerStateViewerModeSelector),
-    shareReplay(1),
-  )
-
-  public overlaySidenav$ = this.cStore.select(s => s.overlaySideNav).pipe(
+    select(atlasSelection.selectors.viewerMode),
     shareReplay(1),
   )
 
   public useViewer$: Observable<TSupportedViewers | 'notsupported'> = combineLatest([
-    this.templateSelected$,
+    this.store$.pipe(
+      atlasSelection.fromRootStore.distinctATP(),
+      switchMap(({ atlas, template }) => atlas && template
+        ? this.sapi.getSpace(atlas["@id"], template["@id"]).getVolumes()
+        : of(null)),
+      map(vols => {
+        if (!vols) return null
+        const flags = {
+          isNehuba: false,
+          isThreeSurfer: false
+        }
+        if (vols.find(vol => vol.data.volume_type === "neuroglancer/precomputed")) {
+          flags.isNehuba = true
+        }
+
+        if (vols.find(vol => vol.data.volume_type === "gii")) {
+          flags.isThreeSurfer = true
+        }
+        return flags
+      })
+    ),
     this.isStandaloneVolumes$,
   ]).pipe(
-    map(([t, isSv]) => {
+    distinctUntilChanged(([ prevFlags, prevIsSv ], [  currFlags, currIsSv ]) => {
+      const same = prevIsSv === currIsSv
+      && prevFlags?.isNehuba === currFlags?.isNehuba
+      && prevFlags?.isThreeSurfer === currFlags?.isThreeSurfer
+      return same
+    }),
+    map<unknown, TSupportedViewers | 'notsupported'>(([flags, isSv]) => {
       if (isSv) return 'nehuba'
-      if (!t) return null
-      if (!!t['nehubaConfigURL'] || !!t['nehubaConfig']) return 'nehuba'
-      if (!!t['three-surfer']) return 'threeSurfer'
+      if (!flags) return null
+      if (flags.isNehuba) return 'nehuba'
+      if (flags.isThreeSurfer) return 'threeSurfer'
       return 'notsupported'
-    })
+    }),
+    shareReplay(1),
   )
 
-  public viewerCtx$ = this.viewerModuleSvc.context$
+  public viewerCtx$ = this.ctxMenuSvc.context$
 
-  private _1umVoi$ = this.routerSvc.customRoute$.pipe(
-    map(obj => obj[`x-voi`] === "d71d369a-c401-4d7e-b97a-3fb78eed06c5"),
-    distinctUntilChanged()
-  )
-
-  private voiForceOff = new Subject()
-  public pliVol$ = merge(
-    this._pliVol$?.pipe(
-      filter(arr => arr.length > 0),
-      mapTo({
-        title: this._pliTitle,
-        description: this._pliDesc,
-        url: [{ doi: this._pliLink }]
-      })
-    ) || NEVER,
-    this._1umVoi$.pipe(
-      filter(flag => flag),
-      map(() => ({
-        title: this._1umTitle,
-        description: this._1umDesc,
-        url: this._1umLink
-          ? [{ doi: this._1umLink }]
-          : []
-      }))
-    ),
-    this.voiForceOff
+  public selectedFeature$: Observable<SapiFeatureModel> = this.store$.pipe(
+    select(userInteraction.selectors.selectedFeature)
   )
 
   /**
@@ -230,14 +180,12 @@ export class ViewerCmp implements OnDestroy {
    */
   public onlyShowMiniTray$: Observable<boolean> = combineLatest([
     this.selectedRegions$,
-    this.pliVol$.pipe(
-      startWith(null as { title: string, description: string, url: { doi: string }[] })
-    ),
     this.viewerMode$.pipe(
       startWith(null as string)
     ),
+    this.selectedFeature$,
   ]).pipe(
-    map(([ regions, layers, viewerMode ]) => regions.length === 0 && !layers && !viewerMode)
+    map(([ regions, viewerMode, selectedFeature ]) => regions.length === 0 && !viewerMode && !selectedFeature)
   )
 
   @ViewChild('viewerStatusCtxMenu', { read: TemplateRef })
@@ -247,57 +195,27 @@ export class ViewerCmp implements OnDestroy {
   private viewerStatusRegionCtxMenu: TemplateRef<any>
 
   public context: TContextArg<TSupportedViewers>
-  private templateSelected: any
-  private getRegionFromlabelIndexId: (arg: {labelIndexId: string}) => any
+  private templateSelected: SapiSpaceModel
 
-  private genericInfoCF: ComponentFactory<GenericInfoCmp>
-
-  public clearVoi(){
-    this.store$.dispatch(
-      uiActionSetPreviewingDatasetFiles({
-        previewingDatasetFiles: []
-      })
-    )
-    this.routerSvc.setCustomRoute('x-voi', null)
-    this.voiForceOff.next(false)
-  }
   constructor(
     private store$: Store<any>,
-    private viewerModuleSvc: ContextMenuService<TContextArg<'threeSurfer' | 'nehuba'>>,
-    private cStore: ComponentStore<TCStoreViewerCmp>,
-    cfr: ComponentFactoryResolver,
+    private ctxMenuSvc: ContextMenuService<TContextArg<'threeSurfer' | 'nehuba'>>,
     private dialogSvc: DialogService,
     private cdr: ChangeDetectorRef,
-    private routerSvc: RouterService,
-    @Optional() @Inject(_PLI_VOLUME_INJ_TOKEN) private _pliVol$: Observable<_TPLIVal[]>,
-    @Optional() @Inject(REGION_OF_INTEREST) public regionOfInterest$: Observable<any>
+    private sapi: SAPI,
   ){
 
-    this.genericInfoCF = cfr.resolveComponentFactory(GenericInfoCmp)
-
     this.subscriptions.push(
-      this.selectedRegions$.subscribe(() => {
-        this.clearPreviewingDataset()
-      }),
-      this.viewerModuleSvc.context$.subscribe(
+      this.ctxMenuSvc.context$.subscribe(
         (ctx: any) => this.context = ctx
       ),
       this.templateSelected$.subscribe(
         t => this.templateSelected = t
       ),
-      this.parcellationSelected$.subscribe(
-        p => {
-          this.getRegionFromlabelIndexId = !!p
-            ? getGetRegionFromLabelIndexId({ parcellation: p })
-            : null
-        }
-      ),
       combineLatest([
         this.templateSelected$,
         this.parcellationSelected$,
-        this.store$.pipe(
-          select(viewerStateGetSelectedAtlas)
-        )
+        this.selectedAtlas$,
       ]).pipe(
         debounceTime(160)
       ).subscribe(async ([tmpl, parc, atlas]) => {
@@ -311,7 +229,7 @@ export class ViewerCmp implements OnDestroy {
           message.push(`- _${atlas.name}_`)
         }
         if (checkPrerelease(tmpl)) {
-          message.push(`- _${tmpl.name}_`)
+          message.push(`- _${tmpl.fullName}_`)
         }
         if (checkPrerelease(parc)) {
           message.push(`- _${parc.name}_`)
@@ -333,7 +251,7 @@ export class ViewerCmp implements OnDestroy {
     )
   }
 
-  ngAfterViewInit(){
+  ngAfterViewInit(): void{
     const cb: TContextMenuReg<TContextArg<'nehuba' | 'threeSurfer'>> = ({ append, context }) => {
 
       /**
@@ -356,25 +274,13 @@ export class ViewerCmp implements OnDestroy {
       let hoveredRegions = []
       if (context.viewerType === 'nehuba') {
         hoveredRegions = (context as TContextArg<'nehuba'>).payload.nehuba.reduce(
-          (acc, curr) => acc.concat(
-            curr.labelIndices.map(
-              lblIdx => {
-                const labelIndexId = `${curr.layerName}#${lblIdx}`
-                if (!!this.getRegionFromlabelIndexId) {
-                  return this.getRegionFromlabelIndexId({
-                    labelIndexId: `${curr.layerName}#${lblIdx}`
-                  })
-                }
-                return labelIndexId
-              }
-            )
-          ),
+          (acc, curr) => acc.concat(...curr.regions),
           []
         )
       }
 
       if (context.viewerType === 'threeSurfer') {
-        hoveredRegions = (context as TContextArg<'threeSurfer'>).payload._mouseoverRegion
+        hoveredRegions = (context as TContextArg<'threeSurfer'>).payload.regions
       }
 
       if (hoveredRegions.length > 0) {
@@ -390,77 +296,130 @@ export class ViewerCmp implements OnDestroy {
 
       return true
     }
-    this.viewerModuleSvc.register(cb)
+    this.ctxMenuSvc.register(cb)
     this.onDestroyCb.push(
-      () => this.viewerModuleSvc.deregister(cb)
-    )
-    this.subscriptions.push(
-      this.overlaySidenav$.pipe(
-        switchMap(switchMapWaitFor({
-          condition: () => !!this.genericInfoVCR
-        }))
-      ).subscribe(data => {
-        if (!this.genericInfoVCR) {
-          console.warn(`genericInfoVCR not defined!`)
-          return
-        }
-        const injector = Injector.create({
-          providers: [{
-            provide: MAT_DIALOG_DATA,
-            useValue: data
-          }]
-        })
-
-        this.genericInfoVCR.clear()
-        this.genericInfoVCR.createComponent(this.genericInfoCF, null, injector)
-        this.cdr.markForCheck()
-      })
+      () => this.ctxMenuSvc.deregister(cb)
     )
   }
 
-  ngOnDestroy() {
+  ngOnDestroy(): void {
     while (this.subscriptions.length) this.subscriptions.pop().unsubscribe()
     while (this.onDestroyCb.length > 0) this.onDestroyCb.pop()()
   }
 
-  public selectRoi(roi: any) {
+  public clearRoi(): void{
     this.store$.dispatch(
-      viewerStateSetSelectedRegions({
-        selectRegions: [ roi ]
-      })
+      atlasSelection.actions.clearSelectedRegions()
     )
   }
 
-  public exitSpecialViewMode(){
+  public selectRoi(roi: SapiRegionModel): void {
     this.store$.dispatch(
-      viewerStateSetViewerMode({
-        payload: null
+      atlasSelection.actions.selectRegion({
+        region: roi
       })
     )
   }
 
-  public clearPreviewingDataset(){
-    /**
-     * clear all preview
-     */
-    this.cStore.setState({
-      overlaySideNav: null
-    })
+  public exitSpecialViewMode(): void{
+    this.store$.dispatch(
+      atlasSelection.actions.clearViewerMode()
+    )
   }
 
-  public handleViewerEvent(event: TViewerEvent<'nehuba' | 'threeSurfer'>){
+  public handleViewerEvent(event: TViewerEvent<'nehuba' | 'threeSurfer'>): void{
     switch(event.type) {
     case EnumViewerEvt.VIEWERLOADED:
       this.viewerLoaded = event.data
+      this.cdr.detectChanges()
       break
     case EnumViewerEvt.VIEWER_CTX:
-      this.viewerModuleSvc.context$.next(event.data)
+      this.ctxMenuSvc.context$.next(event.data)
+      if (event.data.viewerType === "nehuba") {
+        const { nehuba, nav } = (event.data as TContextArg<"nehuba">).payload
+        if (nehuba) {
+          const mousingOverRegions = (nehuba || []).reduce((acc, { regions }) => acc.concat(...regions), [])
+          this.store$.dispatch(
+            userInteraction.actions.mouseoverRegions({
+              regions: mousingOverRegions
+            })
+          )
+        }
+        if (nav) {
+          this.store$.dispatch(
+            userInteraction.actions.mouseoverPosition({
+              position: {
+                "@id": getUuid(),
+                "@type": "https://openminds.ebrains.eu/sands/CoordinatePoint",
+                coordinates: nav.position.map(p => {
+                  return {
+                    value: p,
+                  }
+                }),
+                coordinateSpace: {
+                  '@id': this.templateSelected["@id"]
+                }
+              }
+            })
+          )
+        }
+      }
       break
     default:
     }
   }
 
-  public disposeCtxMenu(){
-    this.viewerModuleSvc.dismissCtxMenu()
+  public disposeCtxMenu(): void{
+    this.ctxMenuSvc.dismissCtxMenu()
+  }
+
+  showDataset(feat: SapiFeatureModel): void {
+    if ((feat as SapiSpatialFeatureModel).location) {
+      const feature = feat as SapiSpatialFeatureModel
+      this.store$.dispatch(
+        atlasSelection.actions.navigateTo({
+          navigation: {
+            orientation: [0, 0, 0, 1],
+            position: feature.location.center.coordinates.map(v => v.value * 1e6)
+          },
+          animation: true
+        })
+      )
+    }
+    
+    this.store$.dispatch(
+      userInteraction.actions.showFeature({
+        feature: feat
+      })
+    )
+  }
+
+  clearSelectedFeature(): void{
+    this.store$.dispatch(
+      userInteraction.actions.clearShownFeature()
+    )
+  }
+
+  onDismissNonbaseLayer(): void{
+    this.store$.dispatch(
+      atlasSelection.actions.clearNonBaseParcLayer()
+    )
+  }
+  onSelectParcellation(parcellation: SapiParcellationModel): void{
+    this.store$.dispatch(
+      atlasSelection.actions.selectParcellation({
+        parcellation
+      })
+    )
+  }
+  navigateTo(position: number[]): void {
+    this.store$.dispatch(
+      atlasSelection.actions.navigateTo({
+        navigation: {
+          position
+        },
+        animation: true,
+      })
+    )
   }
 }
diff --git a/src/viewerModule/viewerCmp/viewerCmp.style.css b/src/viewerModule/viewerCmp/viewerCmp.style.css
index 6633b28f8ec233a078953ba66fbb6c73877b2274..e7f00472f295d233a6d12cbd1b6dcc751d59d000 100644
--- a/src/viewerModule/viewerCmp/viewerCmp.style.css
+++ b/src/viewerModule/viewerCmp/viewerCmp.style.css
@@ -3,11 +3,6 @@
   position: relative;
 }
 
-.explore-btn
-{
-  margin-top: 4.5rem;
-}
-
 .region-text-search-autocomplete-position
 {
   z-index: 10;
@@ -55,7 +50,76 @@ mat-drawer
   transition: margin-left 200ms cubic-bezier(0.35, 0, 0.25, 1);
 }
 
-._pli-container
+.sapi-container
 {
   padding-top: 5rem;
 }
+
+sxplr-sapiviews-core-region-region-chip
+{
+  margin-left:-5rem;
+}
+
+sxplr-sapiviews-core-region-region-chip [prefix]
+{
+  padding-left:5rem;
+}
+
+.auto-complete-container
+{
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+}
+
+.auto-complete-container > sxplr-sapiviews-core-rich-regionlistsearch
+{
+  flex: 1 1 0;
+}
+
+.auto-complete-container > button
+{
+  flex: 0 0 auto;
+}
+
+.min-tray-explr-btn
+{
+  width: 100%;
+  padding-left: 0.5rem;
+  padding-right: 0.5rem;
+  margin-top: -1rem;
+}
+
+.floating-ui
+{
+  display: block;
+  z-index: 5;
+  position: absolute;
+  top: 0;
+  left: 0;
+  height: 100%;
+  width: 100%;
+  pointer-events: none;
+}
+
+logo-container
+{
+  height: 2rem;
+  opacity: 0.2;
+}
+
+mat-list[dense].contextual-block
+{
+  display: inline-block;
+  background-color:rgba(200,200,200,0.8);
+}
+
+:host-context([darktheme="true"]) mat-list[dense].contextual-block
+{
+  background-color : rgba(30,30,30,0.8);
+}
+
+.region-populated
+{
+  overflow: hidden auto;
+}
diff --git a/src/viewerModule/viewerCmp/viewerCmp.template.html b/src/viewerModule/viewerCmp/viewerCmp.template.html
index fae280bfbd67128f67fc6567e1fe7b87883aeca2..90d6d4a3810ecb35d68f616af533a3579493d78a 100644
--- a/src/viewerModule/viewerCmp/viewerCmp.template.html
+++ b/src/viewerModule/viewerCmp/viewerCmp.template.html
@@ -1,6 +1,59 @@
-<div class="position-absolute w-100 h-100">
+<div iav-media-query class="position-absolute w-100 h-100" #media="iavMediaQuery">
   <ng-container *ngTemplateOutlet="viewerTmpl">
   </ng-container>
+
+  <div class="floating-ui">
+
+    <div *ngIf="(media.mediaBreakPoint$ | async) < 2"
+      class="fixed-bottom pe-none mb-2 d-flex justify-content-center">
+      <logo-container></logo-container>
+    </div>
+
+    <div *ngIf="(media.mediaBreakPoint$ | async) < 2" floatingMouseContextualContainerDirective>
+
+      <div class="h-0"
+        iav-mouse-hover
+        #iavMouseHoverContextualBlock="iavMouseHover">
+      </div>
+      <mat-list dense class="contextual-block">
+        <mat-list-item *ngFor="let cvtOutput of iavMouseHoverContextualBlock.currentOnHoverObs$ | async | mouseoverCvt"
+          class="h-auto">
+
+          <mat-icon
+            [fontSet]="cvtOutput.icon.fontSet"
+            [fontIcon]="cvtOutput.icon.fontIcon"
+            mat-list-icon>
+          </mat-icon>
+
+          <div matLine>{{ cvtOutput.text }}</div>
+
+        </mat-list-item>
+
+        <ng-template [ngIf]="voiQueryDirective && (voiQueryDirective.onhover | async)" let-feat>
+          <mat-list-item>
+            <mat-icon
+              fontSet="fas"
+              fontIcon="fa-database"
+              mat-list-icon>
+            </mat-icon>
+            <div matLine>{{ feat?.metadata?.fullName || 'Feature' }}</div>
+          </mat-list-item>
+        </ng-template>
+      </mat-list>
+      <!-- TODO Potentially implementing plugin contextual info -->
+    </div>
+
+    <!-- mouse on click context menu, currently not used -->
+    <!-- <div class="floating-container"
+      [attr.aria-label]="CONTEXT_MENU_ARIA_LABEL"
+      fixedMouseContextualContainerDirective
+      #fixedContainer="iavFixedMouseCtxContainer">
+
+      
+
+    </div> -->
+
+  </div>
 </div>
 
 
@@ -23,7 +76,7 @@
     (@openClose.done)="$event.toState === 'closed' && drawer.close()"
     [autoFocus]="false"
     [disableClose]="true"
-    class="iv-custom-comp darker-bg p-0 pe-all col-10 col-sm-10 col-md-5 col-lg-4 col-xl-3 col-xxl-2 z-index-10">
+    class="sxplr-custom-cmp darker-bg sxplr-p-0 pe-all col-10 col-sm-10 col-md-5 col-lg-4 col-xl-3 col-xxl-2 z-index-10">
 
     <!-- entry template -->
     <ng-template [ngIf]="viewerMode$ | async" let-mode [ngIfElse]="regularTmpl">
@@ -137,58 +190,17 @@
   let-drawer="drawer"
   let-showFullSidenavSwitch="showFullSidenavSwitch">
 
-  <!-- check if preview volume -->
-  <ng-template [ngIf]="overlaySidenav$ | async" let-overlaySideNav>
-    
-    <!-- back btn -->
-    <button mat-button
-      (click)="clearPreviewingDataset()"
-      [attr.aria-label]="ARIA_LABELS.CLOSE"
-      class="position-absolute z-index-10 m-2">
-      <i class="fas fa-chevron-left"></i>
-      <span class="ml-1">
-        Back
-      </span>
-    </button>
-
-    <ng-template #genericInfoVCR>
-    </ng-template>
-  </ng-template>
-
-  <div [ngClass]="{
-    'invisible overflow-hidden h-0': overlaySidenav$ | async,
-    'h-100': !(overlaySidenav$ | async)
-  }" class="pe-all position-relative d-flex flex-column">
-    
-
-  <!-- if pli voi is visible, show pli template
-  otherwise show region tmpl -->
+  <!-- selectedFeature || selectedRegion -->
   <ng-template
-    [ngTemplateOutlet]="(pliVol$ | async)
-      ? voiTmpl
+    [ngTemplateOutlet]="(selectedFeature$ | async)
+      ? selectedFeatureTmpl
       : sidenavRegionTmpl"
     [ngTemplateOutletContext]="{
       drawer: drawer,
-      showFullSidenavSwitch: showFullSidenavSwitch
+      showFullSidenavSwitch: showFullSidenavSwitch,
+      feature: selectedFeature$ | async
     }">
   </ng-template>
-
-    <!-- <ng-template let-pliVol [ngIf]="pliVol$ | async" [ngIfElse]="sidenavRegionTmpl">
-      <ng-template [ngIf]="pliVol.length > 0" [ngIfElse]="sidenavRegionTmpl">
-        <ng-template [ngTemplateOutlet]="voiTmpl">
-
-        </ng-template>
-      </ng-template>
-    </ng-template> -->
-
-    <!-- TODO dataset preview will become deprecated in the future.
-    Regional feature/data feature will replace it -->
-    <!-- <div class="hidden"
-      iav-shown-dataset
-      #iavShownDataset="iavShownDataset">
-    </div> -->
-
-  </div>
 </ng-template>
 
 
@@ -206,33 +218,43 @@
       'transition-margin-left': !drawer.opened
     }">
 
+    <!-- collapsed side bar view -->
     <div class="h-0 w-100 region-text-search-autocomplete-position">
       <ng-container *ngTemplateOutlet="autocompleteTmpl; context: { showTour: true }">
       </ng-container>
+      
+      <ng-template [ngIf]="VOI_QUERY_FLAG">
+        <div *ngIf="!((selectedRegions$ | async)[0])" class="sxplr-p-2 w-100">
+          <ng-container *ngTemplateOutlet="spatialFeatureListViewTmpl"></ng-container>
+        </div>
+      </ng-template>
     </div>
 
     <!-- such a gross implementation -->
     <!-- TODO fix this -->
-    <div class="mt-1-n w-100 pl-2 pr-2 m-1px">
+    <div class="min-tray-explr-btn"
+      sxplr-sapiviews-core-region
+      [sxplr-sapiviews-core-region-atlas]="selectedAtlas$ | async"
+      [sxplr-sapiviews-core-region-template]="templateSelected$ | async"
+      [sxplr-sapiviews-core-region-parcellation]="parcellationSelected$ | async"
+      [sxplr-sapiviews-core-region-region]="(selectedRegions$ | async)[0]"
+      [sxplr-sapiviews-core-region-detail-flag]="true"
+      #sapiRegion="sapiViewsCoreRegion">
+
+      <!-- TODO use sapiViews/core/region/base and fix the rest -->
       <button mat-raised-button
         *ngIf="!(onlyShowMiniTray$ | async)"
         [attr.aria-label]="ARIA_LABELS.EXPAND"
         (click)="showFullSidenav()"
-        class="explore-btn pe-all w-100"
+        class="sxplr-mt-9 sxplr-pe-all w-100"
         [ngClass]="{
-          'darktheme': iavRegion.rgbDarkmode === true,
-          'lighttheme': iavRegion.rgbDarkmode === false
+          'darktheme': sapiRegion.regionDarkmode,
+          'lighttheme': !sapiRegion.regionDarkmode
         }"
-        [style.backgroundColor]="iavRegion?.rgbString || 'accent'">
-        <span class="text iv-custom-comp">
+        [style.backgroundColor]="sapiRegion.regionRgbString">
+        <span class="text sxplr-custom-cmp">
           Explore
         </span>
-
-        <div class="hidden"
-          iav-region
-          [region]="(selectedRegions$ | async) && (selectedRegions$ | async)[0]"
-          #iavRegion="iavRegion">
-        </div>
       </button>
     </div>
   
@@ -243,7 +265,8 @@
     <ng-container *ngTemplateOutlet="tabTmpl; context: {
       isOpen: minTrayVisSwitch.switchState$ | async,
       regionSelected: selectedRegions$ | async,
-      click: minTrayVisSwitch.toggle.bind(minTrayVisSwitch)
+      click: minTrayVisSwitch.toggle.bind(minTrayVisSwitch),
+      badge: voiQueryDirective && (voiQueryDirective.features$ | async).length || null
     }">
     </ng-container>
   </div>
@@ -287,6 +310,13 @@
   <iav-cmp-viewer-nehuba-status *ngIf="(useViewer$ | async) === 'nehuba'"
     class="pe-all mt-2 muted-7 d-inline-block v-align-top">
   </iav-cmp-viewer-nehuba-status>
+  <button
+    mat-icon-button
+    sxplr-share-view
+    *ngIf="(useViewer$ | async) === 'threeSurfer'"
+    class="pe-all mt-2 muted-7 d-inline-block v-align-top">
+    <i class="fas fa-share-square"></i>
+  </button>
 </ng-template>
 
 
@@ -335,16 +365,16 @@
 
   <!-- signin banner at top right corner -->
   <top-menu-cmp class="mt-3 mr-2 d-inline-block"
-    [ismobile]="ismobile"
+    [ismobile]="(media.mediaBreakPoint$ | async) > 3"
     [viewerLoaded]="viewerLoaded">
   </top-menu-cmp>
 
-  <atlas-dropdown-selector
-    class="v-align-top pt-2 pe-all mt-2 iv-custom-comp bg card m-2 mat-elevation-z2 d-inline-block"
+  <sxplr-sapiviews-core-atlas-dropdown-selector
+    class="v-align-top sxplr-pt-2 pe-all mt-2 sxplr-custom-cmp bg card m-2 mat-elevation-z2 d-inline-block"
     quick-tour
     [quick-tour-description]="quickTourAtlasSelector.description"
     [quick-tour-order]="quickTourAtlasSelector.order">
-  </atlas-dropdown-selector>
+  </sxplr-sapiviews-core-atlas-dropdown-selector>
 
 </ng-template>
 
@@ -367,18 +397,54 @@
 <ng-template #bottomLeftTmpl let-showFullSideNav="showFullSideNav">
 
   <!-- atlas selector -->
-  <atlas-layer-selector *ngIf="viewerLoaded && !(isStandaloneVolumes$ | async)"
-    #alSelector="atlasLayerSelector"
-    class="d-inline-block flex-grow-0 flex-shrink-0 pe-all"
-    (iav-outsideClick)="alSelector.selectorExpanded = false">
-  </atlas-layer-selector>
-
-  <!-- chips -->
-  <div *ngIf="parcellationSelected$ | async"
-    class="d-inline-block flex-grow-1 flex-shrink-1 pe-none overflow-x-auto overflow-y-hidden">
-
-    <viewer-state-breadcrumb class="d-inline-block pe-all" (on-item-click)="showFullSideNav()">
-    </viewer-state-breadcrumb>
+  <sxplr-sapiviews-core-atlas-tmplparcselector
+    *ngIf="viewerLoaded && !(isStandaloneVolumes$ | async)"
+    [iav-key-listener]="[{'type': 'keydown', 'key': 'Escape'}]"
+    (iav-key-event)="tmplParcSelector.closeSelector()"
+    (iav-outsideClick)="tmplParcSelector.closeSelector()"
+    #tmplParcSelector="sxplrSapiViewsCoreAtlasTmplparcselector">
+  </sxplr-sapiviews-core-atlas-tmplparcselector>
+
+  <!-- scroll container -->
+  <div class="sxplr-d-inline-flex
+    sxplr-flex-wrap-nowrap
+    sxplr-mxw-80vw
+    sxplr-pe-all
+    sxplr-of-x-auto
+    sxplr-of-y-hidden">
+
+    <!-- selected parcellation chip -->
+    <sxplr-sapiviews-core-parcellation-smartchip
+      class="sxplr-z-2 sxplr-mr-1"
+      [sxplr-sapiviews-core-parcellation-smartchip-parcellation]="parcellationSelected$ | async"
+      [sxplr-sapiviews-core-parcellation-smartchip-all-parcellations]="allAvailableParcellations$ | async"
+      (sxplr-sapiviews-core-parcellation-smartchip-dismiss-nonbase-layer)="onDismissNonbaseLayer()"
+      (sxplr-sapiviews-core-parcellation-smartchip-select-parcellation)="onSelectParcellation($event)"
+      >
+    </sxplr-sapiviews-core-parcellation-smartchip>
+
+    <!-- selected region chip -->
+    <sxplr-sapiviews-core-region-region-chip
+      *ngFor="let region of selectedRegions$ | async"
+      class="sxplr-pe-all sxplr-z-1 sxplr-mr-1"
+      (sxplr-sapiviews-core-region-region-chip-clicked)="showFullSideNav()"
+      [sxplr-sapiviews-core-region-atlas]="selectedAtlas$ | async"
+      [sxplr-sapiviews-core-region-template]="templateSelected$ | async"
+      [sxplr-sapiviews-core-region-parcellation]="parcellationSelected$ | async"
+      [sxplr-sapiviews-core-region-region]="region">
+
+      <div prefix>
+      </div>
+
+      <div suffix class="sxplr-scale-70">
+        <button mat-mini-fab
+          color="primary"
+          iav-stop="mousedown click"
+          (click)="clearRoi()">
+          <i class="fas fa-times"></i>
+        </button>
+      </div>
+    </sxplr-sapiviews-core-region-region-chip>
   </div>
 
 </ng-template>
@@ -391,20 +457,16 @@
     <ng-container [ngSwitch]="useViewer$ | async">
 
       <!-- nehuba viewer -->
-      <iav-cmp-viewer-nehuba-glue class="d-block w-100 h-100 position-absolute left-0 top-0"
+      <iav-cmp-viewer-nehuba-glue class="d-block w-100 h-100 position-absolute left-0 tosxplr-p-0"
         *ngSwitchCase="'nehuba'"
         (viewerEvent)="handleViewerEvent($event)"
-        [selectedTemplate]="templateSelected$ | async"
-        [selectedParcellation]="parcellationSelected$ | async"
         #iavCmpViewerNehubaGlue="iavCmpViewerNehubaGlue">
       </iav-cmp-viewer-nehuba-glue>
 
       <!-- three surfer (free surfer viewer) -->
-      <three-surfer-glue-cmp class="d-block w-100 h-100 position-absolute left-0 top-0"
+      <three-surfer-glue-cmp class="d-block w-100 h-100 position-absolute left-0 tosxplr-p-0"
         *ngSwitchCase="'threeSurfer'"
-        (viewerEvent)="handleViewerEvent($event)"
-        [selectedTemplate]="templateSelected$ | async"
-        [selectedParcellation]="parcellationSelected$ | async">
+        (viewerEvent)="handleViewerEvent($event)">
       </three-surfer-glue-cmp>
 
       <!-- if not supported, show not supported message -->
@@ -412,7 +474,7 @@
 
       <!-- by default, show splash screen -->
       <div *ngSwitchDefault>
-        <ui-splashscreen class="position-absolute left-0 top-0">
+        <ui-splashscreen class="position-absolute left-0 tosxplr-p-0">
         </ui-splashscreen>
       </div>
     </ng-container>
@@ -424,15 +486,68 @@
   </div>
 </ng-template>
 
+<!-- region-hierarchy-tmpl -->
+
+<ng-template #regionHierarchyTmpl>
+  <div class="sxplr-d-flex sxplr-flex-column sxplr-h-100">
+    <sxplr-sapiviews-core-rich-regionshierarchy
+      class="sxplr-w-100 sxplr-flex-var"
+      [sxplr-sapiviews-core-rich-regionshierarchy-regions]="allAvailableRegions$ | async"
+      [sxplr-sapiviews-core-rich-regionshierarchy-accent-regions]="selectedRegions$ | async"
+      (sxplr-sapiviews-core-rich-regionshierarchy-region-select)="selectRoi($event)"
+      >
+    </sxplr-sapiviews-core-rich-regionshierarchy>
+  
+    <mat-dialog-actions align="center" class="sxplr-flex-static">
+      <button mat-button mat-dialog-close>Close</button>
+    </mat-dialog-actions>
+  </div>
+</ng-template>
 
 <!-- auto complete search box -->
 <ng-template #autocompleteTmpl let-showTour="showTour">
-  <div class="iv-custom-comp bg card ml-2 mr-2 mat-elevation-z8 pe-all">
-    <region-text-search-autocomplete class="w-100 pt-2 flex-shrink-0 flex-grow-0">
-    </region-text-search-autocomplete>
+  <div class="sxplr-custom-cmp bg card ml-2 mr-2 mat-elevation-z8 pe-all auto-complete-container">
+    
+    <ng-template #selectedRegionCheckTmpl let-region>
+      <ng-template #fallbackTmpl>
+        <button mat-icon-button
+          class="sxplr-mt-a sxplr-mb-a">
+          <i class="far fa-square"></i>
+        </button>
+      </ng-template>
+
+      <button *ngIf="selectedRegions$ | async | includes : region; else fallbackTmpl"
+        mat-icon-button
+        color="primary"
+        class="sxplr-mt-a sxplr-mb-a">
+        <i class="far fa-check-square"></i>
+      </button>
+    </ng-template>
+
+    <sxplr-sapiviews-core-rich-regionlistsearch
+      [sxplr-sapiviews-core-rich-regionlistsearch-regions]="allAvailableRegions$ | async"
+      [sxplr-sapiviews-core-rich-regionlistsearch-current-search]="selectedRegions$ | async | getProperty : 0 | getProperty : 'name'"
+      [sxplr-sapiviews-core-rich-regionlistsearch-region-template-ref]="selectedRegionCheckTmpl"
+      (sxplr-sapiviews-core-rich-regionlistsearch-region-select)="selectRoi($event)">
+
+      <button mat-icon-button
+        search-input-suffix
+        *ngIf="selectedRegions$ | async | getProperty : 'length'"
+        (click)="clearRoi()">
+        <i class="fas fa-times"></i>
+      </button>
+    </sxplr-sapiviews-core-rich-regionlistsearch>
+
+    <button mat-icon-button
+      color="primary"
+      [sxplr-dialog]="regionHierarchyTmpl"
+      sxplr-dialog-size="xl">
+      <i class="fas fa-sitemap"></i>
+    </button>
 
     <div class="w-100 h-100 position-absolute pe-none" *ngIf="showTour">
     </div>
+
   </div>
 </ng-template>
 
@@ -441,7 +556,8 @@
   let-isOpen="isOpen"
   let-regionSelected="regionSelected"
   let-iavAdditionallayers="iavAdditionallayers"
-  let-click="click">
+  let-click="click"
+  let-badge="badge">
 
   <!-- if mat drawer is open -->
   <ng-template [ngIf]="isOpen" [ngIfElse]="tabTmpl_closedTmpl">
@@ -449,7 +565,8 @@
       [ngTemplateOutletContext]="{
         matColor: 'basic',
         fontIcon: 'fa-chevron-left',
-        click: click
+        click: click,
+        badge: badge
       }">
     </ng-template>
   </ng-template>
@@ -473,18 +590,24 @@
 
       <!-- if region selected > 0 -->
       <ng-template [ngIf]="regionSelected?.length > 0" [ngIfElse]="tabTmpl_nothingSelected">
-        <div class="hidden"
-          iav-region
-          [region]="regionSelected[0]"
-          #tabTmpl_iavRegion="iavRegion">
+
+        <div sxplr-sapiviews-core-region
+          [sxplr-sapiviews-core-region-detail-flag]="true"
+          [sxplr-sapiviews-core-region-atlas]="selectedAtlas$ | async"
+          [sxplr-sapiviews-core-region-template]="templateSelected$ | async"
+          [sxplr-sapiviews-core-region-parcellation]="parcellationSelected$ | async"
+          [sxplr-sapiviews-core-region-region]="regionSelected[0]"
+          #tabTmpl_iavRegion="sapiViewsCoreRegion">
+
         </div>
 
+        <!-- TODO fix with sapiView/core/region directive -->
         <ng-container *ngTemplateOutlet="tabTmpl_defaultTmpl; context: {
           matColor: 'accent',
-          customColor: tabTmpl_iavRegion.rgbString,
-          customColorDarkmode: tabTmpl_iavRegion.rgbDarkmode,
+          customColor: tabTmpl_iavRegion.regionRgbString,
+          customColorDarkmode: tabTmpl_iavRegion.regionDarkmode,
           fontIcon: 'fa-brain',
-          tooltip: 'Explore ' + tabTmpl_iavRegion.region.name,
+          tooltip: 'Explore ' + regionSelected[0].name,
           click: click
         }">
 
@@ -497,7 +620,8 @@
           matColor: 'primary',
           fontIcon: 'fa-sitemap',
           tooltip: 'Explore regions',
-          click: click
+          click: click,
+          badge: badge
         }">
         </ng-container>
       </ng-template>
@@ -531,62 +655,12 @@
     [matBadge]="badge"
     [matBadgeColor]="badgeColor || 'warn'">
 
-    <span [ngClass]="{'iv-custom-comp  text': !!customColor}">
+    <span [ngClass]="{'sxplr-custom-cmp  text': !!customColor}">
       <i class="fas" [ngClass]="fontIcon || 'fa-question'"></i>
     </span>
   </button>
 </ng-template>
 
-<!-- VOI sidenav tmpl -->
-<ng-template #voiTmpl>
-
-  <!-- back btn -->
-  <button mat-button
-    (click)="clearVoi()"
-    [attr.aria-label]="ARIA_LABELS.CLOSE"
-    class="position-absolute z-index-10 m-2">
-    <i class="fas fa-chevron-left"></i>
-    <span class="ml-1">
-      Back
-    </span>
-  </button>
-
-  <mat-card class="_pli-container">
-    <mat-card-title>
-      {{ pliVol$ | async | getProperty : 'title' }}
-    </mat-card-title>
-
-    <mat-card-subtitle class="d-inline-flex align-items-center flex-wrap">
-      <mat-icon fontSet="fas" fontIcon="fa-database"></mat-icon>
-      <span>
-        Dataset preview
-      </span>
-
-      <mat-divider vertical="true" class="ml-2 h-2rem"></mat-divider>
-
-      <a *ngFor="let url of pliVol$ | async | getProperty : 'url'" [href]="url.doi"
-        mat-icon-button
-        matTooltip="Explore in EBRAINS Knowledge Graph"
-        target="_blank">
-        <i class="fas fa-external-link-alt"></i>
-      </a>
-
-    </mat-card-subtitle>
-
-    <small class="d-block text-muted iv-custom-comp darker-bg">
-      {{ pliVol$ | async | getProperty : 'description' }}
-    </small>
-
-    <mat-expansion-panel class="sidenav-cover-header-container">
-      <mat-expansion-panel-header>
-        <mat-panel-title>
-          Registered Volumes
-        </mat-panel-title>
-      </mat-expansion-panel-header>
-      <layer-browser></layer-browser>
-    </mat-expansion-panel>
-  </mat-card>
-</ng-template>
 
 <!-- region sidenav tmpl -->
 <ng-template #sidenavRegionTmpl
@@ -600,7 +674,7 @@
     </ng-container>
   </div>
 
-  <div class="flex-shrink-1 flex-grow-1 d-flex flex-column"
+  <div class="flex-shrink-1 flex-grow-1 d-flex flex-column sxplr-h-100"
     [ngClass]="{'region-populated': (selectedRegions$ | async).length > 0 }">
     <!-- region detail -->
     <ng-container *ngIf="selectedRegions$ | async as selectedRegions; else selectRegionErrorTmpl">
@@ -609,8 +683,22 @@
       <ng-template [ngIf]="selectedRegions.length === 1" [ngIfElse]="multiRegionWrapperTmpl">
         <!-- a series of bugs result in requiring this hacky -->
         <!-- see https://github.com/HumanBrainProject/interactive-viewer/issues/698 -->
-        <ng-container *ngTemplateOutlet="singleRegionTmpl; context: { region: (regionOfInterest$ | async) }">
-        </ng-container>
+
+
+        <ng-template [ngIf]="regionDirective.fetchInProgress$ | async">
+          <spinner-cmp class="sxplr-mt-10 fs-200"></spinner-cmp>
+        </ng-template>
+        <sxplr-sapiviews-core-region-region-rich
+          [sxplr-sapiviews-core-region-atlas]="selectedAtlas$ | async"
+          [sxplr-sapiviews-core-region-template]="templateSelected$ | async"
+          [sxplr-sapiviews-core-region-parcellation]="parcellationSelected$ | async"
+          [sxplr-sapiviews-core-region-region]="selectedRegions[0]"
+          (sxplr-sapiviews-core-region-region-rich-feature-clicked)="showDataset($event)"
+          (sxplr-sapiviews-core-region-navigate-to)="navigateTo($event)"
+          #regionDirective="sapiViewsCoreRegionRich"
+        >
+          <div class="sapi-container" header></div>
+        </sxplr-sapiviews-core-region-region-rich>
       </ng-template>
 
       <!-- multi region wrapper -->
@@ -624,8 +712,7 @@
 
       <!-- place holder if length === 0 -->
       <ng-container *ngIf="selectedRegions.length === 0">
-        <ng-container *ngTemplateOutlet="singleRegionTmpl; context: { region: false }">
-        </ng-container>
+        no region selected
       </ng-container>
     </ng-container>
 
@@ -642,16 +729,6 @@
 </ng-template>
 
 
-<!-- single region tmpl -->
-<ng-template #singleRegionTmpl let-region="region">
-  <!-- region detail -->
-  <region-menu
-    [region]="region"
-    class="flex-grow-1 bs-border-box mat-elevation-z4">
-  </region-menu>
-</ng-template>
-
-
 <!-- expansion tmpl -->
 <ng-template #ngMatAccordionTmpl
   let-title="title"
@@ -660,50 +737,8 @@
   let-iconTooltip="iconTooltip"
   let-iavNgIf="iavNgIf"
   let-content="content">
-  <mat-expansion-panel
-    [attr.data-opened]="expansionPanel.expanded"
-    [attr.data-mat-expansion-title]="title"
-    hideToggle
-    *ngIf="iavNgIf"
-    #expansionPanel="matExpansionPanel">
-
-    <mat-expansion-panel-header>
-
-      <!-- title -->
-      <mat-panel-title>
-        {{ title }}
-      </mat-panel-title>
-
-      <!-- desc + icon -->
-      <mat-panel-description class="d-flex align-items-center justify-content-end"
-        [matTooltip]="iconTooltip">
-        <span class="mr-3">{{ desc }}</span>
-        <span class="accordion-icon d-inline-flex justify-content-center">
-          <i [class]="iconClass"></i>
-        </span>
-      </mat-panel-description>
-
-    </mat-expansion-panel-header>
-
-    <!-- content -->
-    <ng-template matExpansionPanelContent>
-      <ng-container *ngTemplateOutlet="content; context: { expansionPanel: expansionPanel }">
-      </ng-container>
-    </ng-template>
-  </mat-expansion-panel>
 </ng-template>
 
-<!-- misc dataset (e.g. PLI) -->
-<!-- <ng-template #sidenavDsPreviewTmpl let-file="file" let-filename="filename" let-datasetId="datasetId">
-  <div class="w-100 flex-grow-1 d-flex flex-column">
-
-    Previewing misc dataset
-
-    <ng-container *ngTemplateOutlet="collapseBtn">
-    </ng-container>
-  </div>
-</ng-template> -->
-
 <!-- select region error... for whatever reason -->
 <ng-template #selectRegionErrorTmpl>
   SELECT REGION ERROR
@@ -713,41 +748,42 @@
 <!-- multi region tmpl -->
 <ng-template #multiRegionTmpl let-regions="regions">
   <ng-template [ngIf]="regions.length > 0" [ngIfElse]="regionPlaceholderTmpl">
-    <region-menu
-      [region]="{ name: CONST.MULTI_REGION_SELECTION }"
-      class="bs-border-box ml-15px-n mr-15px-n mat-elevation-z4">
-    </region-menu>
 
     <!-- other regions detail accordion -->
     <mat-accordion class="bs-border-box ml-15px-n mr-15px-n mt-2">
 
       <!-- Multi regions include -->
-      <ng-template #multiRegionInclTmpl>
-
-        <mat-chip *ngFor="let r of regions"
-          iav-region
-          [region]="r"
-          class="m-1"
-          [ngClass]="{
-            'darktheme':regionDirective.rgbDarkmode === true,
-            'lighttheme': regionDirective.rgbDarkmode === false
-          }"
-          [style.backgroundColor]="regionDirective.rgbString"
-          #regionDirective="iavRegion">
-          <span class="iv-custom-comp text text-truncate d-inline">
-            {{ r.name }}
-          </span>
-        </mat-chip>
-      </ng-template>
 
-      <ng-container *ngTemplateOutlet="ngMatAccordionTmpl; context: {
-        title: 'Brain regions',
-        desc: regions.length,
-        iconClass: 'fas fa-brain',
-        iavNgIf: true,
-        content: multiRegionInclTmpl
-      }">
-      </ng-container>
+      <mat-expansion-panel
+        [attr.data-opened]="expansionPanel.expanded"
+        [attr.data-mat-expansion-title]="'Brain regions'"
+        hideToggle
+        #expansionPanel="matExpansionPanel">
+
+        <mat-expansion-panel-header>
+
+          <!-- title -->
+          <mat-panel-title>
+            Brain regions
+          </mat-panel-title>
+
+          <!-- desc + icon -->
+          <mat-panel-description class="d-flex align-items-center justify-content-end">
+            <span class="mr-3">{{ regions.length }}</span>
+            <span class="accordion-icon d-inline-flex justify-content-center">
+              <i class="fas fa-brain"></i>
+            </span>
+          </mat-panel-description>
+
+        </mat-expansion-panel-header>
+
+        <!-- content -->
+        <ng-template matExpansionPanelContent>
+          
+          <!-- TODO use actual region chip in sapiViews/core/region/chip -->
+          SOMETHING
+        </ng-template>
+      </mat-expansion-panel>
 
     </mat-accordion>
   </ng-template>
@@ -781,7 +817,7 @@
 
 <!-- context menu template -->
 <ng-template #viewerCtxMenuTmpl let-tmplRefs="tmplRefs">
-  <mat-card class="p-0 d-flex flex-column"
+  <mat-card class="sxplr-p-0 d-flex flex-column"
     [iav-key-listener]="[{type: 'keydown', target: 'document', capture: true}]"
     (iav-key-event)="disposeCtxMenu()"
     (iav-outsideClick)="disposeCtxMenu()">
@@ -807,6 +843,7 @@
   </mat-card>
 </ng-template>
 
+
 <!-- viewer status ctx menu -->
 <ng-template #viewerStatusCtxMenu let-data>
   <mat-list>
@@ -818,7 +855,7 @@
       <ng-container *ngSwitchCase="'nehuba'">
         <mat-list-item>
           <span mat-line>
-            {{ data.context.payload.mouse.real | nmToMm | addUnitAndJoin : '' }} (mm)
+            {{ data.context.payload.mouse.real | nmToMm | numbers | addUnitAndJoin : '' }} (mm)
           </span>
           <span mat-line class="text-muted">
             <i class="fas fa-map"></i>
@@ -853,27 +890,148 @@
   </mat-list>
 </ng-template>
 
+
+<!-- viewer state hover ctx menu -->
 <ng-template #viewerStatusRegionCtxMenu let-data>
   <!-- hovered ROIs -->
   <mat-list>
-    <mat-list-item *ngFor="let hoveredR of data.metadata.hoveredRegions; let first = first">
+    <mat-list-item *ngFor="let region of data.metadata.hoveredRegions; let first = first">
       <mat-divider class="top-0" *ngIf="!first"></mat-divider>
-      <span mat-line>
-        {{ hoveredR.displayName || hoveredR.name }}
-      </span>
-      <span mat-line class="text-muted">
-        <i class="fas fa-brain"></i>
-        <span>
-          Brain region
-        </span>
-      </span>
 
-      <!-- lookup region -->
-      <button mat-icon-button
-        (click)="selectRoi(hoveredR)"
-        ctx-menu-dismiss>
-        <i class="fas fa-search"></i>
-      </button>
+      <ng-container *ngTemplateOutlet="viewerStateSapiRegionTmpl; context: { $implicit: region }">
+      </ng-container>
+
     </mat-list-item>
   </mat-list>
 </ng-template>
+
+
+<!-- sapi region tmpl -->
+<ng-template #viewerStateSapiRegionTmpl let-region>
+  <span mat-line>
+    {{ region.name }}
+  </span>
+  <span mat-line class="text-muted">
+    <i class="fas fa-brain"></i>
+    <span>
+      Brain region
+    </span>
+  </span>
+
+  <!-- lookup region -->
+  <button mat-icon-button
+    (click)="selectRoi(region)"
+    ctx-menu-dismiss>
+    <i class="fas fa-search"></i>
+  </button>
+</ng-template>
+
+
+<!-- feature tmpls -->
+<ng-template #sapiBaseFeatureTmpl
+  let-backCb="backCb"
+  let-feature="feature">
+
+  <sxplr-sapiviews-core-datasets-dataset class="sxplr-z-2 mat-elevation-z2"
+    [sxplr-sapiviews-core-datasets-dataset-input]="feature">
+
+    <div header>
+      <!-- back btn -->
+      <button mat-button
+        *ngIf="backCb"
+        (click)="backCb()"
+        [attr.aria-label]="ARIA_LABELS.CLOSE"
+        class="sxplr-mb-2"
+        >
+        <i class="fas fa-chevron-left"></i>
+        <span class="ml-1">
+          Back
+        </span>
+      </button>
+    </div>
+
+  </sxplr-sapiviews-core-datasets-dataset>
+  
+  <sxplr-sapiviews-features-entry
+    [sxplr-sapiviews-features-entry-atlas]="selectedAtlas$ | async"
+    [sxplr-sapiviews-features-entry-space]="templateSelected$ | async"
+    [sxplr-sapiviews-features-entry-parcellation]="parcellationSelected$ | async"
+    [sxplr-sapiviews-features-entry-region]="(selectedRegions$ | async)[0]"
+    [sxplr-sapiviews-features-entry-feature]="feature">
+  </sxplr-sapiviews-features-entry>
+</ng-template>
+
+<!-- general feature tmpl -->
+
+<ng-template let-feature="feature" #selectedFeatureTmpl>
+  <!-- TODO differentiate between features (spatial, regional etc) -->
+  
+  <!-- spatial feature tmpl -->
+  <ng-container *ngTemplateOutlet="sapiBaseFeatureTmpl; context: {
+    backCb: clearSelectedFeature.bind(this),
+    feature: feature
+  }">
+  </ng-container>
+
+  <ng-layer-ctl *ngFor="let vol of feature.volumes"
+    class="d-block"
+    [ng-layer-ctl-name]="vol.metadata.fullName"
+    [ng-layer-ctl-src]="vol.data.url"
+    [ng-layer-ctl-transform]="vol.data | getProperty : 'detail' | getProperty: 'neuroglancer/precomputed' | getProperty : 'transform'">
+  </ng-layer-ctl>
+  <ng-template #sapiVOITmpl>
+  </ng-template>
+
+</ng-template>
+
+<ng-template #spatialFeatureListViewTmpl>
+  <div *ngIf="voiQueryDirective && (voiQueryDirective.busy$ | async); else notBusyTmpl" class="fs-200">
+    <spinner-cmp></spinner-cmp>
+  </div>
+
+  <ng-template #notBusyTmpl>
+    <mat-card *ngIf="voiQueryDirective && (voiQueryDirective.features$ | async).length > 0" class="pe-all mat-elevation-z4">
+      <mat-card-title>
+        Volumes of interest
+      </mat-card-title>
+      <mat-card-subtitle class="overflow-hidden">
+        <!-- TODO in future, perhaps encapsulate this as a component? seems like a nature fit in sapiView/space/boundingbox -->
+        <ng-template let-bbox [ngIf]="boundingBoxDirective && (boundingBoxDirective.bbox$ | async | getProperty : 'bbox')" [ngIfElse]="bboxFallbackTmpl">
+          Bounding box: {{ bbox[0] | numbers | json }} - {{ bbox[1] | numbers | json }} mm
+        </ng-template>
+        <ng-template #bboxFallbackTmpl>
+          Found nearby
+        </ng-template>
+        
+      </mat-card-subtitle>
+
+      <mat-divider></mat-divider>
+
+      <ng-template [ngIf]="voiQueryDirective">
+
+        <div *ngFor="let feature of voiQueryDirective.features$ | async"
+          mat-ripple
+          (click)="showDataset(feature)"
+          class="sxplr-custom-cmp hoverable w-100 overflow-hidden text-overflow-ellipses">
+          {{ feature.metadata.fullName }}
+        </div>
+      </ng-template>
+    </mat-card>
+  </ng-template>
+</ng-template>
+
+<div class="d-none"
+  *ngIf="VOI_QUERY_FLAG"
+  sxplr-sapiviews-core-space-boundingbox
+  [sxplr-sapiviews-core-space-boundingbox-atlas]="selectedAtlas$ | async"
+  [sxplr-sapiviews-core-space-boundingbox-space]="templateSelected$ | async"
+  [sxplr-sapiviews-core-space-boundingbox-spec]="viewerCtx$ | async | nehubaVCtxToBbox"
+  #bbox="sxplrSapiViewsCoreSpaceBoundingBox"
+  sxplr-sapiviews-features-voi-query
+  [sxplr-sapiviews-features-voi-query-atlas]="selectedAtlas$ | async"
+  [sxplr-sapiviews-features-voi-query-space]="templateSelected$ | async"
+  [sxplr-sapiviews-features-voi-query-bbox]="bbox.bbox$ | async | getProperty : 'bbox'"
+  (sxplr-sapiviews-features-voi-query-onclick)="showDataset($event)"
+  #voiFeatures="sxplrSapiViewsFeaturesVoiQuery">
+
+</div>
diff --git a/src/viewerModule/viewerInternalState.service.ts b/src/viewerModule/viewerInternalState.service.ts
index 3985a2f65e45663166adfa73e0d1a500adeafcf9..745ff8ecf94620babbb2c1c988366d9f89d03e54 100644
--- a/src/viewerModule/viewerInternalState.service.ts
+++ b/src/viewerModule/viewerInternalState.service.ts
@@ -33,7 +33,7 @@ export class ViewerInternalStateSvc{
 
   private registeredEmitter: TViewerInternalStateEmitter
 
-  applyInternalState<T>(arg: TInteralStatePayload<T>){
+  applyInternalState<T>(arg: TInteralStatePayload<T>): void{
     if (!this.registeredEmitter) {
       throw new Error(`No emitter registered. Aborting.`)
     }
@@ -49,7 +49,7 @@ export class ViewerInternalStateSvc{
       done: () => this.deregisterEmitter(emitter)
     }
   }
-  deregisterEmitter(emitter: TViewerInternalStateEmitter){
+  deregisterEmitter(emitter: TViewerInternalStateEmitter): void{
     if (emitter === this.registeredEmitter) {
       this.viewerInternalState$.next(null)
       this.registeredEmitter = null
diff --git a/src/viewerModule/viewerStateBreadCrumb/breadcrumb/breadcrumb.component.ts b/src/viewerModule/viewerStateBreadCrumb/breadcrumb/breadcrumb.component.ts
deleted file mode 100644
index 5a4bea443845687db10084cd253a18ec1c44bb17..0000000000000000000000000000000000000000
--- a/src/viewerModule/viewerStateBreadCrumb/breadcrumb/breadcrumb.component.ts
+++ /dev/null
@@ -1,141 +0,0 @@
-import { Component, EventEmitter, Output, Pipe, PipeTransform } from "@angular/core";
-import { IQuickTourData } from "src/ui/quickTour";
-import { CONST, ARIA_LABELS, QUICKTOUR_DESC } from 'common/constants'
-import { select, Store } from "@ngrx/store";
-import { viewerStateContextedSelectedRegionsSelector, viewerStateGetOverlayingAdditionalParcellations, viewerStateParcVersionSelector, viewerStateSelectedParcellationSelector } from "src/services/state/viewerState/selectors";
-import { distinctUntilChanged, map } from "rxjs/operators";
-import { viewerStateHelperSelectParcellationWithId, viewerStateRemoveAdditionalLayer, viewerStateSetSelectedRegions } from "src/services/state/viewerState.store.helper";
-import { ngViewerActionClearView, ngViewerSelectorClearViewEntries } from "src/services/state/ngViewerState.store.helper";
-import { OVERWRITE_SHOW_DATASET_DIALOG_TOKEN } from "src/util/interfaces";
-import { TDatainfosDetail, TSimpleInfo } from "src/util/siibraApiConstants/types";
-
-@Component({
-  selector: 'viewer-state-breadcrumb',
-  templateUrl: './breadcrumb.template.html',
-  styleUrls: [
-    './breadcrumb.style.css'
-  ],
-  providers: [
-    {
-      provide: OVERWRITE_SHOW_DATASET_DIALOG_TOKEN,
-      useValue: null
-    }
-  ]
-})
-
-export class ViewerStateBreadCrumb {
-
-  public CONST = CONST
-  public ARIA_LABELS = ARIA_LABELS
-
-  @Output('on-item-click')
-  onChipClick = new EventEmitter()
-
-  public quickTourChips: IQuickTourData = {
-    order: 5,
-    description: QUICKTOUR_DESC.CHIPS,
-  }
-
-  public clearViewKeys$ = this.store$.pipe(
-    select(ngViewerSelectorClearViewEntries)
-  )
-
-  public selectedAdditionalLayers$ = this.store$.pipe(
-    select(viewerStateGetOverlayingAdditionalParcellations),
-  )
-
-  public parcellationSelected$ = this.store$.pipe(
-    select(viewerStateSelectedParcellationSelector),
-    distinctUntilChanged(),
-  )
-
-  public selectedRegions$ = this.store$.pipe(
-    select(viewerStateContextedSelectedRegionsSelector),
-    distinctUntilChanged(),
-  )
-
-  public selectedLayerVersions$ = this.store$.pipe(
-    select(viewerStateParcVersionSelector),
-    map(arr => arr.map(item => {
-      const overwrittenName = item['@version'] && item['@version']['name']
-      return overwrittenName
-        ? { ...item, displayName: overwrittenName }
-        : item
-    }))
-  )
-
-
-  constructor(private store$: Store<any>){
-
-  }
-
-  handleChipClick(){
-    this.onChipClick.emit(null)
-  }
-
-  public clearSelectedRegions(){
-    this.store$.dispatch(
-      viewerStateSetSelectedRegions({
-        selectRegions: []
-      })
-    )
-  }
-
-  public unsetClearViewByKey(key: string){
-    this.store$.dispatch(
-      ngViewerActionClearView({ payload: {
-        [key]: false
-      }})
-    )
-  }
-
-  public clearAdditionalLayer(layer: { ['@id']: string }){
-    this.store$.dispatch(
-      viewerStateRemoveAdditionalLayer({
-        payload: layer
-      })
-    )
-  }
-
-  public selectParcellation(parc: any) {
-    this.store$.dispatch(
-      viewerStateHelperSelectParcellationWithId({
-        payload: parc
-      })
-    )
-  }
-
-  public bindFns(fns){
-    return () => {
-      for (const [ fn, ...arg] of fns) {
-        fn(...arg)
-      }
-    }
-  }
-
-}
-
-@Pipe({
-  name: 'originalDatainfoPriorityPipe'
-})
-
-export class OriginalDatainfoPipe implements PipeTransform{
-  public transform(arr: (TSimpleInfo | TDatainfosDetail)[]): TDatainfosDetail[]{
-    const detailedInfos = arr.filter(item => item['@type'] === 'minds/core/dataset/v1.0.0') as TDatainfosDetail[]
-    const simpleInfos = arr.filter(item => item['@type'] === 'fzj/tmp/simpleOriginInfo/v0.0.1') as TSimpleInfo[]
-
-    if (detailedInfos.length > 0) return detailedInfos
-    if (simpleInfos.length > 0) {
-      return arr.map(d => {
-        return {
-          '@type': 'minds/core/dataset/v1.0.0',
-          name: d.name,
-          description: d.name,
-          urls: [],
-          useClassicUi: false
-        }
-      })
-    }
-    return []
-  }
-}
diff --git a/src/viewerModule/viewerStateBreadCrumb/breadcrumb/breadcrumb.template.html b/src/viewerModule/viewerStateBreadCrumb/breadcrumb/breadcrumb.template.html
deleted file mode 100644
index a80037b10ee223444d42bfe1f144872b82fa0fa2..0000000000000000000000000000000000000000
--- a/src/viewerModule/viewerStateBreadCrumb/breadcrumb/breadcrumb.template.html
+++ /dev/null
@@ -1,206 +0,0 @@
-<mat-chip-list
-  quick-tour
-  [quick-tour-description]="quickTourChips.description"
-  [quick-tour-order]="quickTourChips.order">
-
-  <!-- additional layer -->
-
-  <ng-container>
-    <ng-container *ngTemplateOutlet="currParcellationTmpl; context: {
-      addParc: (selectedAdditionalLayers$ | async),
-      parc: (parcellationSelected$ | async)
-    }">
-    </ng-container>
-  </ng-container>
-
-  <!-- any selected region(s) -->
-  <ng-container>
-    <ng-container *ngTemplateOutlet="selectedRegionTmpl">
-    </ng-container>
-  </ng-container>
-</mat-chip-list>
-
-
-<!-- parcellation chip / region chip -->
-<ng-template #currParcellationTmpl let-parc="parc" let-addParc="addParc">
-  <div [matMenuTriggerFor]="layerVersionMenu"
-    [matMenuTriggerData]="{ layerVersionMenuTrigger: layerVersionMenuTrigger }"
-    #layerVersionMenuTrigger="matMenuTrigger">
-
-    <ng-template [ngIf]="addParc.length > 0" [ngIfElse]="defaultParcTmpl">
-      <ng-container *ngFor="let p of addParc">
-        <ng-container *ngTemplateOutlet="chipTmpl; context: {
-          parcel: p,
-          selected: true,
-          dismissable: true,
-          ariaLabel: ARIA_LABELS.PARC_VER_SELECT,
-          onclick: layerVersionMenuTrigger.toggleMenu.bind(layerVersionMenuTrigger)
-        }">
-        </ng-container>
-      </ng-container>
-    </ng-template>
-    <ng-template #defaultParcTmpl>
-      <ng-template [ngIf]="parc">
-
-        <ng-container *ngTemplateOutlet="chipTmpl; context: {
-          parcel: parc,
-          selected: false,
-          dismissable: false,
-          ariaLabel: ARIA_LABELS.PARC_VER_SELECT,
-          onclick: layerVersionMenuTrigger.toggleMenu.bind(layerVersionMenuTrigger)
-        }">
-        </ng-container>
-      </ng-template>
-    </ng-template>
-  </div>
-</ng-template>
-
-
-<ng-template #selectedRegionTmpl>
-
-  <!-- regions chip -->
-  <ng-template [ngIf]="selectedRegions$ | async" let-selectedRegions="ngIf">
-    <!-- if regions.length > 1 -->
-    <!-- use group chip -->
-    <ng-template [ngIf]="selectedRegions.length > 1" [ngIfElse]="singleRegionChipTmpl">
-      <mat-chip
-        color="primary"
-        selected
-        (click)="handleChipClick()"
-        class="pe-all position-relative z-index-1 ml-8-n">
-        <span class="iv-custom-comp text text-truncate d-inline pl-4">
-          {{ CONST.MULTI_REGION_SELECTION }}
-        </span>
-        <mat-icon
-          (click)="clearSelectedRegions()"
-          fontSet="fas"
-          iav-stop="click"
-          fontIcon="fa-times">
-        </mat-icon>
-      </mat-chip>
-    </ng-template>
-
-    <!-- if reginos.lengt === 1 -->
-    <!-- use single region chip -->
-    <ng-template #singleRegionChipTmpl>
-      <ng-container *ngFor="let r of selectedRegions">
-
-        <!-- region chip for discrete map -->
-        <mat-chip
-          (click)="handleChipClick()"
-          [region]="r"
-          class="pe-all position-relative z-index-1 ml-8-n"
-          [ngClass]="{
-            'darktheme':regionDirective.rgbDarkmode === true,
-            'lighttheme': regionDirective.rgbDarkmode === false
-          }"
-          [style.backgroundColor]="regionDirective.rgbString"
-          iav-region
-          #regionDirective="iavRegion">
-          <span class="iv-custom-comp text text-truncate d-inline pl-4">
-            {{ r.name }}
-          </span>
-          <mat-icon
-            class="iv-custom-comp text"
-            (click)="clearSelectedRegions()"
-            fontSet="fas"
-            iav-stop="click"
-            fontIcon="fa-times">
-          </mat-icon>
-        </mat-chip>
-
-        <!-- chips for previewing origin datasets/continous map -->
-        <ng-container *ngFor="let originDataset of (r.originDatasets || []); let index = index">
-
-          <mat-chip *ngFor="let key of clearViewKeys$ | async"
-            (click)="handleChipClick()"
-            class="pe-all position-relative ml-8-n">
-            <span class="pl-4">
-              {{ key }}
-            </span>
-            <mat-icon (click)="unsetClearViewByKey(key)"
-              fontSet="fas"
-              iav-stop="click"
-              fontIcon="fa-times">
-
-            </mat-icon>
-          </mat-chip>
-        </ng-container>
-
-      </ng-container>
-    </ng-template>
-  </ng-template>
-
-</ng-template>
-
-<!-- layer version selector -->
-<mat-menu #layerVersionMenu
-  class="bg-none box-shadow-none"
-  [aria-label]="ARIA_LABELS.PARC_VER_CONTAINER"
-  [hasBackdrop]="false">
-  <ng-template matMenuContent let-layerVersionMenuTrigger="layerVersionMenuTrigger">
-    <div (iav-outsideClick)="layerVersionMenuTrigger.closeMenu()">
-      <ng-container *ngFor="let parcVer of selectedLayerVersions$ | async">
-        <ng-container *ngIf="parcellationSelected$ | async as selectedParcellation">
-
-          <ng-container *ngTemplateOutlet="chipTmpl; context: {
-            parcel: parcVer,
-            selected: selectedParcellation['@id'] === parcVer['@id'],
-            dismissable: false,
-            class: 'w-100',
-            ariaLabel: parcVer.displayName || parcVer.name,
-            onclick: bindFns([
-              [ selectParcellation.bind(this), parcVer ],
-              [ layerVersionMenuTrigger.closeMenu.bind(layerVersionMenuTrigger) ]
-            ])
-          }">
-          </ng-container>
-        </ng-container>
-        <div class="mt-1"></div>
-      </ng-container>
-    </div>
-  </ng-template>
-</mat-menu>
-
-
-<ng-template #chipTmpl
-  let-parcel="parcel"
-  let-selected="selected"
-  let-dismissable="dismissable"
-  let-chipClass="class"
-  let-ariaLabel="ariaLabel"
-  let-onclick="onclick">
-  <mat-chip class="pe-all position-relative z-index-2 d-inline-flex justify-content-between"
-    [ngClass]="chipClass"
-    [attr.aria-label]="ariaLabel"
-    (click)="onclick && onclick()"
-    [selected]="selected">
-
-    <span class="ws-no-wrap">
-      {{ parcel?.groupName ? (parcel?.groupName + ' - ') : '' }}{{ parcel && (parcel.displayName || parcel.name) }}
-    </span>
-
-    <!-- info icon -->
-    <ng-container *ngFor="let originDatainfo of (parcel.originDatainfos | originalDatainfoPriorityPipe)">
-      
-      <mat-icon
-        fontSet="fas"
-        fontIcon="fa-info-circle"
-        iav-stop="click"
-        iav-dataset-show-dataset-dialog
-        [iav-dataset-show-dataset-dialog-name]="originDatainfo.name"
-        [iav-dataset-show-dataset-dialog-description]="originDatainfo.description"
-        [iav-dataset-show-dataset-dialog-urls]="originDatainfo.urls">
-      </mat-icon>
-
-    </ng-container>
-
-    <!-- dismiss icon -->
-    <mat-icon
-      *ngIf="dismissable"
-      (click)="clearAdditionalLayer(parcel); $event.stopPropagation()"
-      fontSet="fas"
-      fontIcon="fa-times">
-    </mat-icon>
-  </mat-chip>
-</ng-template>
diff --git a/src/viewerModule/viewerStateBreadCrumb/module.ts b/src/viewerModule/viewerStateBreadCrumb/module.ts
deleted file mode 100644
index ba53571ed934fe530330bd5b96fa2064484151e3..0000000000000000000000000000000000000000
--- a/src/viewerModule/viewerStateBreadCrumb/module.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-import { CommonModule } from "@angular/common";
-import { NgModule } from "@angular/core";
-import { ParcellationRegionModule } from "src/atlasComponents/parcellationRegion";
-import { KgDatasetModule } from "src/atlasComponents/regionalFeatures/bsFeatures/kgDataset";
-import { QuickTourModule } from "src/ui/quickTour";
-import { AngularMaterialModule } from "src/sharedModules";
-import { UtilModule } from "src/util";
-import { OriginalDatainfoPipe, ViewerStateBreadCrumb } from "./breadcrumb/breadcrumb.component";
-
-@NgModule({
-  imports: [
-    CommonModule,
-    AngularMaterialModule,
-    QuickTourModule,
-    ParcellationRegionModule,
-    KgDatasetModule,
-    UtilModule,
-  ],
-  declarations: [
-    ViewerStateBreadCrumb,
-    OriginalDatainfoPipe,
-  ],
-  exports: [
-    ViewerStateBreadCrumb,
-  ],
-  providers:[
-  ]
-})
-
-export class ViewerStateBreadCrumbModule{}
\ No newline at end of file
diff --git a/src/widget/constants.ts b/src/widget/constants.ts
index e4070494f940a01fb0dce8874eba63a665c1bdfd..a779b079ad01afafeff6bf9e52f0675e4fdad6e4 100644
--- a/src/widget/constants.ts
+++ b/src/widget/constants.ts
@@ -20,3 +20,6 @@ interface TypeActionWidgetReturnVal<T>{
 
 export type TypeActionToWidget<T> = (type: EnumActionToWidget, obj: T, option: IActionWidgetOption) => TypeActionWidgetReturnVal<T>
 
+export const WIDGET_PORTAL_TOKEN = new InjectionToken<Record<string, unknown>>("WIDGET_PORTAL_TOKEN")
+
+export const RM_WIDGET = new InjectionToken('RM_WIDGET')
\ No newline at end of file
diff --git a/src/widget/index.ts b/src/widget/index.ts
index c322c25f3d4ca885b9812367b0e55202a67da575..1cabfcd56301fe9cc9cc32103a33e3db51d49c62 100644
--- a/src/widget/index.ts
+++ b/src/widget/index.ts
@@ -1,4 +1,4 @@
 export { WidgetModule } from './widget.module'
-export { WidgetUnit } from './widgetUnit/widgetUnit.component'
-export { IWidgetOptionsInterface, WidgetServices } from './widgetService.service'
+export { WidgetPortal } from "./widgetPortal/widgetPortal.component"
+export { WidgetService } from "./service"
 export { EnumActionToWidget, TypeActionToWidget, IActionWidgetOption } from './constants'
diff --git a/src/widget/service.ts b/src/widget/service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9430269418731ceb3b57810b54b26ef155ef7ef0
--- /dev/null
+++ b/src/widget/service.ts
@@ -0,0 +1,56 @@
+import { ComponentPortal } from "@angular/cdk/portal";
+import { ComponentFactory, ComponentFactoryResolver, ComponentRef, Injectable, Injector, ViewContainerRef } from "@angular/core";
+import { RM_WIDGET } from "./constants";
+import { WidgetPortal } from "./widgetPortal/widgetPortal.component";
+
+@Injectable({
+  providedIn: 'root'
+})
+
+export class WidgetService {
+  
+  public vcr: ViewContainerRef
+
+  private viewRefMap = new Map<WidgetPortal<unknown>, ComponentRef<WidgetPortal<unknown>>>()
+  private cf: ComponentFactory<WidgetPortal<unknown>>
+  
+  constructor(cfr: ComponentFactoryResolver){
+    this.cf = cfr.resolveComponentFactory(WidgetPortal)
+  }
+
+  public addNewWidget<T>(Component: new (...arg: any) => T, injector: Injector): WidgetPortal<T> {
+    const inj = Injector.create({
+      providers: [{
+        provide: RM_WIDGET,
+        useValue: (cmp: WidgetPortal<T>) => this.rmWidget(cmp)
+      }],
+      parent: injector
+    })
+    const widgetPortal = this.vcr.createComponent(this.cf, 0, inj) as ComponentRef<WidgetPortal<T>>
+    const cmpPortal = new ComponentPortal<T>(Component, this.vcr, inj)
+    
+    this.viewRefMap.set(widgetPortal.instance, widgetPortal)
+
+    widgetPortal.instance.portal = cmpPortal
+    return widgetPortal.instance
+  }
+
+  public rmWidget(wdg: WidgetPortal<unknown>) {
+    
+    /**
+     * if wdg no longer exist in viewRefMap, it should already been deleted.
+     */
+    if (!this.viewRefMap.has(wdg)) {
+      return
+    }
+    const hostView = this.viewRefMap.get(wdg).hostView
+
+    this.viewRefMap.delete(wdg)
+    
+    const idx = this.vcr.indexOf(hostView)
+    if (idx < 0) {
+      console.warn(`idx less than 0, cannot remove`)
+    }
+    this.vcr.remove(idx)
+  }
+}
diff --git a/src/widget/widget.module.ts b/src/widget/widget.module.ts
index dd6b7960a9dfd4fbe32aa66b617f12a73a547df6..138ed66482a78ad917959b416aca333c415c09b0 100644
--- a/src/widget/widget.module.ts
+++ b/src/widget/widget.module.ts
@@ -1,27 +1,29 @@
 import { NgModule } from "@angular/core";
-import { WidgetUnit } from "./widgetUnit/widgetUnit.component";
-import { WidgetServices } from "./widgetService.service";
-import { AngularMaterialModule } from "src/sharedModules";
 import { CommonModule } from "@angular/common";
 import { ComponentsModule } from "src/components";
+import { WidgetCanvas } from "./widgetCanvas.directive";
+import { WidgetPortal } from "./widgetPortal/widgetPortal.component";
+import { MatCardModule } from "@angular/material/card";
+import { DragDropModule } from "@angular/cdk/drag-drop";
+import { MatButtonModule } from "@angular/material/button";
+import { PortalModule } from "@angular/cdk/portal";
 
 @NgModule({
   imports:[
-    AngularMaterialModule,
+    MatCardModule,
+    DragDropModule,
+    MatButtonModule,
+    PortalModule,
     CommonModule,
     ComponentsModule,
   ],
   declarations: [
-    WidgetUnit
-  ],
-  entryComponents: [
-    WidgetUnit
-  ],
-  providers: [
-    WidgetServices,
+    WidgetCanvas,
+    WidgetPortal,
   ],
+  providers: [],
   exports: [
-    WidgetUnit
+    WidgetCanvas,
   ]
 })
 
diff --git a/src/widget/widgetCanvas.directive.ts b/src/widget/widgetCanvas.directive.ts
new file mode 100644
index 0000000000000000000000000000000000000000..206548386ac1bb178d7987b47390aeba95bcf016
--- /dev/null
+++ b/src/widget/widgetCanvas.directive.ts
@@ -0,0 +1,15 @@
+import { Directive, ViewContainerRef } from "@angular/core";
+import { WidgetService } from "./service";
+
+@Directive({
+  selector: `[widget-canvas]`
+})
+
+export class WidgetCanvas {
+  constructor(
+    wSvc: WidgetService,
+    vcr: ViewContainerRef,
+  ){
+    wSvc.vcr = vcr
+  }
+}
diff --git a/src/widget/widgetPortal/widgetPortal.component.ts b/src/widget/widgetPortal/widgetPortal.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5e5cc05fa5fa6f15a139fb415097c3fff4078c48
--- /dev/null
+++ b/src/widget/widgetPortal/widgetPortal.component.ts
@@ -0,0 +1,41 @@
+import { ComponentPortal } from "@angular/cdk/portal";
+import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, Optional } from "@angular/core";
+import { RM_WIDGET } from "../constants";
+
+@Component({
+  selector: 'sxplr-widget-portal',
+  templateUrl: './widgetPortal.template.html',
+  styleUrls: [
+    './widgetPortal.style.css'
+  ],
+  changeDetection: ChangeDetectionStrategy.OnPush
+})
+
+export class WidgetPortal<T>{
+
+  portal: ComponentPortal<T>
+  
+  private _name: string
+  get name() {
+    return this._name
+  }
+  set name(val) {
+    this._name = val
+    this.cdr.markForCheck()
+  }
+
+  defaultPosition = {
+    x: 200,
+    y: 200,
+  }
+
+  constructor(
+    private cdr: ChangeDetectorRef,
+    @Optional() @Inject(RM_WIDGET) private rmWidget: (inst: unknown) => void
+  ){
+    
+  }
+  exit(){
+    if (this.rmWidget) this.rmWidget(this)
+  }
+}
diff --git a/src/widget/widgetPortal/widgetPortal.style.css b/src/widget/widgetPortal/widgetPortal.style.css
new file mode 100644
index 0000000000000000000000000000000000000000..12e9c809623d27f95eef3986c869ed9966496328
--- /dev/null
+++ b/src/widget/widgetPortal/widgetPortal.style.css
@@ -0,0 +1,56 @@
+:host
+{
+  pointer-events: none;
+  display: block;
+  max-width: 24rem;
+}
+
+mat-card
+{
+  pointer-events: all;
+  max-width: 36vw;
+  height: 36rem;
+  max-height: 90vh;
+}
+
+mat-card-content
+{
+  height: 100%;
+  width: 100%;
+  display: flex;
+  flex-direction: column;
+}
+
+.widget-portal-header
+{
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+
+.widget-portal-content
+{
+  flex-grow: 1;
+}
+
+.hover-grab
+{
+  opacity: 0.5;
+  transition: opacity 200ms ease-in-out;
+  cursor: move;
+}
+
+.hover-grab:hover
+{
+  opacity: 1.0;
+}
+
+.widget-grab-handle
+{
+  margin-right:1rem;
+}
+
+.widget-name
+{
+  flex-grow: 1;
+}
diff --git a/src/widget/widgetPortal/widgetPortal.template.html b/src/widget/widgetPortal/widgetPortal.template.html
new file mode 100644
index 0000000000000000000000000000000000000000..7aff890d9ecab44127f5b70a0df3a5ba0f5c305c
--- /dev/null
+++ b/src/widget/widgetPortal/widgetPortal.template.html
@@ -0,0 +1,22 @@
+<mat-card cdkDrag [cdkDragFreeDragPosition]="defaultPosition">
+  <mat-card-content>
+    <div class="widget-portal-header" cdkDragHandle>
+      <span class="hover-grab widget-grab-handle">
+        <i class="fas fa-grip-vertical"></i>
+      </span>
+
+      <span *ngIf="name" class="widget-name">
+        {{ name }}
+      </span>
+
+      <button mat-icon-button (click)="exit()">
+        <i class="fas fa-times"></i>
+      </button>
+    </div>
+    
+    <div class="widget-portal-content">
+      <ng-template [cdkPortalOutlet]="portal">
+      </ng-template>
+    </div>
+  </mat-card-content>
+</mat-card>
diff --git a/src/widget/widgetService.service.ts b/src/widget/widgetService.service.ts
deleted file mode 100644
index 89d1989613a35c1973b892a1627abe2c32361d51..0000000000000000000000000000000000000000
--- a/src/widget/widgetService.service.ts
+++ /dev/null
@@ -1,217 +0,0 @@
-import { ComponentFactory, ComponentFactoryResolver, ComponentRef, Injectable, Injector, OnDestroy, ViewContainerRef } from "@angular/core";
-import { BehaviorSubject, Subscription } from "rxjs";
-import { LoggingService } from "src/logging";
-import { WidgetUnit } from "./widgetUnit/widgetUnit.component";
-
-@Injectable({
-  providedIn : 'root',
-})
-
-export class WidgetServices implements OnDestroy {
-
-  public floatingContainer: ViewContainerRef
-  public dockedContainer: ViewContainerRef
-  public factoryContainer: ViewContainerRef
-
-  private widgetUnitFactory: ComponentFactory<WidgetUnit>
-  private widgetComponentRefs: Set<ComponentRef<WidgetUnit>> = new Set()
-
-  private clickedListener: Subscription[] = []
-
-  public minimisedWindow$: BehaviorSubject<Set<WidgetUnit>>
-  private minimisedWindow: Set<WidgetUnit> = new Set()
-
-  constructor(
-    private cfr: ComponentFactoryResolver,
-    private injector: Injector,
-    private log: LoggingService,
-  ) {
-    this.widgetUnitFactory = this.cfr.resolveComponentFactory(WidgetUnit)
-    this.minimisedWindow$ = new BehaviorSubject(this.minimisedWindow)
-  }
-
-  private subscriptions: Subscription[] = []
-
-
-  public ngOnDestroy() {
-    while (this.subscriptions.length > 0) {
-      this.subscriptions.pop().unsubscribe()
-    }
-  }
-
-  public clearAllWidgets() {
-    [...this.widgetComponentRefs].forEach((cr: ComponentRef<WidgetUnit>) => {
-      if (!cr.instance.persistency) { cr.destroy() }
-    })
-
-    this.clickedListener.forEach(s => s.unsubscribe())
-  }
-
-  public rename(wu: WidgetUnit, {title, titleHTML}: {title: string, titleHTML: string}) {
-    /**
-     * WARNING: always sanitize before pass to rename fn!
-     */
-    wu.title = title
-    wu.titleHTML = titleHTML
-  }
-
-  public minimise(wu: WidgetUnit) {
-    this.minimisedWindow.add(wu)
-    this.minimisedWindow$.next(new Set(this.minimisedWindow))
-  }
-
-  public isMinimised(wu: WidgetUnit) {
-    return this.minimisedWindow.has(wu)
-  }
-
-  public unminimise(wu: WidgetUnit) {
-    this.minimisedWindow.delete(wu)
-    this.minimisedWindow$.next(new Set(this.minimisedWindow))
-  }
-
-  public addNewWidget(guestComponentRef: ComponentRef<any>, options?: Partial<IWidgetOptionsInterface>): ComponentRef<WidgetUnit> {
-    const component = this.widgetUnitFactory.create(this.injector)
-    const _option = getOption(options)
-
-    // TODO bring back docked state?
-    _option.state = 'floating'
-
-    _option.state === 'floating'
-      ? this.floatingContainer.insert(component.hostView)
-      : _option.state === 'docked'
-        ? this.dockedContainer.insert(component.hostView)
-        : this.floatingContainer.insert(component.hostView)
-
-    if (component.constructor === Error) {
-      throw component
-    } else {
-      const _component = (component as ComponentRef<WidgetUnit>)
-
-      // guestComponentRef
-      // insert view
-      _component.instance.container.insert( guestComponentRef.hostView )
-      // on host destroy, destroy guest
-      _component.onDestroy(() => guestComponentRef.destroy())
-
-      /* programmatic DI */
-      _component.instance.widgetServices = this
-
-      /* common properties */
-      _component.instance.state = _option.state
-      _component.instance.exitable = _option.exitable
-      _component.instance.title = _option.title
-      _component.instance.persistency = _option.persistency
-      _component.instance.titleHTML = _option.titleHTML
-
-      /* internal properties, used for changing state */
-      _component.instance.guestComponentRef = guestComponentRef
-
-      if (_option.state === 'floating') {
-        let position = [400, 100] as [number, number]
-        while ([...this.widgetComponentRefs].some(widget =>
-          widget.instance.state === 'floating' &&
-          widget.instance.position.every((v, idx) => v === position[idx]))) {
-          position = position.map(v => v + 10) as [number, number]
-        }
-        _component.instance.position = position
-      }
-
-      /* set width and height. or else floating components will obstruct viewers */
-      _component.instance.setWidthHeight()
-
-      this.widgetComponentRefs.add( _component )
-      _component.onDestroy(() => this.minimisedWindow.delete(_component.instance))
-
-      this.clickedListener.push(
-        _component.instance.clickedEmitter.subscribe((widgetUnit: WidgetUnit) => {
-          /**
-           * TODO this operation
-           */
-          if (widgetUnit.state !== 'floating') {
-            return
-          }
-          const foundWidgetCompRef = [...this.widgetComponentRefs].find(wr => wr.instance === widgetUnit)
-          if (!foundWidgetCompRef) {
-            return
-          }
-          const idx = this.floatingContainer.indexOf(foundWidgetCompRef.hostView)
-          if (idx === this.floatingContainer.length - 1 ) {
-            return
-          }
-          this.floatingContainer.detach(idx)
-          this.floatingContainer.insert(foundWidgetCompRef.hostView)
-        }),
-      )
-
-      return _component
-    }
-  }
-
-  public changeState(widgetUnit: WidgetUnit, options: IWidgetOptionsInterface) {
-    const widgetRef = [...this.widgetComponentRefs].find(cr => cr.instance === widgetUnit)
-    if (widgetRef) {
-      this.widgetComponentRefs.delete(widgetRef)
-      widgetRef.instance.container.detach( 0 )
-      const guestComopnent = widgetRef.instance.guestComponentRef
-      this.addNewWidget(guestComopnent, options)
-
-      widgetRef.destroy()
-    } else {
-      this.log.warn('widgetref not found')
-    }
-  }
-
-  public exitWidget(widgetUnit: WidgetUnit) {
-    const widgetRef = [...this.widgetComponentRefs].find(cr => cr.instance === widgetUnit)
-    if (widgetRef) {
-      widgetRef.destroy()
-      this.widgetComponentRefs.delete(widgetRef)
-    } else {
-      this.log.warn('widgetref not found')
-    }
-  }
-
-  public dockAllWidgets() {
-    /* nb cannot directly iterate the set, as the set will be updated and create and infinite loop */
-    [...this.widgetComponentRefs].forEach(cr => cr.instance.dock())
-  }
-
-  public floatAllWidgets() {
-    [...this.widgetComponentRefs].forEach(cr => cr.instance.undock())
-  }
-}
-
-function safeGetSingle(obj: any, arg: string) {
-  return typeof obj === 'object' && obj !== null && typeof arg === 'string'
-    ? obj[arg]
-    : null
-}
-
-function safeGet(obj: any, ...args: string[]) {
-  let _obj = Object.assign({}, obj)
-  while (args.length > 0) {
-    const arg = args.shift()
-    _obj = safeGetSingle(_obj, arg)
-  }
-  return _obj
-}
-
-function getOption(option?: Partial<IWidgetOptionsInterface>): IWidgetOptionsInterface {
-  return{
-    exitable : safeGet(option, 'exitable') !== null
-      ? safeGet(option, 'exitable')
-      : true,
-    state : safeGet(option, 'state') || 'floating',
-    title : safeGet(option, 'title') || 'Untitled',
-    persistency : safeGet(option, 'persistency') || false,
-    titleHTML: safeGet(option, 'titleHTML') || null,
-  }
-}
-
-export interface IWidgetOptionsInterface {
-  title?: string
-  state?: 'docked' | 'floating'
-  exitable?: boolean
-  persistency?: boolean
-  titleHTML?: string
-}
diff --git a/src/widget/widgetUnit/widgetUnit.component.ts b/src/widget/widgetUnit/widgetUnit.component.ts
deleted file mode 100644
index e77afb49abca1ac73c26ef58ee37e34be53a2d03..0000000000000000000000000000000000000000
--- a/src/widget/widgetUnit/widgetUnit.component.ts
+++ /dev/null
@@ -1,180 +0,0 @@
-import { Component, ComponentRef, EventEmitter, HostBinding, HostListener, Input, OnDestroy, OnInit, Output, ViewChild, ViewContainerRef } from "@angular/core";
-
-import { Observable, Subscription } from "rxjs";
-import { map } from "rxjs/operators";
-import { WidgetServices } from "../widgetService.service";
-
-@Component({
-  templateUrl : './widgetUnit.template.html',
-  styleUrls : [
-    `./widgetUnit.style.css`,
-  ],
-})
-
-export class WidgetUnit implements OnInit, OnDestroy {
-  @ViewChild('container', {read: ViewContainerRef, static: true}) public container: ViewContainerRef
-
-  @HostBinding('attr.state')
-  public state: 'docked' | 'floating' = 'docked'
-
-  @HostBinding('style.width')
-  public width: string = this.state === 'docked' ? null : '0px'
-
-  @HostBinding('style.height')
-  public height: string = this.state === 'docked' ? null : '0px'
-
-  @HostBinding('style.display')
-  public isMinimised: string
-
-  public isMinimised$: Observable<boolean>
-
-  public hoverableConfig = {
-    translateY: -1,
-  }
-
-  /**
-   * Timed alternates of blinkOn property should result in attention grabbing blink behaviour
-   */
-  private _blinkOn: boolean = false
-  get blinkOn() {
-    return this._blinkOn
-  }
-
-  set blinkOn(val: boolean) {
-    this._blinkOn = !!val
-  }
-
-  get showProgress() {
-    return this.progressIndicator !== null
-  }
-
-  /**
-   * Some plugins may like to show progress indicator for long running processes
-   * If null, no progress is running
-   * This value should be between 0 and 1
-   */
-  private _progressIndicator: number = null
-  get progressIndicator() {
-    return this._progressIndicator
-  }
-
-  set progressIndicator(val: number) {
-    if (isNaN(val)) {
-      this._progressIndicator = null
-      return
-    }
-    if (val < 0) {
-      this._progressIndicator = 0
-      return
-    }
-    if (val > 1) {
-      this._progressIndicator = 1
-      return
-    }
-    this._progressIndicator = val
-  }
-
-  public canBeDocked: boolean = false
-  @HostListener('mousedown')
-  public clicked() {
-    this.clickedEmitter.emit(this)
-    this.blinkOn = false
-  }
-
-  @Input() public title: string = 'Untitled'
-
-  @Output()
-  public clickedEmitter: EventEmitter<WidgetUnit> = new EventEmitter()
-
-  @Input()
-  public exitable: boolean = true
-
-  @Input()
-  public titleHTML: string = null
-
-  public guestComponentRef: ComponentRef<any>
-  public widgetServices: WidgetServices
-  public cf: ComponentRef<WidgetUnit>
-  private subscriptions: Subscription[] = []
-
-  public id: string
-  constructor() {
-    this.id = Date.now().toString()
-  }
-
-  public ngOnInit() {
-    this.canBeDocked = typeof this.widgetServices.dockedContainer !== 'undefined'
-
-    this.isMinimised$ = this.widgetServices.minimisedWindow$.pipe(
-      map(set => set.has(this)),
-    )
-    this.subscriptions.push(
-      this.isMinimised$.subscribe(flag => this.isMinimised = flag ? 'none' : null),
-    )
-  }
-
-  public ngOnDestroy() {
-    while (this.subscriptions.length > 0) {
-      this.subscriptions.pop().unsubscribe()
-    }
-  }
-
-  /**
-   * @param {boolean}
-   * @description when new viewer is init, if this viewer will persist
-   * @default false
-   * @TODO does it make sense to tie widget persistency with WidgetUnit class?
-   */
-  public persistency: boolean = false
-
-  public undock(event?: Event) {
-    if (event) {
-      event.stopPropagation()
-      event.preventDefault()
-    }
-
-    this.widgetServices.changeState(this, {
-      title : this.title,
-      state: 'floating',
-      exitable: this.exitable,
-      persistency: this.persistency,
-    })
-  }
-
-  public dock(event?: Event) {
-    if (event) {
-      event.stopPropagation()
-      event.preventDefault()
-    }
-
-    this.widgetServices.changeState(this, {
-      title : this.title,
-      state: 'docked',
-      exitable: this.exitable,
-      persistency: this.persistency,
-    })
-  }
-
-  public exit(event?: Event) {
-    if (event) {
-      event.stopPropagation()
-      event.preventDefault()
-    }
-
-    this.widgetServices.exitWidget(this)
-  }
-
-  public setWidthHeight() {
-    this.width = this.state === 'docked' ? null : '0px'
-    this.height = this.state === 'docked' ? null : '0px'
-  }
-
-  /* floating widget specific functionalities */
-
-  @HostBinding('style.transform')
-  get styleTransform() {
-    return this.state === 'floating' ? `translate(${this.position.map(v => v + 'px').join(',')})` : null
-  }
-
-  public position: [number, number] = [400, 100]
-}
diff --git a/src/widget/widgetUnit/widgetUnit.style.css b/src/widget/widgetUnit/widgetUnit.style.css
deleted file mode 100644
index 9002aa7d4dcb155fb9e4fbd98ca065576625e20b..0000000000000000000000000000000000000000
--- a/src/widget/widgetUnit/widgetUnit.style.css
+++ /dev/null
@@ -1,91 +0,0 @@
-:host
-{
-  pointer-events: all;
-  display:block;
-}
-
-div[widgetUnitHeading]
-{
-  font-size:110%;
-  padding : 0.5em 0.7em;
-  display:flex;
-  white-space: nowrap;
-}
-
-  div[widgetUnitHeading] > div[title]
-  {
-    flex : 1 1 0px;
-    overflow:hidden;
-    text-overflow: ellipsis;
-  }
-
-  div[widgetUnitHeading] > div[icons]
-  {
-    flex : 0 0 0px;
-    display:flex;
-  }
-
-  div[widgetUnitHeading] > div[icons] > i
-  {
-    margin-left:0.5em;
-  }
-
-.widget-body
-{
-  min-width:280px;
-}
-
-:host-context([state='floating']) div[widgetUnitHeading]:hover
-{
-  cursor : move;
-}
-
-@keyframes blinkDark
-{
-  0% {
-    border-color: rgba(128, 128, 200, 0.0);
-  }
-
-  100% {
-    border-color: rgba(128, 128, 200, 1.0);
-  }
-}
-
-@keyframes blink
-{
-  0% {
-    border-color: rgba(128, 128, 255, 0.0);
-  }
-
-  100% {
-    border-color: rgba(128, 128, 255, 1.0);
-  }
-}
-
-:host-context([darktheme="true"]) .blinkOn
-{
-  animation: 0.5s blinkDark ease-in-out 9 alternate; 
-  border: 1px solid rgba(128, 128, 200, 1.0) !important;
-}
-
-:host-context([darktheme="false"]) .blinkOn
-{
-  animation: 0.5s blink ease-in-out 9 alternate; 
-  border: 1px solid rgba(128, 128, 255, 1.0) !important;
-}
-
-[heading]
-{
-  position:relative;
-}
-
-[heading] > [progressBar]
-{
-  position: absolute;
-  width: 100%;
-  height: 100%;
-  left: 0;
-  top: 0;
-  opacity: 0.4;
-  pointer-events: none;
-}
diff --git a/src/widget/widgetUnit/widgetUnit.template.html b/src/widget/widgetUnit/widgetUnit.template.html
deleted file mode 100644
index 6af4e0338400bd94e1e36f6daad5708aa1a0c11c..0000000000000000000000000000000000000000
--- a/src/widget/widgetUnit/widgetUnit.template.html
+++ /dev/null
@@ -1,42 +0,0 @@
-<mat-card cdkDrag
-  [cdkDragDisabled]="state === 'docked'"
-  [ngClass]="{'blinkOn': blinkOn, 'bodyCollapsable': state === 'docked'}"
-  class="widget-body">
-
-  <!-- body -->
-  <mat-card-content>
-    <!-- top bar, drag handle etc -->
-    <div class="d-flex align-items-center">
-      <!-- drag handle -->
-      <span class="hover-grab p-2"
-        cdkDragHandle>
-        <i class="fas fa-grip-vertical"></i>
-      </span>
-
-      <div class="flex-grow-1"></div>
-
-      <!-- close btn -->
-      <button mat-icon-button
-        (click)="exit($event)">
-        <i class="fas fa-times"></i>
-      </button>
-    </div>
-
-    <h4 class="mat-h4">
-      <ng-template [ngTemplateOutlet]="titleTmpl">
-      </ng-template>
-    </h4>
-    <ng-template #container>
-    </ng-template>
-  </mat-card-content>
-</mat-card>
-
-
-<!-- title tmpl -->
-<ng-template #titleTmpl>
-  <div *ngIf="!titleHTML">
-    {{ title }}
-  </div>
-  <div [innerHTML]="titleHTML" *ngIf="titleHTML">
-  </div>
-</ng-template>
diff --git a/src/zipFilesOutput/downloadSingleFile.directive.ts b/src/zipFilesOutput/downloadSingleFile.directive.ts
index 1ad93e0fac8a36c607965305291538cacb3108e7..d23f0dad79766203bba582003d39208a609f5505 100644
--- a/src/zipFilesOutput/downloadSingleFile.directive.ts
+++ b/src/zipFilesOutput/downloadSingleFile.directive.ts
@@ -12,12 +12,18 @@ export class SingleFileOutput {
   @Input('single-file-output')
   singleFile: TZipFileConfig
 
+  @Input('single-file-output-filename')
+  singleFileFileName: string
+
+  @Input('single-file-output-blob')
+  singleFileBlob: Blob
+
   @HostListener('click')
-  onClick(){
+  onClick(): void{
     const anchor = this.doc.createElement('a')
-    const blob = new Blob([this.singleFile.filecontent], { type: 'text/plain' })
+    const blob = this.singleFileBlob || new Blob([this.singleFile.filecontent], { type: 'text/plain' })
     anchor.href = URL.createObjectURL(blob)
-    anchor.download = this.singleFile.filename
+    anchor.download = this.singleFileFileName || this.singleFile.filename
 
     this.doc.body.appendChild(anchor)
     anchor.click()
diff --git a/src/zipFilesOutput/zipFilesOutput.directive.ts b/src/zipFilesOutput/zipFilesOutput.directive.ts
index dc980728cfd8675ecfaaf57eb1c4317f74556ba0..9b154497a29255abd44a175ca87d39449027be96 100644
--- a/src/zipFilesOutput/zipFilesOutput.directive.ts
+++ b/src/zipFilesOutput/zipFilesOutput.directive.ts
@@ -36,7 +36,7 @@ export class ZipFilesOutput {
   }
 
   @HostListener('click')
-  async onClick(){
+  async onClick(): Promise<void>{
     if (Array.isArray(this.zipFiles)) {
       await this.zipArray(this.zipFiles)
       return
diff --git a/third_party/vanilla_nehuba.js b/third_party/vanilla_nehuba.js
index d28509fb281124c7a82f575c77395cc38f031aa2..7bd0a86dd06b66afae50c14f69ca3c74d3c1ad22 100644
--- a/third_party/vanilla_nehuba.js
+++ b/third_party/vanilla_nehuba.js
@@ -1,3 +1,7 @@
 (() => {
-  export_nehuba.createNehubaViewer({}, err => console.error(err))
+  if (!export_nehuba) {
+    console.warn(`export_nehuba is not defined. Did you forget to import vanilla nehuba?`)
+    return
+  }
+  window.nehubaViewer = export_nehuba.createNehubaViewer({}, err => console.error(err))
 })()
\ No newline at end of file
diff --git a/tsconfig.app.json b/tsconfig.app.json
index 9eecf098ab9df8a94086e9c511f3800652f701c8..95493e7241e42229321c881be819aa585c90d02f 100644
--- a/tsconfig.app.json
+++ b/tsconfig.app.json
@@ -3,9 +3,10 @@
   "exclude": [
     "**/*.spec.ts",
     "./spec/*",
-    "src/atlasViewerExports/*"
+    "src/atlasViewerExports/*",
+    "**/*.stories.*"
   ],
   "files": [
     "src/main-aot.ts"
   ]
-}
\ No newline at end of file
+}
diff --git a/tsconfig.json b/tsconfig.json
index 99482118bf1420f2acbd875d83eba79992a3b08c..7e2716b1f639eb80fe50f7ac5f3842c1a0ccbe18 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -11,7 +11,13 @@
       "third_party/*" : ["third_party/*"],
       "src/*" : ["src/*"],
       "common/*": ["common/*"]
-    }
+    },
+    /**
+    * related: https://github.com/storybookjs/storybook/issues/9241
+    * related: https://stackoverflow.com/questions/63457043/error-ts2314-generic-type-modulewithproviderst-requires-1-type-arguments
+    * if shedding storybookjs dev dependency, maybe shed this as well
+    */
+    "skipLibCheck": true
   },
   "angularCompilerOptions":{
     "fullTemplateTypeCheck": true,
diff --git a/tslint.json b/tslint.json
deleted file mode 100644
index d96430c1d0da68127028648f02108ce49b766912..0000000000000000000000000000000000000000
--- a/tslint.json
+++ /dev/null
@@ -1,38 +0,0 @@
-{
-  "defaultSeverity": "error",
-  "extends": [
-    "tslint:recommended"
-  ],
-  "jsRules": {},
-  "rules": {
-    "variable-name": {
-      "options": [
-        "allow-leading-underscore"
-      ]
-    },
-    "indent": [true, "spaces", 2],
-    "quotemark":false,
-    "semicolon":false,
-    "object-literal-sort-keys":false,
-    "arrow-parens": false,
-    "no-var-requires": false,
-    "ban-types": false,
-    "no-require-imports": {
-      "severity":"off"
-    },
-    "interface-name": {
-      "severity": "off"
-    },
-    "member-ordering": {
-      "options": [
-        {
-          "order": "statics-first",
-          "alphabetize": true
-        }
-      ],
-      "severity": "off"
-    },
-    "max-line-length": false
-  },
-  "rulesDirectory": []
-}
\ No newline at end of file
diff --git a/worker/worker-typedarray.js b/worker/worker-typedarray.js
index e501bfa19de2db4892f542131403e2db1a63ab4d..6157de059e71cb5f1e78c44199f02f8cc3c1dba3 100644
--- a/worker/worker-typedarray.js
+++ b/worker/worker-typedarray.js
@@ -1,4 +1,124 @@
 (function(exports){
+  /**
+   * CM_CONST adopted from https://github.com/bpostlethwaite/colormap
+   * at commit hash 3406182
+   * 
+   * with MIT license
+   * 
+   * Copyright (c) <2012> ICRL
+   * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+   * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+   * 
+   * https://github.com/bpostlethwaite/colormap/blob/3406182/colorScale.js
+   */
+  const CM_CONST = {
+    "jet":[{"index":0,"rgb":[0,0,131]},{"index":0.125,"rgb":[0,60,170]},{"index":0.375,"rgb":[5,255,255]},{"index":0.625,"rgb":[255,255,0]},{"index":0.875,"rgb":[250,0,0]},{"index":1,"rgb":[128,0,0]}],
+    "viridis": [{"index":0,"rgb":[68,1,84]},{"index":0.13,"rgb":[71,44,122]},{"index":0.25,"rgb":[59,81,139]},{"index":0.38,"rgb":[44,113,142]},{"index":0.5,"rgb":[33,144,141]},{"index":0.63,"rgb":[39,173,129]},{"index":0.75,"rgb":[92,200,99]},{"index":0.88,"rgb":[170,220,50]},{"index":1,"rgb":[253,231,37]}],
+    "plasma": [{"index":0,"rgb":[13,8,135]},{"index":0.13,"rgb":[75,3,161]},{"index":0.25,"rgb":[125,3,168]},{"index":0.38,"rgb":[168,34,150]},{"index":0.5,"rgb":[203,70,121]},{"index":0.63,"rgb":[229,107,93]},{"index":0.75,"rgb":[248,148,65]},{"index":0.88,"rgb":[253,195,40]},{"index":1,"rgb":[240,249,33]}],
+    "magma": [{"index":0,"rgb":[0,0,4]},{"index":0.13,"rgb":[28,16,68]},{"index":0.25,"rgb":[79,18,123]},{"index":0.38,"rgb":[129,37,129]},{"index":0.5,"rgb":[181,54,122]},{"index":0.63,"rgb":[229,80,100]},{"index":0.75,"rgb":[251,135,97]},{"index":0.88,"rgb":[254,194,135]},{"index":1,"rgb":[252,253,191]}],
+    "inferno": [{"index":0,"rgb":[0,0,4]},{"index":0.13,"rgb":[31,12,72]},{"index":0.25,"rgb":[85,15,109]},{"index":0.38,"rgb":[136,34,106]},{"index":0.5,"rgb":[186,54,85]},{"index":0.63,"rgb":[227,89,51]},{"index":0.75,"rgb":[249,140,10]},{"index":0.88,"rgb":[249,201,50]},{"index":1,"rgb":[252,255,164]}],
+    "greyscale": [{"index":0,"rgb":[0,0,0]},{"index":1,"rgb":[255,255,255]}],
+  }
+
+  function lerp(min, max, val) {
+    const absDiff = max - min
+    const lowerVal = (val - min) / absDiff
+    return [lowerVal, 1 - lowerVal]
+  }
+
+  function getLerpToCm(colormap) {
+    if (!CM_CONST[colormap]) {
+      throw new Error(`colormap ${colormap} does not exist in CM_CONST`)
+    }
+    const cm = CM_CONST[colormap]
+
+    function check(nv, current) {
+      const lower = cm[current].index <= nv
+      const higher = nv <= cm[current + 1].index
+      let returnVal = null
+      if (lower && higher) {
+        returnVal = {
+          index: nv,
+          rgb: [0, 0, 0]
+        }
+        const [ lowerPc, higherPc ] = lerp(cm[current].index, cm[current + 1].index, nv)
+        returnVal.rgb = returnVal.rgb.map((_, idx) => cm[current]['rgb'][idx] * lowerPc + cm[current + 1]['rgb'][idx] * higherPc)
+      }
+      return [ returnVal, { lower, higher } ]
+    }
+    return function lerpToCm(nv) {
+      let minLow = 0, maxHigh = cm.length, current = Math.floor((maxHigh + minLow) / 2), found
+      let iter = 0
+      while (true) {
+        iter ++
+        if (iter > 100) {
+          throw new Error(`iter > 1000, something we`)
+        }
+        const [val, { lower, higher }] = check(nv, current)
+        if (val) {
+          found = val
+          break
+        }
+        if (lower) {
+          minLow = current
+        }
+        if (higher) {
+          maxHigh = current
+        }
+        current = Math.floor((maxHigh + minLow) / 2)
+      }
+      return found
+    }
+  }
+
+
+  function unpackToArray(inputArray, width, height, channel, dtype) {
+    /**
+     * NB: assuming C (row major) order!
+     */
+
+    if (channel !== 1) {
+      throw new Error(`cm2rgba channel must be 1, but is ${channel} instead`)
+    }
+    const depth = (() => {
+      if (dtype === "int32") return 4
+      if (dtype === "float32") return 4
+      if (dtype === "uint8") return 1
+      throw new Error(`unrecognised dtype: ${dtype}`)
+    })()
+    if (width * height * depth !== inputArray.length) {
+      throw new Error(`expecting width * height * depth === input.length, but ${width} * ${height} * ${depth} === ${width * height * depth} !== ${inputArray.length}`)
+    }
+
+    const UseConstructor = (() => {
+
+      if (dtype === "int32") return Int32Array
+      if (dtype === "float32") return Float32Array
+      if (dtype === "uint8") return Uint8Array
+      throw new Error(`unrecognised dtype: ${dtype}`)
+    })()
+    
+    const newArray = new UseConstructor(inputArray.buffer)
+    const outputArray = []
+    let min = null
+    let max = null
+    for (let y = 0; y < height; y ++) {
+      if (!outputArray[y]) outputArray[y] = []
+      for (let x = 0; x < width; x++) {
+        const num = newArray[y * width + x]
+        min = min === null ? num : Math.min(num, min)
+        max = max === null ? num : Math.max(num, max)
+        outputArray[y][x] = newArray[y * width + x]
+      }
+    }
+    return {
+      outputArray,
+      min,
+      max
+    }
+  }
+
   exports.typedArray = {
     fortranToRGBA(inputArray, width, height, channel) {
       if (channel !== 1 && channel !== 3) {
@@ -28,6 +148,52 @@
         }
       }
       return buffer
+    },
+    cm2rgba(inputArray, width, height, channel, dtype, processParams) {
+      const { 
+        outputArray,
+        min,
+        max,
+       } = unpackToArray(inputArray, width, height, channel, dtype)
+      const {
+        colormap="jet"
+      } = processParams || {}
+
+      const _ = new ArrayBuffer(width * height * 4)
+      const buffer = new Uint8ClampedArray(_)
+
+      const absDiff = max - min
+      const lerpToCm = getLerpToCm(colormap)
+      
+      for (let row = 0; row < height; row ++) {
+        for (let col = 0; col < width; col ++){
+          const normalizedValue = (outputArray[row][col] - min) / absDiff
+          const { rgb } = lerpToCm(normalizedValue)
+
+          const toIdx = (row * width + col) * 4
+          buffer[toIdx] = rgb[0]
+          buffer[toIdx + 1] = rgb[1]
+          buffer[toIdx + 2] = rgb[2]
+          buffer[toIdx + 3] = 255
+        }
+      }
+      return {
+        buffer,
+        min,
+        max,
+      }
+    },
+    rawArray(inputArray, width, height, channel, dtype) {
+      const { 
+        outputArray,
+        min,
+        max,
+       } = unpackToArray(inputArray, width, height, channel, dtype)
+       return {
+          outputArray,
+          min,
+          max,
+       }
     }
   }
 })(
diff --git a/worker/worker.js b/worker/worker.js
index 04a3db58bd8cbac500f2ab0dedc896ea2de7f979..ab8cc83715853cc94fb09e4a2bfce4b5f2597b6a 100644
--- a/worker/worker.js
+++ b/worker/worker.js
@@ -17,7 +17,6 @@ if (typeof self.importScripts === 'function')  self.importScripts('./worker-type
  */
 
 const validTypes = [
-  'GET_LANDMARKS_VTK',
   'GET_USERLANDMARKS_VTK',
   'PROPAGATE_PARC_REGION_ATTR'
 ]
@@ -26,16 +25,21 @@ const VALID_METHOD = {
   PROCESS_PLOTLY: `PROCESS_PLOTLY`,
   PROCESS_NIFTI: 'PROCESS_NIFTI',
   PROCESS_TYPED_ARRAY: `PROCESS_TYPED_ARRAY`,
+  PROCESS_TYPED_ARRAY_F2RGBA: `PROCESS_TYPED_ARRAY_F2RGBA`,
+  PROCESS_TYPED_ARRAY_CM2RGBA: "PROCESS_TYPED_ARRAY_CM2RGBA",
+  PROCESS_TYPED_ARRAY_RAW: "PROCESS_TYPED_ARRAY_RAW",
 }
 
 const VALID_METHODS = [
   VALID_METHOD.PROCESS_PLOTLY,
   VALID_METHOD.PROCESS_NIFTI,
   VALID_METHOD.PROCESS_TYPED_ARRAY,
+  VALID_METHOD.PROCESS_TYPED_ARRAY_F2RGBA,
+  VALID_METHOD.PROCESS_TYPED_ARRAY_CM2RGBA,
+  VALID_METHOD.PROCESS_TYPED_ARRAY_RAW,
 ]
 
 const validOutType = [
-  'ASSEMBLED_LANDMARKS_VTK',
   'ASSEMBLED_USERLANDMARKS_VTK',
 ]
 
@@ -170,32 +174,6 @@ const parseLmToVtk = (landmarks, scale) => {
     .concat(reduce.labelString.join('\n'))
 }
 
-let landmarkVtkUrl
-
-const getLandmarksVtk = (action) => {
-
-  // landmarks are array of triples in nm (array of array of numbers)
-  const landmarks = action.landmarks
-  const template = action.template
-  const scale = action.scale
-    ? action.scale
-    : 2.8
-
-  const vtk = parseLmToVtk(landmarks, scale)
-
-  if(!vtk) return
-
-  // when new set of landmarks are to be displayed, the old landmarks will be discarded
-  if(landmarkVtkUrl) URL.revokeObjectURL(landmarkVtkUrl)
-
-  landmarkVtkUrl = URL.createObjectURL(new Blob( [encoder.encode(vtk)], {type : 'application/octet-stream'} ))
-  postMessage({
-    type : 'ASSEMBLED_LANDMARKS_VTK',
-    template,
-    url : landmarkVtkUrl
-  })
-}
-
 let userLandmarkVtkUrl
 
 const getuserLandmarksVtk = (action) => {
@@ -295,6 +273,27 @@ onmessage = (message) => {
       }
     }
     if (message.data.method === VALID_METHOD.PROCESS_TYPED_ARRAY) {
+      try {
+        const { inputArray, dtype, width, height, channel } = message.data.param
+        const array = self.typedArray.packNpArray(inputArray, dtype, width, height, channel)
+
+        postMessage({
+          id,
+          result: {
+            array
+          }
+        })
+      } catch (e) {
+        postMessage({
+          id,
+          error: {
+            code: 401,
+            message: `process typed array error: ${e.toString()}`
+          }
+        })
+      }
+    }
+    if (message.data.method === VALID_METHOD.PROCESS_TYPED_ARRAY_F2RGBA) {
       try {
         const { inputArray, width, height, channel } = message.data.param
         const buffer = self.typedArray.fortranToRGBA(inputArray, width, height, channel)
@@ -315,11 +314,57 @@ onmessage = (message) => {
         })
       }
     }
+    if (message.data.method === VALID_METHOD.PROCESS_TYPED_ARRAY_CM2RGBA) {
+      try {
+        const { inputArray, width, height, channel, dtype, processParams } = message.data.param
+        const { buffer, min, max } = self.typedArray.cm2rgba(inputArray, width, height, channel, dtype, processParams)
+
+        postMessage({
+          id,
+          result: {
+            buffer,
+            min,
+            max,
+          }
+        }, [ buffer.buffer ])
+      } catch (e) {
+        postMessage({
+          id,
+          error: {
+            code: 401,
+            message: `process typed array error: ${e.toString()}`
+          }
+        })
+      }
+    }
+    if (message.data.method === VALID_METHOD.PROCESS_TYPED_ARRAY_RAW) {
+      try {
+        const { inputArray, width, height, channel, dtype, processParams } = message.data.param
+        const { outputArray, min, max } = self.typedArray.rawArray(inputArray, width, height, channel, dtype, processParams)
+
+        postMessage({
+          id,
+          result: {
+            outputArray,
+            min,
+            max,
+          }
+        })
+      } catch (e) {
+        postMessage({
+          id,
+          error: {
+            code: 401,
+            message: `process typed array error: ${e.toString()}`
+          }
+        })
+      }
+    }
     postMessage({
       id,
       error: {
         code: 404,
-        message: `worker method not found`
+        message: `worker method not found. ${message.data.method}`
       }
     })
     return
@@ -327,9 +372,6 @@ onmessage = (message) => {
 
   if(validTypes.findIndex(type => type === message.data.type) >= 0){
     switch(message.data.type){
-      case 'GET_LANDMARKS_VTK':
-        getLandmarksVtk(message.data)
-        return
       case 'GET_USERLANDMARKS_VTK':
         getuserLandmarksVtk(message.data)
         return