diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index ca360d48344a917435ac26d848ee6dc36f7d67a6..28f5c441c2c2565a2e5c66bf40e4f7a2bdfb7c5e 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -221,6 +221,20 @@ e-acsl-tests-dev:
   tags:
    - nix
 
+ivette:
+  stage: build
+  image: node
+  cache:
+    paths:
+    - ivette/node_modules/
+  script:
+   - node --version
+   - npm --version
+   - yarn --version
+   - make -C ivette
+  tags:
+    - docker
+
 make_public:
   stage: make_public
   script:
diff --git a/doc/pandoc/style.css b/doc/pandoc/style.css
index 054b077e70cba3ffcb19a38fa68fa3180cf7da9e..866aafb08dc81699fc86e6d931044bd2317ddf59 100644
--- a/doc/pandoc/style.css
+++ b/doc/pandoc/style.css
@@ -36,7 +36,7 @@ body {
     height: 100%;
     overflow: hidden;
     font-family: "Verdana", sans;
-    font-size: 12pt;
+    font-size: 10pt;
 }
 
 /* -------------------------------------------------------------------------- */
@@ -72,7 +72,7 @@ body {
     justify-content: left ;
     align-content: center ;
     box-sizing: border-box ;
-    width: 18cm ;
+    width: 22cm ;
     height: 100% ;
     padding: 0cm 1cm 1cm 1cm ;
 }
@@ -81,10 +81,6 @@ body {
 /* --- Navigation                                                         --- */
 /* -------------------------------------------------------------------------- */
 
-#TOC {
-    font-size: smaller ;
-}
-
 #NAVIGATION a {
     color: black ;
 }
@@ -92,7 +88,7 @@ body {
 #NAVIGATION a.root {
     display: block;
     font-family: "Optima", "Verdana", "Arial", sans;
-    font-size: 16pt;
+    font-size: 10pt;
     margin-top: 1cm;
     margin-bottom: 6mm;
 }
@@ -123,7 +119,7 @@ body {
 /* -------------------------------------------------------------------------- */
 
 h1 {
-    width: 18cm;
+    width: 22cm;
     font-family: "Optima", "Verdana", "Arial", sans;
     text-align: left;
     margin-top: 1cm;
@@ -135,13 +131,13 @@ h1 {
 h2 {
     width: 18cm;
     font-family: "Optima", "Verdana", "Arial", sans;
-    margin-top: 5mm;
+    margin-top: 16mm;
     margin-bottom: 2mm;
     border-bottom: solid thin darkred ;
 }
 
 h3 {
-    width: 17cm;
+    width: 18cm;
     font-family: "Optima", "Verdana", "Arial", sans;
     margin-top: 5mm;
     margin-bottom: 3mm;
@@ -158,11 +154,11 @@ h4,h5,h6 {
     font-weight: bold;
 }
 
-p { margin: 6px 0px 6px 0px; width: 15cm; }
+p { margin: 18px 0px 18px 0px; width: 18cm; }
 
 ul,ol,blockquote {
     margin-left: 24px ;
-    width: 13cm;
+    width: 15cm;
 }
 
 li {
@@ -177,7 +173,7 @@ pre {
     background-color: #eef ;
 }
 
-pre,code { font-size: smaller ; color: #106000 }
+pre,code { color: #106000 }
 
 hr {
     border: none ;
@@ -188,10 +184,13 @@ hr {
 
 table {
     border-collapse: collapse ;
+    margin-top: 4mm ;
+    margin-bottom: 4mm ;
     margin-left: 6mm ;
 }
 
 th,td {
+    font-weight: normal ;
     padding: 2px 16px 2px 12px ;
 }
 
diff --git a/headers/header_spec.txt b/headers/header_spec.txt
index 407d92e8f4656ccd8d89f827a9a10691f8f8484c..dfdb833077bb0f6fe13cd38d9348d5067d5bafa9 100644
--- a/headers/header_spec.txt
+++ b/headers/header_spec.txt
@@ -1099,6 +1099,8 @@ src/plugins/server/kernel_main.ml: CEA_LGPL_OR_PROPRIETARY
 src/plugins/server/kernel_main.mli: CEA_LGPL_OR_PROPRIETARY
 src/plugins/server/kernel_project.ml: CEA_LGPL_OR_PROPRIETARY
 src/plugins/server/kernel_project.mli: CEA_LGPL_OR_PROPRIETARY
+src/plugins/server/kernel_properties.ml: CEA_LGPL_OR_PROPRIETARY
+src/plugins/server/kernel_properties.mli: CEA_LGPL_OR_PROPRIETARY
 src/plugins/server/main.ml: CEA_LGPL_OR_PROPRIETARY
 src/plugins/server/main.mli: CEA_LGPL_OR_PROPRIETARY
 src/plugins/server/request.ml: CEA_LGPL_OR_PROPRIETARY
@@ -1107,6 +1109,8 @@ src/plugins/server/server_parameters.ml: CEA_LGPL_OR_PROPRIETARY
 src/plugins/server/server_parameters.mli: CEA_LGPL_OR_PROPRIETARY
 src/plugins/server/server_batch.ml: CEA_LGPL_OR_PROPRIETARY
 src/plugins/server/server_zmq.ml: CEA_LGPL_OR_PROPRIETARY
+src/plugins/server/states.ml: CEA_LGPL_OR_PROPRIETARY
+src/plugins/server/states.mli: CEA_LGPL_OR_PROPRIETARY
 src/plugins/server/syntax.ml: CEA_LGPL_OR_PROPRIETARY
 src/plugins/server/syntax.mli: CEA_LGPL_OR_PROPRIETARY
 src/plugins/scope/Scope.mli: CEA_LGPL_OR_PROPRIETARY
diff --git a/ivette/.babelrc b/ivette/.babelrc
new file mode 100644
index 0000000000000000000000000000000000000000..3e895f0c581bd6d45a280384ee46860f7d37c213
--- /dev/null
+++ b/ivette/.babelrc
@@ -0,0 +1,21 @@
+// --------------------------------------------------------------------------
+// --- Template .babelrc configuration file for Dome
+// --------------------------------------------------------------------------
+
+/*
+   Template from $(DOME)/template/babelrc.json
+
+   This configuration is a necessary extension to electron-webpack
+   for enabling react-hot-reloading.
+*/
+
+{
+  "presets": [
+    [ "@babel/env", { "modules": false } ],
+    [ "@babel/react" ]
+  ],
+  "plugins": [
+    "@babel/plugin-proposal-object-rest-spread",
+    "react-hot-loader/babel"
+  ]
+}
diff --git a/ivette/.dome-pkg-app.lock b/ivette/.dome-pkg-app.lock
new file mode 100644
index 0000000000000000000000000000000000000000..7282f0cc447a1caf3cb155d03008966d7b2f41ab
--- /dev/null
+++ b/ivette/.dome-pkg-app.lock
@@ -0,0 +1 @@
+react@^16.8 react-dom source-map-support lodash react-virtualized react-draggable codemirror
diff --git a/ivette/.dome-pkg-dev.lock b/ivette/.dome-pkg-dev.lock
new file mode 100644
index 0000000000000000000000000000000000000000..e1751fd66367a580e267a065cb09c992b4c59e94
--- /dev/null
+++ b/ivette/.dome-pkg-dev.lock
@@ -0,0 +1 @@
+@babel/core@^7 @babel/preset-env @babel/preset-react @babel/plugin-proposal-object-rest-spread electron@^7 electron-builder electron-webpack electron-devtools-installer webpack babel-loader css-loader react-hot-loader @hot-loader/react-dom @types/react
diff --git a/ivette/.gitignore b/ivette/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..994f69553f09fc8fe011cbfc56b5f34408d84098
--- /dev/null
+++ b/ivette/.gitignore
@@ -0,0 +1,15 @@
+# --------------------------------------------------------------------------
+# --- Template .gitignore file for Dome
+# --------------------------------------------------------------------------
+
+.dome-*.stamp
+.dome-*.back
+node_modules
+yarn.lock
+yarn-error.log
+/bin
+/dist
+/doc/html
+/src/dome
+
+# --------------------------------------------------------------------------
diff --git a/ivette/Makefile b/ivette/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..2594cb8e96fe5ea6957120e1e7c48956e435207f
--- /dev/null
+++ b/ivette/Makefile
@@ -0,0 +1,27 @@
+# --------------------------------------------------------------------------
+APP=Ivette
+DOME=./src/dome
+DOME_ARGS=--command $$(dirname $$0)/../../bin/frama-c
+DOME_DEV=-server-debug 1
+DOME_CLI=./bin/frama-c-gui
+DOME_API=./src/frama-c
+COPYRIGHT=CEA LIST / LSL
+# --------------------------------------------------------------------------
+
+.PHONY: app dev doc dist
+
+app: dome-app
+dev: dome-dev
+doc: dome-doc
+dist: dome-dist
+
+# --------------------------------------------------------------------------
+# --- Ivette Plugins
+# --------------------------------------------------------------------------
+
+DOME_PLUGINS=
+DOME_EXPORTS=
+
+# --------------------------------------------------------------------------
+include $(DOME)/template/makefile
+# --------------------------------------------------------------------------
diff --git a/ivette/README.md b/ivette/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..da470aaa23646815685ab9ab6d1b6b954acb31fc
--- /dev/null
+++ b/ivette/README.md
@@ -0,0 +1,25 @@
+## Setup
+
+From the `./gui` sub-directory of Frama-C main directory:
+
+```
+$ make app
+```
+
+## Mirroring to Dome/Electron
+
+The content of ./src/dome is git-subtree to be kept in sync
+with Dome/Electron. The related commands must be issued from
+the Frama-C root directory:
+
+1. Importing a branch from dome/electron:
+
+```
+$ git subtree pull -P gui/src/dome git@git.frama-c.com:dome/electron.git <branch>
+```
+
+2. Exporting to a branch into dome/electron:
+
+```
+$ git subtree push -P gui/src/dome git@git.frama-c.com:dome/electron.git <branch>
+```
diff --git a/ivette/backup/sandbox/Project.js b/ivette/backup/sandbox/Project.js
new file mode 100644
index 0000000000000000000000000000000000000000..37d33df468aedf4ad087430193c8e6ba1c18d445
--- /dev/null
+++ b/ivette/backup/sandbox/Project.js
@@ -0,0 +1,206 @@
+import React, { useState, useEffect } from 'react';
+import { showOpenFiles } from 'dome/dialogs';
+import { Button } from 'dome/controls/buttons';
+import { Label } from 'dome/controls/labels';
+import { Badge } from 'dome/controls/icons';
+import { Hbox, Vbox } from 'dome/layout/boxes';
+import Server from './server';
+import { Set } from 'immutable';
+
+class SelectList extends React.Component {
+  render() {
+    return (
+      <select value={this.props.value} onChange={this.props.onChange}>
+        {this.props.values.map(v => <option key={v.id}>{v.name}</option>)}
+      </select>
+    )
+  }
+}
+
+function FileSelect(props) {
+  const [files, setFiles] = useState(Set([]));
+  const [addedFiles, setAddedFiles] = useState(Set([]));
+  const [removedFiles, setRemovedFiles] = useState(Set([]));
+
+  function updateFiles() {
+    Server
+      .sendGET("kernel.ast.getFiles", [], false)
+      .then(
+        (data) => {
+          console.log("success: " + data + '\n');
+          setFiles(Set(data));
+          setAddedFiles(Set([]));
+          setRemovedFiles(Set([]));
+        },
+        (_rid, data) => {
+          console.log("err:" + data + '\n')
+        });
+  };
+
+  //Done only once
+  useEffect(updateFiles, []);
+
+  function addFiles() {
+    showOpenFiles({ message: "Add files" })
+      .then(files => {
+        setAddedFiles(() => {
+          const sfiles = Set(files);
+          const res = addedFiles.union(sfiles);
+          return res
+        })
+      });
+  };
+
+  function generateItem(v) {
+    const removed = removedFiles.has(v);
+    const added = addedFiles.has(v);
+    return (
+      <Hbox key={v}>
+        <Label
+          style={(removed ? { color: 'red' } : (added ? { color: 'green' } : {}))}>
+          {v}
+        </Label>
+        <Badge
+          value={removed ? 'CIRC.PLUS' : 'CIRC.MINUS'}
+          onClick={() =>
+            setRemovedFiles(() =>
+              removed ? removedFiles.remove(v) : removedFiles.add(v))
+          } />
+      </Hbox>
+    )
+  };
+
+  function sendFiles() {
+    const filesToSend = files.union(addedFiles).subtract(removedFiles);
+    Server
+      .sendSET("kernel.ast.setFiles", filesToSend.toArray(), false)
+      .then(
+        () => {
+          console.log("setFiles: success");
+          Server
+            .sendEXEC("kernel.ast.execCompute", [], false)
+            .then(
+              () => {
+                console.log("execComput: success");
+                updateFiles();
+              },
+              (_rid, data) => {
+                console.log("execCompute err:" + data + '\n')
+              }
+            );
+        },
+        (_rid, data) => {
+          console.log("setFiles err:" + data + '\n')
+        }
+      );
+  };
+
+  return (
+    <Vbox>
+      {files.map(generateItem)}
+      {addedFiles.map(generateItem)}
+      <Hbox>
+        <Button label='Add files' onClick={addFiles} />
+        <Button label='Update' onClick={sendFiles} />
+      </Hbox>
+    </Vbox>
+  )
+}
+
+function Projects(props) {
+  const [projects, setProjects] = useState([]);
+  const [current, setCurrent] = useState(undefined);
+  const [newName, setNewName] = useState("project_name");
+
+
+  function updateProjects() {
+    Server
+      .sendGET("kernel.project.getList", [], false)
+      .then(
+        (data) => {
+          console.log("success: " + data.datadir + '\n');
+          setProjects(data)
+        },
+        (_rid, data) => {
+          console.log("err:" + data + '\n')
+        });
+  };
+
+  //Done only once
+  useEffect(updateProjects, []);
+
+  function new_project() {
+    Server
+      .sendSET("kernel.project.setCreate", newName, false)
+      .then(
+        (data) => {
+          console.log("success: " + data.datadir + '\n');
+          updateProjects();
+        },
+        (_rid, data) => {
+          console.log("err:" + data + '\n')
+        });
+  };
+
+
+  console.log("projects:", projects);
+  return (
+    <Hbox>
+      <form onSubmit={new_project}>
+        <label>
+          Name of new project :
+          <input
+            type="text"
+            value={newName}
+            onChange={event => setNewName(event.target.value)}
+          />
+        </label>
+        <input type="submit" value="Create" />
+      </form>
+      <label>
+        Current project:
+        <SelectList
+          values={projects}
+          value={current}
+          onChange={event => setCurrent(event.target.value)}
+        />
+      </label>
+    </Hbox>
+  )
+}
+
+function Kernel(props) {
+  const [state, setState] = useState("undefined");
+
+  function getCfg() {
+    console.log("send request\n");
+    Server
+      .sendGET("kernel.getConfig", [], false)
+      .then(
+        (data) => {
+          console.log("success: " + data.datadir + '\n');
+          setState(data.datadir);
+        },
+        (_rid, data) => {
+          console.log("err:" + data + '\n')
+        });
+  };
+
+  return (
+    <Hbox>
+      <Button label='GetConfig' onClick={getCfg} />
+      <Label label={state} />
+    </Hbox>
+  )
+}
+
+export default function Project(props) {
+
+  return (
+    <>
+      <Kernel />
+      <Projects />
+      <FileSelect />
+    </>
+  )
+}
\ No newline at end of file
diff --git a/ivette/backup/sandbox/Properties.js b/ivette/backup/sandbox/Properties.js
new file mode 100644
index 0000000000000000000000000000000000000000..ac8e770bef928215afea13c5dc33985af090daf4
--- /dev/null
+++ b/ivette/backup/sandbox/Properties.js
@@ -0,0 +1,137 @@
+// --------------------------------------------------------------------------
+// --- Frama-C Properties Table
+// --------------------------------------------------------------------------
+
+import React from 'react' ;
+import Dome from 'dome' ;
+import { useState, useEffect } from 'react' ;
+
+import { Button } from 'dome/controls/buttons';
+import { Vfill, Vbox, Scroll } from 'dome/layout/boxes' ;
+import { Splitter } from 'dome/layout/splitters' ;
+import { Column, Table } from 'dome/table/views' ;
+import { ArrayModel } from 'dome/table/arrays' ;
+import { Form, Section, FieldList, FieldCheckbox, FieldRadio } from 'dome/layout/forms' ;
+
+import Server from './server.js' ;
+import Events from './Events.js' ;
+
+export default function Properties (props) {
+  const model = props.properties;
+
+  /* --- Columns of the table ----------------------------------------------- */
+
+  const columnFile = <Column id='file'
+                             label='File' />;
+  const columnFct = <Column id='fct'
+                            label='Function' />;
+  const columnProp = <Column id='property'
+                             label='Property'
+                             fill />;
+  const columnStatus = <Column id='status'
+                               label='Status' />;
+
+  /* The default columns displayed. The property column is always shown. */
+  const defaultColumns =
+        { path:false,
+          fct:true,
+          status:true,
+        };
+  /* The columns displayed. Set by the user through a form. */
+  const [columnsValue, _] = useState(defaultColumns);
+
+  /* Builds the array of columns according to [columnsValue]. */
+  function makeColumns () {
+    const columns = new Array();
+    if (columnsValue.path)
+      columns.push(columnFile);
+    if (columnsValue.fct)
+      columns.push(columnFct);
+    columns.push(columnProp); // Always shown.
+    if (columnsValue.status)
+      columns.push(columnStatus);
+    return columns;
+  }
+
+  /* The columns array used by the table. */
+  const [ columns, setColumns ] = useState(makeColumns());
+
+  function onChangeColumns () {
+    setColumns(makeColumns());
+  }
+
+  /* Form to choose the columns displayed. */
+  const columns_list =
+    <Form value={columnsValue} onChange={onChangeColumns} >
+      <Section label="Columns" unfold='false' >
+        <FieldCheckbox label="File" path='path' />
+        <FieldCheckbox label="Function" path='fct' />
+        <FieldCheckbox label="Status" path='status' />
+      </Section>
+    </Form>;
+
+  /* --- Filters of logical properties ------------------------------------- */
+
+  /* All properties are shown by default. */
+  const default_status =
+        { valid:true,
+          valid_hyp:true,
+          unknown:true,
+          invalid:true,
+          invalid_hyp:true,
+          considered_valid:true,
+          untried:true,
+          dead:true,
+          inconsistent:true
+        };
+  const [status, setStatus] = useState(default_status);
+
+  /* Function filtering the properties by status. */
+  function filter (item) {
+    switch (item.status) {
+    case 'Valid':
+    case 'Valid_but_dead': return status.valid;
+    case 'Valid_under_hyp': return status.valid_hyp;
+    case 'Invalid':
+    case 'Invalid_but_dead': return status.invalid;
+    case 'Invalid_under_hyp': return status.invalid_hyp;
+    case 'Unknown':
+    case 'Unknown_but_dead': return status.unknown;
+    case 'Considered_valid': return status.considered_valid;
+    case 'Never_tried': return status.untried;
+    case 'Dead': return status.dead;
+    case 'Inconsistent': return status.inconsistent;
+    default: return true;
+    }
+  }
+
+  function onChangeFilter (value, error) {
+    model.setFiltering(filter);
+  }
+
+  /* Filters selection. */
+  const filter_list =
+    <Form value={status} onChange={onChangeFilter} >
+      <Section label="Status" unfold='true' >
+        <FieldCheckbox label="Valid" path='valid' />
+        <FieldCheckbox label="Valid under hyp." path='valid_hyp' />
+        <FieldCheckbox label="Unknown" path='unknown' />
+        <FieldCheckbox label="Invalid" path='invalid' />
+        <FieldCheckbox label="Invalid under hyp." path='invalid_hyp' />
+        <FieldCheckbox label="Considered valid" path='considered_valid' />
+        <FieldCheckbox label="Untried" path='untried' />
+        <FieldCheckbox label="Dead" path='dead' />
+        <FieldCheckbox label="Inconsistent" path='inconsistent' />
+      </Section>
+    </Form>;
+
+  /* Table of logical properties. */
+  const table = <Table model={model} children={columns} />;
+
+  return (
+    <Splitter dir='LEFT' >
+      <Vfill> <Scroll> {filter_list} {columns_list} </Scroll> </Vfill>
+      <Vfill> {table} </Vfill>
+    </Splitter>
+  )
+}
diff --git a/ivette/backup/sandbox/SourceCode.js b/ivette/backup/sandbox/SourceCode.js
new file mode 100644
index 0000000000000000000000000000000000000000..023eeafbfb30a162fb9aebacb2217358969aec90
--- /dev/null
+++ b/ivette/backup/sandbox/SourceCode.js
@@ -0,0 +1,129 @@
+// --------------------------------------------------------------------------
+// --- Frama-C Source Panel
+// -------------------------------------------------------------------------
+
+import React from 'react' ;
+import Dome from 'dome' ;
+
+import { Button } from 'dome/controls/buttons';
+import Toolbar from 'dome/layout/toolbars' ;
+import { Hbox, Hfill, Vbox, Vfill, Space } from 'dome/layout/boxes' ;
+import { Splitter } from 'dome/layout/splitters' ;
+import { Column, Table } from 'dome/table/views' ;
+import { Text } from 'dome/text/editors' ;
+import Server from './server.js' ;
+import Events from './Events.js' ;
+
+import { useState, useEffect } from 'react' ;
+
+import 'codemirror/mode/clike/clike.js';
+import 'codemirror/theme/ambiance.css' ;
+import 'codemirror/theme/solarized.css';
+
+/* Pretty prints the source code from [text] in [buffer]. */
+function printSource (buffer, text) {
+  if (text === null) return;
+  if (Array.isArray(text)) {
+    const tag = text.shift();
+    if (tag !== '') {
+      buffer.openTextMarker( { id:tag } );
+      text.forEach(txt => printSource(buffer, txt));
+      buffer.closeTextMarker();
+    } else
+      text.forEach(txt => printSource(buffer, txt));
+  } else
+    buffer.append(text);
+}
+
+/* Display the list of functions and the source code for the selected function
+   in the list. */
+export default function Source (props) {
+  /* Buffer for the source code. */
+  const buffer = props.sourceCode;
+  /* Model for the function list. */
+  const model = props.functions;
+
+  /* Print the source code of the function [data.fct]. */
+  function printFunction (data) {
+    console.log("Select function: " + data.fct);
+    Server.sendGET("kernel.ast.printFunction", data.fct, false).then
+    ( data => {
+      buffer.clear();
+      printSource(buffer, data);
+    });
+  }
+
+  const [ theme, setTheme ] = useState('default');
+  function selectTheme (name) {
+    return function () { setTheme(name); }
+  }
+
+  function contextMenu (data) {
+    const item1 = { label:"Default", onClick:selectTheme("default") };
+    const item2 = { label:"Ambiance", onClick:selectTheme("ambiance") };
+    const item3 = { label:"Solarized light", onClick:selectTheme("solarized light") };
+    const item4 = { label:"Solarized dark", onClick:selectTheme("solarized dark") };
+    Dome.popupMenu([item1, item2, item3, item4]).catch(() => {});
+  }
+
+  /* Table for the function list. */
+  const table =
+    <Table model={model}
+           onSelection={printFunction} >
+      <Column id='fct'
+              label='Functions'
+              title='List of functions in the current Frama-C project' />
+    </Table>;
+
+  const [ lineWrapping, setLineWrapping ] = useState(false);
+  function changeLineWrapping () { setLineWrapping(!lineWrapping); }
+
+  const code = <Text buffer={buffer}
+                     mode='text/x-csrc'
+                     theme={theme}
+                     lineWrapping={lineWrapping}
+                     readOnly />;
+
+  const [ fontSize, setFontSize ] = useState(12);
+  function increaseFontSize () { setFontSize(fontSize + 2); }
+  function decreaseFontSize () { setFontSize(fontSize - 2); }
+
+  const toolbar =
+    <Toolbar.ToolBar>
+      <Toolbar.Space/>
+      <Toolbar.Select
+        value={theme}
+        onChange={(name) => {selectTheme(name)();}} >
+        <option value='default' label='Default'/>
+        <option value='ambiance' label='Ambiance'/>
+        <option value='solarized light' label='Solarized light'/>
+        <option value='solarized dark' label='Solarized dark'/>
+      </Toolbar.Select>
+      <Toolbar.ButtonGroup>
+        <Toolbar.Button
+          icon='MINUS'
+          title='Decrease the font size'
+          onClick={decreaseFontSize}
+        />
+        <Toolbar.Button
+          icon='PLUS'
+          title='Increase the font size'
+          onClick={increaseFontSize}
+        />
+      </Toolbar.ButtonGroup>
+      <Toolbar.Button icon={lineWrapping ? "CHECK" : "CROSS"}
+                      label="Line wrapping"
+                      title="Change line wrapping mode"
+                      onClick={changeLineWrapping} />
+    </Toolbar.ToolBar>;
+
+  return (
+    <Splitter dir='LEFT' >
+      <Vfill style={{ minWidth: 100 }}> {table} </Vfill>
+      <Vfill onContextMenu={contextMenu} style={{ fontSize: fontSize }} >
+        {toolbar}
+        {code}
+      </Vfill>
+    </Splitter>
+  );
+}
diff --git a/ivette/backup/sandbox/SourceFiles.js b/ivette/backup/sandbox/SourceFiles.js
new file mode 100644
index 0000000000000000000000000000000000000000..8a7386b57064bb60670ecca9b6145651e6b236c5
--- /dev/null
+++ b/ivette/backup/sandbox/SourceFiles.js
@@ -0,0 +1,44 @@
+import React, { useState, useEffect } from 'react';
+import { Buffer } from 'dome/text/buffers'
+import { Text } from 'dome/text/editors';
+import Server from './server';
+import System from 'dome/system';
+import 'codemirror/mode/clike/clike.js';
+
+export default function SourceFiles() {
+
+  const [filenames, setFilenames] = useState([]);
+  const buffer = new Buffer({ mode: 'text/x-csrc' });
+
+  function getSourceFilenames() {
+    console.log('SourceFiles::getFiles');
+
+    Server
+      .sendGET("kernel.ast.getFiles", [], false)
+      .then(
+        (fnames) => {
+          console.log('Got source filenames:' + fnames);
+          setFilenames(fnames);
+        });
+  }
+
+  useEffect(getSourceFilenames, []);
+
+  useEffect(() => {
+    if (filenames.length > 0) {
+      console.log('Displaying ' + filenames[0] + '!');
+      System
+        .readFile(filenames[0])
+        .then(
+          (fcontent) => {
+            console.log('Got file content: ' + fcontent);
+            buffer.clear();
+            buffer.append(fcontent);
+          });
+    }
+  }, [filenames])
+
+  return (
+    <Text buffer={buffer} readOnly='true' lineNumbers='true' />
+  )
+}
diff --git a/ivette/electron-webpack.json b/ivette/electron-webpack.json
new file mode 100644
index 0000000000000000000000000000000000000000..048484644edbcf136112d58accdf3e584cda1979
--- /dev/null
+++ b/ivette/electron-webpack.json
@@ -0,0 +1,22 @@
+// --------------------------------------------------------------------------
+// --- Electron-webpack configuration file for Dome
+// --------------------------------------------------------------------------
+
+/*
+   Template from $(DOME)/template/electron-webpack.json
+
+   This configuration file makes electron-webpack loading
+   webpack extensions to resolve Dome packages differently
+   for electron main-process and renderer-process.
+
+   You may configure further electron-webpack to your convenience.
+*/
+
+{
+  "main": {
+    "webpackConfig": "webpack.main.js"
+  },
+  "renderer": {
+    "webpackConfig": "webpack.renderer.js"
+  }
+}
diff --git a/ivette/package.json b/ivette/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..f64e23e2cd377d25fb48ad301f179aef78712d38
--- /dev/null
+++ b/ivette/package.json
@@ -0,0 +1,36 @@
+{
+  "name": "Ivette",
+  "version": "1.0.0",
+  "main": "index.js",
+  "repository": "git@git.frama-c.com:frama-c/Ivette.git",
+  "author": "Loïc Correnson <loic.correnson@cea.fr>",
+  "license": "MIT",
+  "devDependencies": {
+    "@babel/core": "^7",
+    "@babel/plugin-proposal-object-rest-spread": "^7.7.7",
+    "@babel/preset-env": "^7.7.7",
+    "@babel/preset-react": "^7.7.4",
+    "@hot-loader/react-dom": "^16.11.0",
+    "@types/react": "^16.9.17",
+    "babel-loader": "^8.0.6",
+    "css-loader": "^3.4.0",
+    "electron": "^7",
+    "electron-builder": "^21.2.0",
+    "electron-devtools-installer": "^2.2.4",
+    "electron-webpack": "^2.7.4",
+    "jsdoc": "^3.6.3",
+    "react-hot-loader": "^4.12.18",
+    "webpack": "^4.41.4"
+  },
+  "dependencies": {
+    "codemirror": "^5.49.2",
+    "immutable": "^4.0.0-rc.12",
+    "lodash": "^4.17.15",
+    "react": "^16.8",
+    "react-dom": "^16.12.0",
+    "react-draggable": "^4.2.0",
+    "react-virtualized": "^9.21.2",
+    "source-map-support": "^0.5.16",
+    "zeromq": "^6.0.0-beta.5"
+  }
+}
diff --git a/ivette/src/dome/.gitignore b/ivette/src/dome/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..7cd1e3e820fe332ae0b7c2364e684d9dfebb5d87
--- /dev/null
+++ b/ivette/src/dome/.gitignore
@@ -0,0 +1,53 @@
+#########################################
+# Makefile
+#########################################
+
+.dome-*.stamp
+.dome-*.back
+.dome-*.orig
+.dome-*.lock
+
+#########################################
+# Dome Repository
+#########################################
+
+/.gitdome
+
+#########################################
+# JavaScript
+#########################################
+
+node_modules
+yarn.lock
+/package.json
+
+#########################################
+# Example Application
+#########################################
+
+/.babelrc
+/webpack.main.js
+/webpack.renderer.js
+/webpack.plugin.js
+/electron-webpack.json
+/dist
+/bin
+/src/main/index.js
+/src/misc/exports.js
+/src/renderer/index.js
+/src/renderer/Application.js
+/src/renderer/Preferences.js
+/src/renderer/styles.css
+/src/renderer/demo
+/src/plugins
+
+#########################################
+# Documentation
+#########################################
+
+/html
+/doc/template/gallery-icons.html
+/doc/template/gallery-styles.js
+/doc/template/gallery-styles.html
+
+#########################################
diff --git a/ivette/src/dome/CHANGELOG.md b/ivette/src/dome/CHANGELOG.md
new file mode 100644
index 0000000000000000000000000000000000000000..4e51470c1da281d1264d99637ca279df412f3ac7
--- /dev/null
+++ b/ivette/src/dome/CHANGELOG.md
@@ -0,0 +1 @@
+# Version 0.0.1
diff --git a/ivette/src/dome/CONTRIBUTING.md b/ivette/src/dome/CONTRIBUTING.md
new file mode 100644
index 0000000000000000000000000000000000000000..718b8d2fcd61ed47dedb4a630db6eea79a3402d2
--- /dev/null
+++ b/ivette/src/dome/CONTRIBUTING.md
@@ -0,0 +1,56 @@
+## Authorship
+
+All contributions are proprietary of CEA Tech.
+An exhaustive list of authors is maintained in [CONTRIBUTORS.md] file.
+
+## Git Conventions
+
+The branches are organized as follows:
+
+- `master` is the prototype for building the next release.
+- `stable` is the current working candidate for the next release.
+It shall be an ancestor of `master` and is considered ready to be distributed.
+- `release/DOME-vN.N.N` are tags pointing to public releases of the framework.
+- `feature/*` are branches where new development are experimented before being merged
+into `master`.
+
+The release branches `master`, `stable` and `release/*` are protected and only
+release managers are authorized to merge into.
+
+It is highly recommended to not rebase `feature` branches already pushed on the repository,
+unless there is only one contributor to the feature, on order to avoid losing data.
+
+## Javascript Coding Rules
+
+Your favorite editor shall be configured such that:
+- indentation in two-spaces per level, with no tabulation
+- module names are lowercase identifiers
+- react compomnents are capitalized
+
+## Framework Organization
+
+- [CHANGELOG.md] tracks modifications with releases
+- [CONTRIBUTING.md] recommandations for Dome developpers
+- [CONTRIBUTORS.md] maintains the list of authors
+- [LICENSE.md] framework terms and conditions
+- [README.md] public description
+- `./Makefile` framework quick-testing
+- `./doc` framework documentation resources
+- `./dist` local build for tests (generated)
+- `./html` local documentation (generated)
+- `./src/dome` framework sources
+- `./template` framework configuration templates
+
+For developping an application with **Dome**, follow the indications
+provided by the [README.md] file.
+
+The root Makefile is _not_ designed for
+developping an application with the **Dome** framework.
+See `make` or `make help` for details.
+
+This Makefile has been designed for live-editing and quick-testing the
+framework itself (see « Live Editing » chapter of Html documentation).
+You can type `make dev` to generate an application in `.` and live-edit the
+generated `./src/main/*.js` and `./src/renderer/*.js` files.
+Then, `make app` and `make dist` can be used to test the framework in production mode.
+Use `make doc` to build the documentation in `./html` directory.
diff --git a/ivette/src/dome/CONTRIBUTORS.md b/ivette/src/dome/CONTRIBUTORS.md
new file mode 100644
index 0000000000000000000000000000000000000000..f7753c13aed32ec8ff2062f3fd18b82762137233
--- /dev/null
+++ b/ivette/src/dome/CONTRIBUTORS.md
@@ -0,0 +1,8 @@
+## Dome Contributors
+
+(by order of appearance)
+
+- [Loïc Correnson](loic.correnson@cea.fr), CEA-LIST, main architect
+- [Franck Pujol](franck.pujol@atos.net), ATOS, early designer
+- [Valentin Perrelle](valentin.perrelle@cea.fr), CEA-LIST, beta tester
+- [Andre Maroneze](andre.oliveiramaroneze@cea.fr), CEA-LIST, beta tester
diff --git a/ivette/src/dome/LICENSE.md b/ivette/src/dome/LICENSE.md
new file mode 100644
index 0000000000000000000000000000000000000000..90f4610c92560f23a6273bfe73812313a21ae403
--- /dev/null
+++ b/ivette/src/dome/LICENSE.md
@@ -0,0 +1,5 @@
+Copyright (C) 2016
+CEA (Commissariat à l'énergie atomique et aux énergies alternatives)
+
+All rights reserved.
+Contact CEA LIST for licensing.
diff --git a/ivette/src/dome/Makefile b/ivette/src/dome/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..91d1bf27385974c111d0cc69543f9870a1959f09
--- /dev/null
+++ b/ivette/src/dome/Makefile
@@ -0,0 +1,45 @@
+# --------------------------------------------------------------------------
+help:
+	@echo "*****************************************"
+	@echo "***      Dome Framework Makefile      ***"
+	@echo "*****************************************"
+	@echo
+	@echo "   make doc: alias for dome-doc"
+	@echo "   make dev: alias for dome-dev"
+	@echo "   make app: alias for dome-app"
+	@echo "   make dist: alias for dome-dist"
+	@echo "   make dome-help: detailed help for dome"
+	@echo "   make DEMO=<xxx> demo: switch to 'src/renderer/demo/xxx.js' "
+	@echo
+	@echo "*****************************************"
+
+# --------------------------------------------------------------------------
+
+APP=Dome
+DOME=.
+DEMO=Hello
+
+include $(DOME)/template/makefile
+
+.PHONY: doc demo dev app dist
+
+doc: dome-doc
+dev: dome-dev
+app: dome-app
+dist: dome-dist
+
+demo: src/renderer/demo/$(DEMO).js
+	@echo "Switch to src/renderer/demo/$(DEMO).js"
+	@rm -f src/renderer/Application.js
+	@echo "// Dome Demonstrator" >> src/renderer/Application.js
+	@echo "import React from 'react' ;"  >> src/renderer/Application.js
+	@echo "import Main from './demo/$(DEMO).js' ;" >> src/renderer/Application.js
+	@echo "export default (() => <Main/>);" >> src/renderer/Application.js
+
+src/renderer/demo/%.js:
+	@echo "Generate  $@"
+	@echo "// Dome Demonstrator" > $@
+	@echo "import React from 'react';" >> $@
+	@echo "export default (() => <h1>$* Demo</h1>);" >> $@
+
+# --------------------------------------------------------------------------
diff --git a/ivette/src/dome/README.md b/ivette/src/dome/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..049962c686f5719ef5cf8e277e3abeeb156b0e57
--- /dev/null
+++ b/ivette/src/dome/README.md
@@ -0,0 +1,57 @@
+# Dome Framework
+
+This project allows you to build and distribute desktop applications.
+The proposed framework is based on **React** [1] and **Electron** [2].
+
+## Quick Start
+
+We strongly recommand to use **Yarn** [3] for installing the
+necessary **Node** packages and their dependencies. Both shall be installed
+with decently recent versions (`yarn -v 1.5.*` and `node -v 8.*`).
+
+Then, prepare a directory for developing your application, and download
+the Dome Application Framework into sub-directory `dome`.
+You shall setup a makefile for your application as follows:
+
+```
+include dome/template/makefile
+all: dome-dev
+```
+
+Now, simply type `make` and all the necessary packages will be automatically
+installed and configured. This can take a while for the very first installation.
+Eventually, your application shall start with a « _Hello World_ » window.  Now,
+simply live edit the generated `src/renderer/Application.js` which is the main
+entry point of your application.
+
+## Advanced Usage
+
+You may install the framework in any directory,
+provided you set the `DOME` makefile or environment variable accordingly.
+By default, this variable is set to `./dome` by the template makefile.
+
+Type `make help` to see all the predefined compilation targets.
+In particular, `make dome-doc` will generate the Dome Documentation locally, in
+`$(DOME)/html/index.hml`.
+
+The `$(DOME)/template/makefile` is provided with usefull targets, but you can also
+use your own scripts. All generated configuration files are borrowed from
+resources located in the `$(DOME)/template` directory. You can extend and modify
+all the generated files, simply follow the comments provided.
+
+Typicall installation places for the Dome framework are:
+- `DOME=./dome` for usual application developpers;
+- `DOME=./src/dome` for application developpers that also have to hack and live-edit the Dome framework;
+- `DOME=.` is also possible to quick test the framework or make a demo in `src/renderer/Application.js`.
+
+The last option is not possible when installing the framework since it would conflict with your
+own project development. For instance, if your install Dome with `git`, you must keep the `$(DOME)/.git`
+distinct and ignore the entire `$(DOME)` directory in your own versionned files.
+
+## References
+
+[1] https://reactjs.org
+
+[2] https://electronjs.org
+
+[3] https://yarnpkg.com/lang/en/docs/install/
diff --git a/ivette/src/dome/doc/README.md b/ivette/src/dome/doc/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..8559ab10dac0ceefca552bb97d4a41ced8673e75
--- /dev/null
+++ b/ivette/src/dome/doc/README.md
@@ -0,0 +1,26 @@
+# Introduction
+
+**Dome** allows you to build native, desktop applications using
+[React](https://reactjs.org) javascript technology. It is build upon the
+[Electron](http://electron.atom.io) framework, featuring a full-featured Chromium
+engine which renders the graphical user interface to the user.
+
+The **Dome** framework provides you with the following facilities:
+- all-in-one pre-configured rich development environment
+- live-editing of your application code
+- a collection of ready-to-use, well designed, scalable components
+- easy integration with any other React components from your own
+- binary distribution for all supported platform (Windows, Linux and MacOS)
+
+## To Start With
+
+- [Quick Start](tutorial-quickstart.html)
+- [Live Editing](tutorial-hotreload.html)
+- [Application Design](tutorial-application.html)
+- [Application Development](tutorial-development.html)
+
+## References
+
+- [Custom Hooks](tutorial-hooks.html)
+- [Icon Gallery](tutorial-icons.html)
+- [Glossary](tutorial-glossary.html)
diff --git a/ivette/src/dome/doc/config.json b/ivette/src/dome/doc/config.json
new file mode 100644
index 0000000000000000000000000000000000000000..5afa1619947dcfad43202fc93c29fefa3268d6ed
--- /dev/null
+++ b/ivette/src/dome/doc/config.json
@@ -0,0 +1,18 @@
+{
+  "source": {
+    "include": [ "doc/README.md", "src" ]
+  },
+  "opts": {
+    "recurse": true,
+    "tutorials": "doc/tutorials",
+    "template": "doc/template"
+  },
+  "plugins": [ "plugins/markdown" ],
+  "templates": {
+    "default": {
+      "useLongnameInNav": true,
+      "outputSourceFiles": false,
+      "includeDate": false
+    }
+  }
+}
diff --git a/ivette/src/dome/doc/gallery.css b/ivette/src/dome/doc/gallery.css
new file mode 100644
index 0000000000000000000000000000000000000000..128e7f8bacf9facaecac2755b028ab705e2e91f8
--- /dev/null
+++ b/ivette/src/dome/doc/gallery.css
@@ -0,0 +1,68 @@
+/* -------------------------------------------------------------------------- */
+/* --- Galleries & Palettes CSS                                           --- */
+/* -------------------------------------------------------------------------- */
+
+.database {
+    width: 100% ;
+    height: auto ;
+    display: flex ;
+    flex-direction: row ;
+    flex-wrap: wrap ;
+}
+
+.refname {
+    -webkit-user-select: text; /* Safari 3.1+ */
+    -moz-user-select: text; /* Firefox 2+ */
+    -ms-user-select: text; /* IE 10+ */
+    user-select: text; /* Standard syntax */
+    font-family: Consolas, Monaco, 'Andale Mono', monospace;
+    font-size: 14px ;
+    color: #4D8053;
+}
+
+/* -------------------------------------------------------------------------- */
+/* --- Additional CSS for Icon Gallery                                    --- */
+/* -------------------------------------------------------------------------- */
+
+.icon-block {
+    margin: 3pt ;
+    padding: 2pt ;
+    display: flex ;
+    flex-direction: row ;
+    flex-wrap: nowrap ;
+    align-items: center ;
+}
+
+.icon-block:hover {
+    background: lightblue ;
+}
+
+.icon-name {
+    order: 2 ;
+    padding: 2pt ;
+    margin-left: 2pt ;
+    vertical-align: baseline ;
+    cursor: text ;
+    display: inline ;
+}
+
+.icon-name:target {
+    background: orange ;
+}
+
+.icon-name:hover, .icon-name:focus {
+    outline-style: none ;
+    text-decoration: none ;
+}
+
+.icon-svg {
+    order: 1 ;
+    width: 20px ;
+    height: 20px ;
+}
+
+/* -------------------------------------------------------------------------- */
+/* --- Additional CSS for Style Palette                                   --- */
+/* -------------------------------------------------------------------------- */
+
+/* -------------------------------------------------------------------------- */
diff --git a/ivette/src/dome/doc/icons.js b/ivette/src/dome/doc/icons.js
new file mode 100755
index 0000000000000000000000000000000000000000..0867937a8fc83ff2bfb4b2d08224758530490249
--- /dev/null
+++ b/ivette/src/dome/doc/icons.js
@@ -0,0 +1,82 @@
+// --------------------------------------------------------------------------
+// --- Icons Gallery Generator
+// --------------------------------------------------------------------------
+
+const path = require('path');
+const icons = require(path.resolve( process.argv[2] ));
+const index = [];
+const sections = {};
+
+// --- Indexing & Sectioning -----------------------------------------
+
+for ( var name in icons ) {
+  const icon = icons[name];
+  const section = icon.section || "Others" ;
+  if (section != "Others" && index.indexOf(section) < 0) index.push( section );
+  var content = sections[section] ;
+  if (!content) sections[section] = content = [] ;
+  content.push(name) ;
+}
+
+index.sort();
+if (sections["Others"]) index.push("Others");
+
+console.log( `<div id="main">` );
+
+// --- Gallery (per section) -----------------------------------------
+
+console.log( `<div id="content">` );
+console.log( `<div id="page">` );
+console.log( `  <h1 class="page-title">Icons Gallery</h1>` );
+
+for ( var s = 0 ; s < index.length ; s++ ) {
+  const section = index[s] ;
+  console.log( `<section>` );
+  console.log( `<article><h4 id="SECTION.${section}">${section}</h4>` );
+  console.log( `  <div class="database">` );
+
+  const content = sections[section] ;
+  content.sort();
+
+  for( var k = 0 ; k < content.length ; k++ ) {
+    const name = content[k];
+    const icon = icons[name];
+    const title = icon.title || name ;
+    console.log( `  <div class="icon-block" onclick="window.location.href='#ICON.${name}'" title="${title}">`);
+    console.log( `    <div class="icon-name refname" id="ICON.${name}">${name}</div>` );
+    console.log( `    <svg class="icon-svg" viewBox="${icon.viewBox}">` );
+    console.log( `      <path d="${icon.path}"/>`);
+    console.log( `    </svg>` );
+    console.log( `  </div>` );
+  }
+  console.log( `  </div>` );
+  console.log( `</article>` );
+  console.log( `</section>` );
+}
+
+console.log( `</div>` ); // #content
+console.log( `</div>` ); // #page
+
+// --- Navigation Bar ------------------------------------------------
+
+console.log( `<div id="index">` );
+console.log( `<nav>` );
+console.log( `<h2><a href="index.html">Dome</a></h2>` );
+console.log( `<h3>Classes</h3>` );
+console.log( `<ul><li><a href="module-dome_controls_icons.Icon.html">Icon (Component)</a></li>` );
+for ( var s=0; s < index.length ; s++ ) {
+  const section = index[s];
+  console.log( `  <li><a href="#SECTION.${section}">${section}</a></li>` );
+}
+console.log( `</ul>` );
+console.log( `<h3>Icons</h3><ul>` );
+for( var name in icons ) {
+  console.log( `<li><a href="#ICON.${name}">${name}</a></li>` );
+}
+console.log( `</ul>`);
+console.log( `</nav>` );
+console.log( `</div>` ); // #index
+
+console.log( `</div>` ); // #main
+
+// -------------------------------------------------------------------
diff --git a/ivette/src/dome/doc/template/gallery-head.html b/ivette/src/dome/doc/template/gallery-head.html
new file mode 100644
index 0000000000000000000000000000000000000000..30ff5781d97df4e83a14566a7f447a946f11c601
--- /dev/null
+++ b/ivette/src/dome/doc/template/gallery-head.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="utf-8">
+    <title>Dome</title>
+    <script src="scripts/prettify/prettify.js"> </script>
+    <script src="scripts/prettify/lang-css.js"> </script>
+    <!--[if lt IE 9]>
+      <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
+    <![endif]-->
+    <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
+    <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
+    <link type="text/css" rel="stylesheet" href="styles/gallery.css">
+</head>
+<body>
diff --git a/ivette/src/dome/doc/template/gallery-tail.html b/ivette/src/dome/doc/template/gallery-tail.html
new file mode 100644
index 0000000000000000000000000000000000000000..e29004ae18363bf68f2c9891cbd2307ac8ad583d
--- /dev/null
+++ b/ivette/src/dome/doc/template/gallery-tail.html
@@ -0,0 +1,11 @@
+
+<br class="clear"/>
+
+<footer>
+Copyright (&#169;) CEA Tech 2017
+</footer>
+
+<script> prettyPrint(); </script>
+<script src="scripts/linenumber.js"> </script>
+</body>
+</html>
diff --git a/ivette/src/dome/doc/template/publish.js b/ivette/src/dome/doc/template/publish.js
new file mode 100644
index 0000000000000000000000000000000000000000..94a2514c517647b047e421aa3820a1cfc9b7070f
--- /dev/null
+++ b/ivette/src/dome/doc/template/publish.js
@@ -0,0 +1,673 @@
+'use strict';
+
+var doop = require('jsdoc/util/doop');
+var env = require('jsdoc/env');
+var fs = require('jsdoc/fs');
+var helper = require('jsdoc/util/templateHelper');
+var logger = require('jsdoc/util/logger');
+var path = require('jsdoc/path');
+var taffy = require('taffydb').taffy;
+var template = require('jsdoc/template');
+var util = require('util');
+
+var htmlsafe = helper.htmlsafe;
+var linkto = helper.linkto;
+var resolveAuthorLinks = helper.resolveAuthorLinks;
+var scopeToPunc = helper.scopeToPunc;
+var hasOwnProp = Object.prototype.hasOwnProperty;
+
+var data;
+var view;
+
+var outdir = path.normalize(env.opts.destination);
+
+function find(spec) {
+    return helper.find(data, spec);
+}
+
+function tutoriallink(tutorial) {
+    return helper.toTutorial(tutorial, null, { tag: 'em', classname: 'disabled', prefix: 'Tutorial: ' });
+}
+
+function getAncestorLinks(doclet) {
+    return helper.getAncestorLinks(data, doclet);
+}
+
+function hashToLink(doclet, hash) {
+    if ( !/^(#.+)/.test(hash) ) { return hash; }
+
+    var url = helper.createLink(doclet);
+
+    url = url.replace(/(#.+|$)/, hash);
+    return '<a href="' + url + '">' + hash + '</a>';
+}
+
+function needsSignature(doclet) {
+    var needsSig = false;
+
+    // function and class definitions always get a signature
+    if (doclet.kind === 'function' || doclet.kind === 'class') {
+        needsSig = true;
+    }
+    // typedefs that contain functions get a signature, too
+    else if (doclet.kind === 'typedef' && doclet.type && doclet.type.names &&
+        doclet.type.names.length) {
+        for (var i = 0, l = doclet.type.names.length; i < l; i++) {
+            if (doclet.type.names[i].toLowerCase() === 'function') {
+                needsSig = true;
+                break;
+            }
+        }
+    }
+
+    return needsSig;
+}
+
+function getSignatureAttributes(item) {
+    var attributes = [];
+
+    if (item.optional) {
+        attributes.push('opt');
+    }
+
+    if (item.nullable === true) {
+        attributes.push('nullable');
+    }
+    else if (item.nullable === false) {
+        attributes.push('non-null');
+    }
+
+    return attributes;
+}
+
+function updateItemName(item) {
+    var attributes = getSignatureAttributes(item);
+    var itemName = item.name || '';
+
+    if (item.variable) {
+        itemName = '&hellip;' + itemName;
+    }
+
+    if (attributes && attributes.length) {
+        itemName = util.format( '%s<span class="signature-attributes">%s</span>', itemName,
+            attributes.join(', ') );
+    }
+
+    return itemName;
+}
+
+function addParamAttributes(params) {
+    return params.filter(function(param) {
+        return param.name && param.name.indexOf('.') === -1;
+    }).map(updateItemName);
+}
+
+function buildItemTypeStrings(item) {
+    var types = [];
+
+    if (item && item.type && item.type.names) {
+        item.type.names.forEach(function(name) {
+            types.push( linkto(name, htmlsafe(name)) );
+        });
+    }
+
+    return types;
+}
+
+function buildAttribsString(attribs) {
+    var attribsString = '';
+
+    if (attribs && attribs.length) {
+        attribsString = htmlsafe( util.format('(%s) ', attribs.join(', ')) );
+    }
+
+    return attribsString;
+}
+
+function addNonParamAttributes(items) {
+    var types = [];
+
+    items.forEach(function(item) {
+        types = types.concat( buildItemTypeStrings(item) );
+    });
+
+    return types;
+}
+
+function addSignatureParams(f) {
+    var params = f.params ? addParamAttributes(f.params) : [];
+
+    f.signature = util.format( '%s(%s)', (f.signature || ''), params.join(', ') );
+}
+
+function addSignatureReturns(f) {
+    var attribs = [];
+    var attribsString = '';
+    var returnTypes = [];
+    var returnTypesString = '';
+
+    // jam all the return-type attributes into an array. this could create odd results (for example,
+    // if there are both nullable and non-nullable return types), but let's assume that most people
+    // who use multiple @return tags aren't using Closure Compiler type annotations, and vice-versa.
+    if (f.returns) {
+        f.returns.forEach(function(item) {
+            helper.getAttribs(item).forEach(function(attrib) {
+                if (attribs.indexOf(attrib) === -1) {
+                    attribs.push(attrib);
+                }
+            });
+        });
+
+        attribsString = buildAttribsString(attribs);
+    }
+
+    if (f.returns) {
+        returnTypes = addNonParamAttributes(f.returns);
+    }
+    if (returnTypes.length) {
+        returnTypesString = util.format( ' &rarr; %s{%s}', attribsString, returnTypes.join('|') );
+    }
+
+    f.signature = '<span class="signature">' + (f.signature || '') + '</span>' +
+        '<span class="type-signature">' + returnTypesString + '</span>';
+}
+
+function addSignatureTypes(f) {
+    var types = f.type ? buildItemTypeStrings(f) : [];
+
+    f.signature = (f.signature || '') + '<span class="type-signature">' +
+        (types.length ? ' :' + types.join('|') : '') + '</span>';
+}
+
+function addAttribs(f) {
+    var attribs = helper.getAttribs(f);
+    var attribsString = buildAttribsString(attribs);
+
+    f.attribs = util.format('<span class="type-signature">%s</span>', attribsString);
+}
+
+function shortenPaths(files, commonPrefix) {
+    Object.keys(files).forEach(function(file) {
+        files[file].shortened = files[file].resolved.replace(commonPrefix, '')
+            // always use forward slashes
+            .replace(/\\/g, '/');
+    });
+
+    return files;
+}
+
+function getPathFromDoclet(doclet) {
+    if (!doclet.meta) {
+        return null;
+    }
+
+    return doclet.meta.path && doclet.meta.path !== 'null' ?
+        path.join(doclet.meta.path, doclet.meta.filename) :
+        doclet.meta.filename;
+}
+
+function generate(title, docs, filename, resolveLinks) {
+    resolveLinks = resolveLinks === false ? false : true;
+
+    var docData = {
+        env: env,
+        title: title,
+        docs: docs
+    };
+
+    var outpath = path.join(outdir, filename),
+        html = view.render('container.tmpl', docData);
+
+    if (resolveLinks) {
+        html = helper.resolveLinks(html); // turn {@link foo} into <a href="foodoc.html">foo</a>
+    }
+
+    fs.writeFileSync(outpath, html, 'utf8');
+}
+
+function generateSourceFiles(sourceFiles, encoding) {
+    encoding = encoding || 'utf8';
+    Object.keys(sourceFiles).forEach(function(file) {
+        var source;
+        // links are keyed to the shortened path in each doclet's `meta.shortpath` property
+        var sourceOutfile = helper.getUniqueFilename(sourceFiles[file].shortened);
+        helper.registerLink(sourceFiles[file].shortened, sourceOutfile);
+
+        try {
+            source = {
+                kind: 'source',
+                code: helper.htmlsafe( fs.readFileSync(sourceFiles[file].resolved, encoding) )
+            };
+        }
+        catch(e) {
+            logger.error('Error while generating source file %s: %s', file, e.message);
+        }
+
+        generate('Source: ' + sourceFiles[file].shortened, [source], sourceOutfile,
+            false);
+    });
+}
+
+/**
+ * Look for classes or functions with the same name as modules (which indicates that the module
+ * exports only that class or function), then attach the classes or functions to the `module`
+ * property of the appropriate module doclets. The name of each class or function is also updated
+ * for display purposes. This function mutates the original arrays.
+ *
+ * @private
+ * @param {Array.<module:jsdoc/doclet.Doclet>} doclets - The array of classes and functions to
+ * check.
+ * @param {Array.<module:jsdoc/doclet.Doclet>} modules - The array of module doclets to search.
+ */
+function attachModuleSymbols(doclets, modules) {
+    var symbols = {};
+
+    // build a lookup table
+    doclets.forEach(function(symbol) {
+        symbols[symbol.longname] = symbols[symbol.longname] || [];
+        symbols[symbol.longname].push(symbol);
+    });
+
+    return modules.map(function(module) {
+        if (symbols[module.longname]) {
+            module.modules = symbols[module.longname]
+                // Only show symbols that have a description. Make an exception for classes, because
+                // we want to show the constructor-signature heading no matter what.
+                .filter(function(symbol) {
+                    return symbol.description || symbol.kind === 'class';
+                })
+                .map(function(symbol) {
+                    symbol = doop(symbol);
+
+                    if (symbol.kind === 'class' || symbol.kind === 'function') {
+                        symbol.name = symbol.name.replace('module:', '(require("') + '"))';
+                    }
+
+                    return symbol;
+                });
+        }
+    });
+}
+
+function buildMemberNav(items, itemHeading, itemsSeen, linktoFn) {
+    var nav = '';
+
+    if (items.length) {
+        var itemsNav = '';
+
+        items.forEach(function(item) {
+            if ( !hasOwnProp.call(item, 'longname') ) {
+                itemsNav += '<li>' + linktoFn('', item.name) + '</li>';
+            }
+            else if ( !hasOwnProp.call(itemsSeen, item.longname) ) {
+                var displayName;
+                if (env.conf.templates.default.useLongnameInNav) {
+                    displayName = item.longname;
+                } else {
+                    displayName = item.name;
+                }
+                itemsNav += '<li>' + linktoFn(item.longname, displayName.replace(/\b(module|event):/g, '')) + '</li>';
+
+                itemsSeen[item.longname] = true;
+            }
+        });
+
+        if (itemsNav !== '') {
+            nav += '<h3>' + itemHeading + '</h3><ul>' + itemsNav + '</ul>';
+        }
+    }
+
+    return nav;
+}
+
+function linktoTutorial(longName, name) {
+    return tutoriallink(name);
+}
+
+function linktoExternal(longName, name) {
+    return linkto(longName, name.replace(/(^"|"$)/g, ''));
+}
+
+/**
+ * Create the navigation sidebar.
+ * @param {object} members The members that will be used to create the sidebar.
+ * @param {array<object>} members.classes
+ * @param {array<object>} members.externals
+ * @param {array<object>} members.globals
+ * @param {array<object>} members.mixins
+ * @param {array<object>} members.modules
+ * @param {array<object>} members.namespaces
+ * @param {array<object>} members.tutorials
+ * @param {array<object>} members.events
+ * @param {array<object>} members.interfaces
+ * @return {string} The HTML for the navigation sidebar.
+ */
+function buildNav(members) {
+  const title = process.env.APP || 'Dome' ;
+    var nav = '<h2><a href="index.html">' + title + '</a></h2>';
+    var seen = {};
+    var seenTutorials = {};
+
+  const indexTutorials = require( path.resolve(process.env.DOME,'doc/tutorials/index.json' ));
+  const sortedTutorials = members.tutorials.sort((a,b) => {
+    const ta = indexTutorials[a.name] ;
+    const tb = indexTutorials[b.name] ;
+    const ra = (ta && ta.order) || 0 ;
+    const rb = (tb && tb.order) || 0 ;
+    return ra - rb ;
+  });
+
+  const isDome = (item) =>
+        (item.name && item.name.startsWith('dome')) ||
+        (item.longname && item.longname.startsWith('dome')) ;
+  const isApp = (item) => !isDome(item);
+  const domeModules = members.modules.filter(isDome);
+  const appModules = members.modules.filter(isApp);
+
+    nav += buildMemberNav(sortedTutorials, 'Tutorials', seenTutorials, linktoTutorial);
+    nav += buildMemberNav(appModules, `${title} Modules`, {}, linkto);
+    nav += buildMemberNav(domeModules, 'Dome Modules', {}, linkto);
+    nav += buildMemberNav(members.events, 'Events', seen, linkto);
+    nav += buildMemberNav(members.classes, 'Classes', seen, linkto);
+    nav += buildMemberNav(members.interfaces, 'Interfaces', seen, linkto);
+    nav += buildMemberNav(members.mixins, 'Mixins', seen, linkto);
+    nav += buildMemberNav(members.namespaces, 'Namespaces', seen, linkto);
+    nav += buildMemberNav(members.externals, 'Externals', seen, linktoExternal);
+
+    if (members.globals.length) {
+        var globalNav = '';
+
+        members.globals.forEach(function(g) {
+            if ( g.kind !== 'typedef' && !hasOwnProp.call(seen, g.longname) ) {
+                globalNav += '<li>' + linkto(g.longname, g.name) + '</li>';
+            }
+            seen[g.longname] = true;
+        });
+
+        if (!globalNav) {
+            // turn the heading into a link so you can actually get to the global page
+            nav += '<h3>' + linkto('global', 'Global') + '</h3>';
+        }
+        else {
+            nav += '<h3>Global</h3><ul>' + globalNav + '</ul>';
+        }
+    }
+
+    return nav;
+}
+
+/**
+    @param {TAFFY} taffyData See <http://taffydb.com/>.
+    @param {object} opts
+    @param {Tutorial} tutorials
+ */
+exports.publish = function(taffyData, opts, tutorials) {
+    data = taffyData;
+
+    var conf = env.conf.templates || {};
+    conf.default = conf.default || {};
+
+    var templatePath = path.normalize(opts.template);
+    view = new template.Template( path.join(templatePath, 'tmpl') );
+
+    // claim some special filenames in advance, so the All-Powerful Overseer of Filename Uniqueness
+    // doesn't try to hand them out later
+    var indexUrl = helper.getUniqueFilename('index');
+    // don't call registerLink() on this one! 'index' is also a valid longname
+
+    var globalUrl = helper.getUniqueFilename('global');
+    helper.registerLink('global', globalUrl);
+
+    // set up templating
+    view.layout = conf.default.layoutFile ?
+        path.getResourcePath(path.dirname(conf.default.layoutFile),
+            path.basename(conf.default.layoutFile) ) :
+        'layout.tmpl';
+
+    // set up tutorials for helper
+    helper.setTutorials(tutorials);
+
+    data = helper.prune(data);
+    data.sort('longname, version, since');
+    helper.addEventListeners(data);
+
+    var sourceFiles = {};
+    var sourceFilePaths = [];
+    data().each(function(doclet) {
+         doclet.attribs = '';
+
+        if (doclet.examples) {
+            doclet.examples = doclet.examples.map(function(example) {
+                var caption, code;
+
+                if (example.match(/^\s*<caption>([\s\S]+?)<\/caption>(\s*[\n\r])([\s\S]+)$/i)) {
+                    caption = RegExp.$1;
+                    code = RegExp.$3;
+                }
+
+                return {
+                    caption: caption || '',
+                    code: code || example
+                };
+            });
+        }
+        if (doclet.see) {
+            doclet.see.forEach(function(seeItem, i) {
+                doclet.see[i] = hashToLink(doclet, seeItem);
+            });
+        }
+
+        // build a list of source files
+        var sourcePath;
+        if (doclet.meta) {
+            sourcePath = getPathFromDoclet(doclet);
+            sourceFiles[sourcePath] = {
+                resolved: sourcePath,
+                shortened: null
+            };
+            if (sourceFilePaths.indexOf(sourcePath) === -1) {
+                sourceFilePaths.push(sourcePath);
+            }
+        }
+    });
+
+    // update outdir if necessary, then create outdir
+    var packageInfo = ( find({kind: 'package'}) || [] ) [0];
+    if (packageInfo && packageInfo.name) {
+        outdir = path.join( outdir, packageInfo.name, (packageInfo.version || '') );
+    }
+    fs.mkPath(outdir);
+
+    // copy the template's static files to outdir
+    var fromDir = path.join(templatePath, 'static');
+    var staticFiles = fs.ls(fromDir, 3);
+
+    staticFiles.forEach(function(fileName) {
+        var toDir = fs.toDir( fileName.replace(fromDir, outdir) );
+        fs.mkPath(toDir);
+        fs.copyFileSync(fileName, toDir);
+    });
+
+    // copy user-specified static files to outdir
+    var staticFilePaths;
+    var staticFileFilter;
+    var staticFileScanner;
+    if (conf.default.staticFiles) {
+        // The canonical property name is `include`. We accept `paths` for backwards compatibility
+        // with a bug in JSDoc 3.2.x.
+        staticFilePaths = conf.default.staticFiles.include ||
+            conf.default.staticFiles.paths ||
+            [];
+        staticFileFilter = new (require('jsdoc/src/filter')).Filter(conf.default.staticFiles);
+        staticFileScanner = new (require('jsdoc/src/scanner')).Scanner();
+
+        staticFilePaths.forEach(function(filePath) {
+            var extraStaticFiles;
+
+            filePath = path.resolve(env.pwd, filePath);
+            extraStaticFiles = staticFileScanner.scan([filePath], 10, staticFileFilter);
+
+            extraStaticFiles.forEach(function(fileName) {
+                var sourcePath = fs.toDir(filePath);
+                var toDir = fs.toDir( fileName.replace(sourcePath, outdir) );
+                fs.mkPath(toDir);
+                fs.copyFileSync(fileName, toDir);
+            });
+        });
+    }
+
+    if (sourceFilePaths.length) {
+        sourceFiles = shortenPaths( sourceFiles, path.commonPrefix(sourceFilePaths) );
+    }
+    data().each(function(doclet) {
+        var url = helper.createLink(doclet);
+        helper.registerLink(doclet.longname, url);
+
+        // add a shortened version of the full path
+        var docletPath;
+        if (doclet.meta) {
+            docletPath = getPathFromDoclet(doclet);
+            docletPath = sourceFiles[docletPath].shortened;
+            if (docletPath) {
+                doclet.meta.shortpath = docletPath;
+            }
+        }
+    });
+
+    data().each(function(doclet) {
+        var url = helper.longnameToUrl[doclet.longname];
+
+        if (url.indexOf('#') > -1) {
+            doclet.id = helper.longnameToUrl[doclet.longname].split(/#/).pop();
+        }
+        else {
+            doclet.id = doclet.name;
+        }
+
+        if ( needsSignature(doclet) ) {
+            addSignatureParams(doclet);
+            addSignatureReturns(doclet);
+            addAttribs(doclet);
+        }
+    });
+
+    // do this after the urls have all been generated
+    data().each(function(doclet) {
+        doclet.ancestors = getAncestorLinks(doclet);
+
+        if (doclet.kind === 'member') {
+            addSignatureTypes(doclet);
+            addAttribs(doclet);
+        }
+
+        if (doclet.kind === 'constant') {
+            addSignatureTypes(doclet);
+            addAttribs(doclet);
+            doclet.kind = 'member';
+        }
+    });
+
+    var members = helper.getMembers(data);
+    members.tutorials = tutorials.children;
+
+    // output pretty-printed source files by default
+    var outputSourceFiles = conf.default && conf.default.outputSourceFiles !== false ? true :
+        false;
+
+    // add template helpers
+    view.find = find;
+    view.linkto = linkto;
+    view.resolveAuthorLinks = resolveAuthorLinks;
+    view.tutoriallink = tutoriallink;
+    view.htmlsafe = htmlsafe;
+    view.outputSourceFiles = outputSourceFiles;
+
+    // once for all
+    view.nav = buildNav(members);
+    attachModuleSymbols( find({ longname: {left: 'module:'} }), members.modules );
+
+    // generate the pretty-printed source files first so other pages can link to them
+    if (outputSourceFiles) {
+        generateSourceFiles(sourceFiles, opts.encoding);
+    }
+
+    if (members.globals.length) { generate('Global', [{kind: 'globalobj'}], globalUrl); }
+
+    // index page displays information from package.json and lists files
+    var files = find({kind: 'file'}),
+        packages = find({kind: 'package'});
+
+  const title = process.env.APP || 'Dome' ;
+    generate(title + ' Documentation',
+        packages.concat(
+            [{kind: 'mainpage', readme: opts.readme, longname: (opts.mainpagetitle) ? opts.mainpagetitle : 'Main Page'}]
+        ).concat(files),
+    indexUrl);
+
+    // set up the lists that we'll use to generate pages
+    var classes = taffy(members.classes);
+    var modules = taffy(members.modules);
+    var namespaces = taffy(members.namespaces);
+    var mixins = taffy(members.mixins);
+    var externals = taffy(members.externals);
+    var interfaces = taffy(members.interfaces);
+
+    Object.keys(helper.longnameToUrl).forEach(function(longname) {
+        var myModules = helper.find(modules, {longname: longname});
+        if (myModules.length) {
+            generate('Module: ' + myModules[0].name, myModules, helper.longnameToUrl[longname]);
+        }
+
+        var myClasses = helper.find(classes, {longname: longname});
+        if (myClasses.length) {
+            generate('Class: ' + myClasses[0].name, myClasses, helper.longnameToUrl[longname]);
+        }
+
+        var myNamespaces = helper.find(namespaces, {longname: longname});
+        if (myNamespaces.length) {
+            generate('Namespace: ' + myNamespaces[0].name, myNamespaces, helper.longnameToUrl[longname]);
+        }
+
+        var myMixins = helper.find(mixins, {longname: longname});
+        if (myMixins.length) {
+            generate('Mixin: ' + myMixins[0].name, myMixins, helper.longnameToUrl[longname]);
+        }
+
+        var myExternals = helper.find(externals, {longname: longname});
+        if (myExternals.length) {
+            generate('External: ' + myExternals[0].name, myExternals, helper.longnameToUrl[longname]);
+        }
+
+        var myInterfaces = helper.find(interfaces, {longname: longname});
+        if (myInterfaces.length) {
+            generate('Interface: ' + myInterfaces[0].name, myInterfaces, helper.longnameToUrl[longname]);
+        }
+    });
+
+    // TODO: move the tutorial functions to templateHelper.js
+    function generateTutorial(title, tutorial, filename) {
+        var tutorialData = {
+            title: title,
+            header: tutorial.title,
+            content: tutorial.parse(),
+            children: tutorial.children
+        };
+
+        var tutorialPath = path.join(outdir, filename),
+            html = view.render('tutorial.tmpl', tutorialData);
+
+        // yes, you can use {@link} in tutorials too!
+        html = helper.resolveLinks(html); // turn {@link foo} into <a href="foodoc.html">foo</a>
+
+        fs.writeFileSync(tutorialPath, html, 'utf8');
+    }
+
+    // tutorials can have only one parent so there is no risk for loops
+    function saveChildren(node) {
+        node.children.forEach(function(child) {
+            generateTutorial('Tutorial: ' + child.title, child, helper.tutorialToUrl(child.name));
+            saveChildren(child);
+        });
+    }
+    saveChildren(tutorials);
+};
diff --git a/ivette/src/dome/doc/template/static/fonts/OpenSans-Bold-webfont.eot b/ivette/src/dome/doc/template/static/fonts/OpenSans-Bold-webfont.eot
new file mode 100644
index 0000000000000000000000000000000000000000..5d20d916338a5890a033952e2e07ba7380f5a7d3
Binary files /dev/null and b/ivette/src/dome/doc/template/static/fonts/OpenSans-Bold-webfont.eot differ
diff --git a/ivette/src/dome/doc/template/static/fonts/OpenSans-Bold-webfont.svg b/ivette/src/dome/doc/template/static/fonts/OpenSans-Bold-webfont.svg
new file mode 100644
index 0000000000000000000000000000000000000000..ff4d7ddb6aeedf32398e5f02be7aa31759373ccb
Binary files /dev/null and b/ivette/src/dome/doc/template/static/fonts/OpenSans-Bold-webfont.svg differ
diff --git a/ivette/src/dome/doc/template/static/fonts/OpenSans-Bold-webfont.woff b/ivette/src/dome/doc/template/static/fonts/OpenSans-Bold-webfont.woff
new file mode 100644
index 0000000000000000000000000000000000000000..1205787b0ed50db71ebd4f8a7f85d106721ff258
Binary files /dev/null and b/ivette/src/dome/doc/template/static/fonts/OpenSans-Bold-webfont.woff differ
diff --git a/ivette/src/dome/doc/template/static/fonts/OpenSans-BoldItalic-webfont.eot b/ivette/src/dome/doc/template/static/fonts/OpenSans-BoldItalic-webfont.eot
new file mode 100644
index 0000000000000000000000000000000000000000..1f639a15ff3cc66d3b22b55a7f28a8cf0d821b03
Binary files /dev/null and b/ivette/src/dome/doc/template/static/fonts/OpenSans-BoldItalic-webfont.eot differ
diff --git a/ivette/src/dome/doc/template/static/fonts/OpenSans-BoldItalic-webfont.svg b/ivette/src/dome/doc/template/static/fonts/OpenSans-BoldItalic-webfont.svg
new file mode 100644
index 0000000000000000000000000000000000000000..5ef8ef90048c8f1600aa087b1b7b7617341986ea
Binary files /dev/null and b/ivette/src/dome/doc/template/static/fonts/OpenSans-BoldItalic-webfont.svg differ
diff --git a/ivette/src/dome/doc/template/static/fonts/OpenSans-BoldItalic-webfont.woff b/ivette/src/dome/doc/template/static/fonts/OpenSans-BoldItalic-webfont.woff
new file mode 100644
index 0000000000000000000000000000000000000000..ed760c0628b6a0026041f5b8bba466a0471fd2e0
Binary files /dev/null and b/ivette/src/dome/doc/template/static/fonts/OpenSans-BoldItalic-webfont.woff differ
diff --git a/ivette/src/dome/doc/template/static/fonts/OpenSans-Italic-webfont.eot b/ivette/src/dome/doc/template/static/fonts/OpenSans-Italic-webfont.eot
new file mode 100644
index 0000000000000000000000000000000000000000..0c8a0ae06ed09f6a7be1b84defae7ccda65cc2e0
Binary files /dev/null and b/ivette/src/dome/doc/template/static/fonts/OpenSans-Italic-webfont.eot differ
diff --git a/ivette/src/dome/doc/template/static/fonts/OpenSans-Italic-webfont.svg b/ivette/src/dome/doc/template/static/fonts/OpenSans-Italic-webfont.svg
new file mode 100644
index 0000000000000000000000000000000000000000..d86c165fb1332ba91e2cac838a747822d451f26f
Binary files /dev/null and b/ivette/src/dome/doc/template/static/fonts/OpenSans-Italic-webfont.svg differ
diff --git a/ivette/src/dome/doc/template/static/fonts/OpenSans-Italic-webfont.woff b/ivette/src/dome/doc/template/static/fonts/OpenSans-Italic-webfont.woff
new file mode 100644
index 0000000000000000000000000000000000000000..ff652e64356b538c001423b6aedefcf1ee66cd17
Binary files /dev/null and b/ivette/src/dome/doc/template/static/fonts/OpenSans-Italic-webfont.woff differ
diff --git a/ivette/src/dome/doc/template/static/fonts/OpenSans-Light-webfont.eot b/ivette/src/dome/doc/template/static/fonts/OpenSans-Light-webfont.eot
new file mode 100644
index 0000000000000000000000000000000000000000..14868406aa7d728a88d63963f119635813b5d30e
Binary files /dev/null and b/ivette/src/dome/doc/template/static/fonts/OpenSans-Light-webfont.eot differ
diff --git a/ivette/src/dome/doc/template/static/fonts/OpenSans-Light-webfont.svg b/ivette/src/dome/doc/template/static/fonts/OpenSans-Light-webfont.svg
new file mode 100644
index 0000000000000000000000000000000000000000..bac8c03979f56051d3e2b72cb2008865a07b09ca
Binary files /dev/null and b/ivette/src/dome/doc/template/static/fonts/OpenSans-Light-webfont.svg differ
diff --git a/ivette/src/dome/doc/template/static/fonts/OpenSans-Light-webfont.woff b/ivette/src/dome/doc/template/static/fonts/OpenSans-Light-webfont.woff
new file mode 100644
index 0000000000000000000000000000000000000000..e786074813a27d0a7a249047832988d5bf0fe756
Binary files /dev/null and b/ivette/src/dome/doc/template/static/fonts/OpenSans-Light-webfont.woff differ
diff --git a/ivette/src/dome/doc/template/static/fonts/OpenSans-LightItalic-webfont.eot b/ivette/src/dome/doc/template/static/fonts/OpenSans-LightItalic-webfont.eot
new file mode 100644
index 0000000000000000000000000000000000000000..8f445929ffb03b50e98c2a2f7d831a0cb1b276a2
Binary files /dev/null and b/ivette/src/dome/doc/template/static/fonts/OpenSans-LightItalic-webfont.eot differ
diff --git a/ivette/src/dome/doc/template/static/fonts/OpenSans-LightItalic-webfont.svg b/ivette/src/dome/doc/template/static/fonts/OpenSans-LightItalic-webfont.svg
new file mode 100644
index 0000000000000000000000000000000000000000..047fc73b406572fa39ec17e00eb26d88fd9b7d1c
Binary files /dev/null and b/ivette/src/dome/doc/template/static/fonts/OpenSans-LightItalic-webfont.svg differ
diff --git a/ivette/src/dome/doc/template/static/fonts/OpenSans-LightItalic-webfont.woff b/ivette/src/dome/doc/template/static/fonts/OpenSans-LightItalic-webfont.woff
new file mode 100644
index 0000000000000000000000000000000000000000..43e8b9e6cc061ff17fd2903075cbde12715512b3
Binary files /dev/null and b/ivette/src/dome/doc/template/static/fonts/OpenSans-LightItalic-webfont.woff differ
diff --git a/ivette/src/dome/doc/template/static/fonts/OpenSans-Regular-webfont.eot b/ivette/src/dome/doc/template/static/fonts/OpenSans-Regular-webfont.eot
new file mode 100644
index 0000000000000000000000000000000000000000..6bbc3cf58cb011a6b4bf3cb1612ce212608f7274
Binary files /dev/null and b/ivette/src/dome/doc/template/static/fonts/OpenSans-Regular-webfont.eot differ
diff --git a/ivette/src/dome/doc/template/static/fonts/OpenSans-Regular-webfont.svg b/ivette/src/dome/doc/template/static/fonts/OpenSans-Regular-webfont.svg
new file mode 100644
index 0000000000000000000000000000000000000000..b2ce45def77b17dfcecbbfeb545146f88d927792
Binary files /dev/null and b/ivette/src/dome/doc/template/static/fonts/OpenSans-Regular-webfont.svg differ
diff --git a/ivette/src/dome/doc/template/static/fonts/OpenSans-Regular-webfont.woff b/ivette/src/dome/doc/template/static/fonts/OpenSans-Regular-webfont.woff
new file mode 100644
index 0000000000000000000000000000000000000000..e231183dce4c7b452afc9e7799586fd285e146f4
Binary files /dev/null and b/ivette/src/dome/doc/template/static/fonts/OpenSans-Regular-webfont.woff differ
diff --git a/ivette/src/dome/doc/template/static/scripts/linenumber.js b/ivette/src/dome/doc/template/static/scripts/linenumber.js
new file mode 100644
index 0000000000000000000000000000000000000000..8d52f7eafdb166eeba1de2f1a223fcde9ffbe4fe
--- /dev/null
+++ b/ivette/src/dome/doc/template/static/scripts/linenumber.js
@@ -0,0 +1,25 @@
+/*global document */
+(function() {
+    var source = document.getElementsByClassName('prettyprint source linenums');
+    var i = 0;
+    var lineNumber = 0;
+    var lineId;
+    var lines;
+    var totalLines;
+    var anchorHash;
+
+    if (source && source[0]) {
+        anchorHash = document.location.hash.substring(1);
+        lines = source[0].getElementsByTagName('li');
+        totalLines = lines.length;
+
+        for (; i < totalLines; i++) {
+            lineNumber++;
+            lineId = 'line' + lineNumber;
+            lines[i].id = lineId;
+            if (lineId === anchorHash) {
+                lines[i].className += ' selected';
+            }
+        }
+    }
+})();
diff --git a/ivette/src/dome/doc/template/static/scripts/prettify/Apache-License-2.0.txt b/ivette/src/dome/doc/template/static/scripts/prettify/Apache-License-2.0.txt
new file mode 100644
index 0000000000000000000000000000000000000000..d645695673349e3947e8e5ae42332d0ac3164cd7
--- /dev/null
+++ b/ivette/src/dome/doc/template/static/scripts/prettify/Apache-License-2.0.txt
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/ivette/src/dome/doc/template/static/scripts/prettify/lang-css.js b/ivette/src/dome/doc/template/static/scripts/prettify/lang-css.js
new file mode 100644
index 0000000000000000000000000000000000000000..041e1f59067977554f29146ed51c13bcf509f0af
--- /dev/null
+++ b/ivette/src/dome/doc/template/static/scripts/prettify/lang-css.js
@@ -0,0 +1,2 @@
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\f\r ]+/,null," \t\r\n"]],[["str",/^"(?:[^\n\f\r"\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*"/,null],["str",/^'(?:[^\n\f\r'\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*'/,null],["lang-css-str",/^url\(([^"')]*)\)/i],["kwd",/^(?:url|rgb|!important|@import|@page|@media|@charset|inherit)(?=[^\w-]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*)\s*:/i],["com",/^\/\*[^*]*\*+(?:[^*/][^*]*\*+)*\//],["com",
+/^(?:<\!--|--\>)/],["lit",/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],["lit",/^#[\da-f]{3,6}/i],["pln",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i],["pun",/^[^\s\w"']+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[["kwd",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[["str",/^[^"')]+/]]),["css-str"]);
diff --git a/ivette/src/dome/doc/template/static/scripts/prettify/prettify.js b/ivette/src/dome/doc/template/static/scripts/prettify/prettify.js
new file mode 100644
index 0000000000000000000000000000000000000000..eef5ad7e6a07676b3919146d583d1c190bf1e163
--- /dev/null
+++ b/ivette/src/dome/doc/template/static/scripts/prettify/prettify.js
@@ -0,0 +1,28 @@
+var q=null;window.PR_SHOULD_USE_CONTINUATION=!0;
+(function(){function L(a){function m(a){var f=a.charCodeAt(0);if(f!==92)return f;var b=a.charAt(1);return(f=r[b])?f:"0"<=b&&b<="7"?parseInt(a.substring(1),8):b==="u"||b==="x"?parseInt(a.substring(2),16):a.charCodeAt(1)}function e(a){if(a<32)return(a<16?"\\x0":"\\x")+a.toString(16);a=String.fromCharCode(a);if(a==="\\"||a==="-"||a==="["||a==="]")a="\\"+a;return a}function h(a){for(var f=a.substring(1,a.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),a=
+[],b=[],o=f[0]==="^",c=o?1:0,i=f.length;c<i;++c){var j=f[c];if(/\\[bdsw]/i.test(j))a.push(j);else{var j=m(j),d;c+2<i&&"-"===f[c+1]?(d=m(f[c+2]),c+=2):d=j;b.push([j,d]);d<65||j>122||(d<65||j>90||b.push([Math.max(65,j)|32,Math.min(d,90)|32]),d<97||j>122||b.push([Math.max(97,j)&-33,Math.min(d,122)&-33]))}}b.sort(function(a,f){return a[0]-f[0]||f[1]-a[1]});f=[];j=[NaN,NaN];for(c=0;c<b.length;++c)i=b[c],i[0]<=j[1]+1?j[1]=Math.max(j[1],i[1]):f.push(j=i);b=["["];o&&b.push("^");b.push.apply(b,a);for(c=0;c<
+f.length;++c)i=f[c],b.push(e(i[0])),i[1]>i[0]&&(i[1]+1>i[0]&&b.push("-"),b.push(e(i[1])));b.push("]");return b.join("")}function y(a){for(var f=a.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),b=f.length,d=[],c=0,i=0;c<b;++c){var j=f[c];j==="("?++i:"\\"===j.charAt(0)&&(j=+j.substring(1))&&j<=i&&(d[j]=-1)}for(c=1;c<d.length;++c)-1===d[c]&&(d[c]=++t);for(i=c=0;c<b;++c)j=f[c],j==="("?(++i,d[i]===void 0&&(f[c]="(?:")):"\\"===j.charAt(0)&&
+(j=+j.substring(1))&&j<=i&&(f[c]="\\"+d[i]);for(i=c=0;c<b;++c)"^"===f[c]&&"^"!==f[c+1]&&(f[c]="");if(a.ignoreCase&&s)for(c=0;c<b;++c)j=f[c],a=j.charAt(0),j.length>=2&&a==="["?f[c]=h(j):a!=="\\"&&(f[c]=j.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return f.join("")}for(var t=0,s=!1,l=!1,p=0,d=a.length;p<d;++p){var g=a[p];if(g.ignoreCase)l=!0;else if(/[a-z]/i.test(g.source.replace(/\\u[\da-f]{4}|\\x[\da-f]{2}|\\[^UXux]/gi,""))){s=!0;l=!1;break}}for(var r=
+{b:8,t:9,n:10,v:11,f:12,r:13},n=[],p=0,d=a.length;p<d;++p){g=a[p];if(g.global||g.multiline)throw Error(""+g);n.push("(?:"+y(g)+")")}return RegExp(n.join("|"),l?"gi":"g")}function M(a){function m(a){switch(a.nodeType){case 1:if(e.test(a.className))break;for(var g=a.firstChild;g;g=g.nextSibling)m(g);g=a.nodeName;if("BR"===g||"LI"===g)h[s]="\n",t[s<<1]=y++,t[s++<<1|1]=a;break;case 3:case 4:g=a.nodeValue,g.length&&(g=p?g.replace(/\r\n?/g,"\n"):g.replace(/[\t\n\r ]+/g," "),h[s]=g,t[s<<1]=y,y+=g.length,
+t[s++<<1|1]=a)}}var e=/(?:^|\s)nocode(?:\s|$)/,h=[],y=0,t=[],s=0,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=document.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);m(a);return{a:h.join("").replace(/\n$/,""),c:t}}function B(a,m,e,h){m&&(a={a:m,d:a},e(a),h.push.apply(h,a.e))}function x(a,m){function e(a){for(var l=a.d,p=[l,"pln"],d=0,g=a.a.match(y)||[],r={},n=0,z=g.length;n<z;++n){var f=g[n],b=r[f],o=void 0,c;if(typeof b===
+"string")c=!1;else{var i=h[f.charAt(0)];if(i)o=f.match(i[1]),b=i[0];else{for(c=0;c<t;++c)if(i=m[c],o=f.match(i[1])){b=i[0];break}o||(b="pln")}if((c=b.length>=5&&"lang-"===b.substring(0,5))&&!(o&&typeof o[1]==="string"))c=!1,b="src";c||(r[f]=b)}i=d;d+=f.length;if(c){c=o[1];var j=f.indexOf(c),k=j+c.length;o[2]&&(k=f.length-o[2].length,j=k-c.length);b=b.substring(5);B(l+i,f.substring(0,j),e,p);B(l+i+j,c,C(b,c),p);B(l+i+k,f.substring(k),e,p)}else p.push(l+i,b)}a.e=p}var h={},y;(function(){for(var e=a.concat(m),
+l=[],p={},d=0,g=e.length;d<g;++d){var r=e[d],n=r[3];if(n)for(var k=n.length;--k>=0;)h[n.charAt(k)]=r;r=r[1];n=""+r;p.hasOwnProperty(n)||(l.push(r),p[n]=q)}l.push(/[\S\s]/);y=L(l)})();var t=m.length;return e}function u(a){var m=[],e=[];a.tripleQuotedStrings?m.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?m.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/,
+q,"'\"`"]):m.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&e.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var h=a.hashComments;h&&(a.cStyleComments?(h>1?m.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):m.push(["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),e.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,q])):m.push(["com",/^#[^\n\r]*/,
+q,"#"]));a.cStyleComments&&(e.push(["com",/^\/\/[^\n\r]*/,q]),e.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));a.regexLiterals&&e.push(["lang-regex",/^(?:^^\.?|[!+-]|!=|!==|#|%|%=|&|&&|&&=|&=|\(|\*|\*=|\+=|,|-=|->|\/|\/=|:|::|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|[?@[^]|\^=|\^\^|\^\^=|{|\||\|=|\|\||\|\|=|~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/]);(h=a.types)&&e.push(["typ",h]);a=(""+a.keywords).replace(/^ | $/g,
+"");a.length&&e.push(["kwd",RegExp("^(?:"+a.replace(/[\s,]+/g,"|")+")\\b"),q]);m.push(["pln",/^\s+/,q," \r\n\t\xa0"]);e.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,q],["pun",/^.[^\s\w"-$'./@\\`]*/,q]);return x(m,e)}function D(a,m){function e(a){switch(a.nodeType){case 1:if(k.test(a.className))break;if("BR"===a.nodeName)h(a),
+a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)e(a);break;case 3:case 4:if(p){var b=a.nodeValue,d=b.match(t);if(d){var c=b.substring(0,d.index);a.nodeValue=c;(b=b.substring(d.index+d[0].length))&&a.parentNode.insertBefore(s.createTextNode(b),a.nextSibling);h(a);c||a.parentNode.removeChild(a)}}}}function h(a){function b(a,d){var e=d?a.cloneNode(!1):a,f=a.parentNode;if(f){var f=b(f,1),g=a.nextSibling;f.appendChild(e);for(var h=g;h;h=g)g=h.nextSibling,f.appendChild(h)}return e}
+for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),e;(e=a.parentNode)&&e.nodeType===1;)a=e;d.push(a)}var k=/(?:^|\s)nocode(?:\s|$)/,t=/\r\n?|\n/,s=a.ownerDocument,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=s.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);for(l=s.createElement("LI");a.firstChild;)l.appendChild(a.firstChild);for(var d=[l],g=0;g<d.length;++g)e(d[g]);m===(m|0)&&d[0].setAttribute("value",
+m);var r=s.createElement("OL");r.className="linenums";for(var n=Math.max(0,m-1|0)||0,g=0,z=d.length;g<z;++g)l=d[g],l.className="L"+(g+n)%10,l.firstChild||l.appendChild(s.createTextNode("\xa0")),r.appendChild(l);a.appendChild(r)}function k(a,m){for(var e=m.length;--e>=0;){var h=m[e];A.hasOwnProperty(h)?window.console&&console.warn("cannot override language handler %s",h):A[h]=a}}function C(a,m){if(!a||!A.hasOwnProperty(a))a=/^\s*</.test(m)?"default-markup":"default-code";return A[a]}function E(a){var m=
+a.g;try{var e=M(a.h),h=e.a;a.a=h;a.c=e.c;a.d=0;C(m,h)(a);var k=/\bMSIE\b/.test(navigator.userAgent),m=/\n/g,t=a.a,s=t.length,e=0,l=a.c,p=l.length,h=0,d=a.e,g=d.length,a=0;d[g]=s;var r,n;for(n=r=0;n<g;)d[n]!==d[n+2]?(d[r++]=d[n++],d[r++]=d[n++]):n+=2;g=r;for(n=r=0;n<g;){for(var z=d[n],f=d[n+1],b=n+2;b+2<=g&&d[b+1]===f;)b+=2;d[r++]=z;d[r++]=f;n=b}for(d.length=r;h<p;){var o=l[h+2]||s,c=d[a+2]||s,b=Math.min(o,c),i=l[h+1],j;if(i.nodeType!==1&&(j=t.substring(e,b))){k&&(j=j.replace(m,"\r"));i.nodeValue=
+j;var u=i.ownerDocument,v=u.createElement("SPAN");v.className=d[a+1];var x=i.parentNode;x.replaceChild(v,i);v.appendChild(i);e<o&&(l[h+1]=i=u.createTextNode(t.substring(b,o)),x.insertBefore(i,v.nextSibling))}e=b;e>=o&&(h+=2);e>=c&&(a+=2)}}catch(w){"console"in window&&console.log(w&&w.stack?w.stack:w)}}var v=["break,continue,do,else,for,if,return,while"],w=[[v,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"],
+"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],F=[w,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],G=[w,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"],
+H=[G,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"],w=[w,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],I=[v,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"],
+J=[v,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],v=[v,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],K=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/,N=/\S/,O=u({keywords:[F,H,w,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END"+
+I,J,v],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),A={};k(O,["default-code"]);k(x([],[["pln",/^[^<?]+/],["dec",/^<!\w[^>]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^<xmp\b[^>]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^<script\b[^>]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^<style\b[^>]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),
+["default-markup","htm","html","mxml","xhtml","xml","xsl"]);k(x([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css",
+/^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);k(x([],[["atv",/^[\S\s]+/]]),["uq.val"]);k(u({keywords:F,hashComments:!0,cStyleComments:!0,types:K}),["c","cc","cpp","cxx","cyc","m"]);k(u({keywords:"null,true,false"}),["json"]);k(u({keywords:H,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:K}),["cs"]);k(u({keywords:G,cStyleComments:!0}),["java"]);k(u({keywords:v,hashComments:!0,multiLineStrings:!0}),["bsh","csh","sh"]);k(u({keywords:I,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}),
+["cv","py"]);k(u({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["perl","pl","pm"]);k(u({keywords:J,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb"]);k(u({keywords:w,cStyleComments:!0,regexLiterals:!0}),["js"]);k(u({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes",
+hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);k(x([],[["str",/^[\S\s]+/]]),["regex"]);window.prettyPrintOne=function(a,m,e){var h=document.createElement("PRE");h.innerHTML=a;e&&D(h,e);E({g:m,i:e,h:h});return h.innerHTML};window.prettyPrint=function(a){function m(){for(var e=window.PR_SHOULD_USE_CONTINUATION?l.now()+250:Infinity;p<h.length&&l.now()<e;p++){var n=h[p],k=n.className;if(k.indexOf("prettyprint")>=0){var k=k.match(g),f,b;if(b=
+!k){b=n;for(var o=void 0,c=b.firstChild;c;c=c.nextSibling)var i=c.nodeType,o=i===1?o?b:c:i===3?N.test(c.nodeValue)?b:o:o;b=(f=o===b?void 0:o)&&"CODE"===f.tagName}b&&(k=f.className.match(g));k&&(k=k[1]);b=!1;for(o=n.parentNode;o;o=o.parentNode)if((o.tagName==="pre"||o.tagName==="code"||o.tagName==="xmp")&&o.className&&o.className.indexOf("prettyprint")>=0){b=!0;break}b||((b=(b=n.className.match(/\blinenums\b(?::(\d+))?/))?b[1]&&b[1].length?+b[1]:!0:!1)&&D(n,b),d={g:k,h:n,i:b},E(d))}}p<h.length?setTimeout(m,
+250):a&&a()}for(var e=[document.getElementsByTagName("pre"),document.getElementsByTagName("code"),document.getElementsByTagName("xmp")],h=[],k=0;k<e.length;++k)for(var t=0,s=e[k].length;t<s;++t)h.push(e[k][t]);var e=q,l=Date;l.now||(l={now:function(){return+new Date}});var p=0,d,g=/\blang(?:uage)?-([\w.]+)(?!\S)/;m()};window.PR={createSimpleLexer:x,registerLangHandler:k,sourceDecorator:u,PR_ATTRIB_NAME:"atn",PR_ATTRIB_VALUE:"atv",PR_COMMENT:"com",PR_DECLARATION:"dec",PR_KEYWORD:"kwd",PR_LITERAL:"lit",
+PR_NOCODE:"nocode",PR_PLAIN:"pln",PR_PUNCTUATION:"pun",PR_SOURCE:"src",PR_STRING:"str",PR_TAG:"tag",PR_TYPE:"typ"}})();
diff --git a/ivette/src/dome/doc/template/static/styles/jsdoc-default.css b/ivette/src/dome/doc/template/static/styles/jsdoc-default.css
new file mode 100644
index 0000000000000000000000000000000000000000..05743bc519b3916b49b940e3017b8a3e2f7bf498
--- /dev/null
+++ b/ivette/src/dome/doc/template/static/styles/jsdoc-default.css
@@ -0,0 +1,496 @@
+/* -------------------------------------------------------------------------- */
+/* --- Main Document                                                      --- */
+/* -------------------------------------------------------------------------- */
+
+@font-face {
+    font-family: 'Open Sans';
+    font-weight: normal;
+    font-style: normal;
+    src: url('../fonts/OpenSans-Regular-webfont.eot');
+    src:
+        local('Open Sans'),
+        local('OpenSans'),
+        url('../fonts/OpenSans-Regular-webfont.eot?#iefix') format('embedded-opentype'),
+        url('../fonts/OpenSans-Regular-webfont.woff') format('woff'),
+        url('../fonts/OpenSans-Regular-webfont.svg#open_sansregular') format('svg');
+}
+
+@font-face {
+    font-family: 'Open Sans Light';
+    font-weight: normal;
+    font-style: normal;
+    src: url('../fonts/OpenSans-Light-webfont.eot');
+    src:
+        local('Open Sans Light'),
+        local('OpenSans Light'),
+        url('../fonts/OpenSans-Light-webfont.eot?#iefix') format('embedded-opentype'),
+        url('../fonts/OpenSans-Light-webfont.woff') format('woff'),
+        url('../fonts/OpenSans-Light-webfont.svg#open_sanslight') format('svg');
+}
+
+html
+{
+    background-color: #fff;
+    font-size: 14px;
+}
+
+body
+{
+    display: block ;
+    position: fixed ;
+    top: 0px ;
+    bottom: 0px ;
+    left: 0px ;
+    right: 0px ;
+    overflow: hidden ;
+    margin: 0px ;
+    padding: 0px ;
+    font-family: 'Open Sans', sans-serif;
+    line-height: 1.5;
+    color: #4d4e53;
+    background-color: white;
+}
+
+#main {
+    display: flex ;
+    flex-flow: row nowrap ;
+    width: 100% ;
+    height: 100% ;
+    overflow: hidden ;
+}
+
+#index {
+    box-sizing: border-box ;
+    display: block ;
+    order: 2 ;
+    flex: 0 1 auto ;
+    width: auto ;
+    min-width: 300px ;
+    height: 100% ;
+    overflow: auto ;
+}
+
+#content {
+    box-sizing: border-box ;
+    order: 1 ;
+    flex: 1 1 auto ;
+    width: 100% ;
+    height: 100% ;
+    display: flex ;
+    overflow: auto ;
+    justify-content: center ;
+    align-content: center ;
+}
+
+#page {
+    margin: 10px ;
+    flex: 1 1 auto ;
+    min-width: 600px ;
+    max-width: 1000px ;
+}
+
+/* -------------------------------------------------------------------------- */
+/* --- Content Styling                                                    --- */
+/* -------------------------------------------------------------------------- */
+
+h4:target { border-left: thick solid orange ; padding-top: 4px ; padding-left: 2px }
+
+a, a:visited, a:active, a code {
+    color: #b63910 ;
+    text-decoration: none;
+}
+
+a:hover {
+    text-decoration: underline;
+}
+
+header
+{
+    display: block;
+    padding: 0px 4px;
+}
+
+tt, code, kbd, samp {
+    color: #8a8a61;
+    font-family: Consolas, Monaco, 'Andale Mono', monospace;
+}
+
+.class-description {
+    font-size: 130%;
+    line-height: 140%;
+    margin-bottom: 1em;
+    margin-top: 1em;
+}
+
+.class-description:empty {
+    margin: 0;
+}
+
+article dl {
+    margin-bottom: 40px;
+}
+
+div.summary {
+    margin-top: 12px ;
+    margin-bottom: 12px ;
+    padding-left: 20px ;
+    padding-bottom: 10px ;
+}
+
+table.summary { }
+table.summary tr { }
+table.summary td { padding-left: 20px }
+table.summary p { margin: 0 }
+
+section
+{
+    display: block;
+    background-color: #fff;
+    padding: 12px 24px;
+    margin-right: 30px;
+}
+
+.variation {
+    display: none;
+}
+
+.signature-attributes {
+    font-size: 60%;
+    color: #aaa;
+    font-style: italic;
+    font-weight: lighter;
+}
+
+nav
+{
+    display: block;
+    margin: 20px ;
+    width: content ;
+}
+
+nav ul {
+    font-family: 'Lucida Grande', 'Lucida Sans Unicode', arial, sans-serif;
+    font-size: 100%;
+    padding: 0;
+    margin: 0;
+    list-style-type: none;
+}
+
+nav li {
+    white-space: nowrap ;
+}
+
+nav ul a, nav ul a:visited, nav ul a:active {
+    font-family: Consolas, Monaco, 'Andale Mono', monospace;
+    color: #4D8053;
+}
+
+nav ul a:hover {
+    text-decoration: none ;
+    background-color: lightblue ;
+}
+
+nav ul a {
+    padding-top: 4px ;
+    padding-bottom: 2px ;
+    padding-left: 2px ;
+    padding-right: 2px ;
+    margin-left: 4px ;
+}
+
+nav h3 {
+    font-size: 18px;
+    margin-top: 12px;
+}
+
+footer {
+    font-size: 8pt;
+    color: gray;
+    display: block;
+    padding: 6px;
+    margin-top: 12px;
+    margin-left: 20px;
+    border-top: thin solid #c0c0c0 ;
+}
+
+h1, h2, h3, h4 {
+    font-weight: 200;
+    margin: 0;
+}
+
+h1
+{
+    font-family: 'Verdana', sans-serif;
+    font-size: 32px;
+    letter-spacing: -2px;
+    margin: 20px 0px 12px ;
+}
+
+h1.page-title {
+    background: #f50000;
+    color: white;
+    padding-left: 20px ;
+    margin: 0px ;
+}
+
+h2, h3
+{
+    font-size: 24px;
+    font-weight: 700;
+    letter-spacing: -1px;
+    margin-bottom: 12px;
+}
+
+h4
+{
+    font-size: 18px;
+    letter-spacing: -0.33px;
+    margin-bottom: 8px;
+    padding-bottom: 4px;
+    border-bottom: thin solid #c0c0c0;
+    color: #4d4e53;
+}
+
+h5, .container-overview .subsection-title
+{
+    font-size: 120%;
+    font-weight: bold;
+    letter-spacing: -0.01em;
+    margin: 8px 0 3px 0;
+}
+
+h6
+{
+    font-size: 100%;
+    letter-spacing: -0.01em;
+    margin: 6px 0 3px 0;
+    font-style: italic;
+}
+
+.ancestors { color: #999; }
+.ancestors a
+{
+    color: #999 !important;
+    text-decoration: none;
+}
+
+.clear
+{
+    clear: both;
+}
+
+.important
+{
+    font-weight: bold;
+    color: #950B02;
+}
+
+.yes-def {
+    text-indent: -1000px;
+}
+
+.type-signature {
+    color: #aaa;
+}
+
+.name, .signature {
+    font-family: Consolas, Monaco, 'Andale Mono', monospace;
+}
+
+h4.name { position: relative; }
+h4.name .anchor { position: absolute; right: 2px; }
+
+.details { margin-top: 14px; }
+.details dt { width: 120px; float: left; padding-left: 10px;  padding-top: 6px; }
+.details dd { margin-left: 70px; }
+.details ul { margin: 0; }
+.details ul { list-style-type: none; }
+.details li { margin-left: 30px; padding-top: 6px; }
+.details pre.prettyprint { margin: 0 }
+.details .object-value { padding-top: 0; }
+
+.description {
+    margin-bottom: 1em;
+    margin-top: 1em;
+}
+
+.code-caption
+{
+    font-style: italic;
+    font-size: 107%;
+    margin: 0;
+}
+
+.prettyprint
+{
+    border: 1px solid #ddd;
+    width: 80%;
+    overflow: auto;
+}
+
+.prettyprint.source {
+    width: inherit;
+}
+
+.prettyprint code
+{
+    font-size: 100%;
+    line-height: 18px;
+    display: block;
+    padding: 4px 12px;
+    margin: 0;
+    background-color: #fff;
+    color: #4D8053;
+}
+
+.prettyprint code span.line
+{
+  display: inline-block;
+}
+
+.prettyprint.linenums
+{
+  padding-left: 70px;
+  -webkit-user-select: none;
+  -moz-user-select: none;
+  -ms-user-select: none;
+  user-select: none;
+}
+
+.prettyprint.linenums ol
+{
+  padding-left: 0;
+}
+
+.prettyprint.linenums li
+{
+  border-left: 3px #ddd solid;
+}
+
+.prettyprint.linenums li.selected,
+.prettyprint.linenums li.selected *
+{
+  background-color: lightyellow;
+}
+
+.prettyprint.linenums li *
+{
+  -webkit-user-select: text;
+  -moz-user-select: text;
+  -ms-user-select: text;
+  user-select: text;
+}
+
+table {
+    border-collapse: collapse;
+    margin-left: 1em;
+}
+
+thead {
+    background-color: #ddd;
+    font-weight: bold;
+}
+
+td, th
+{
+    border: 1px solid #ddd;
+    margin: 0px;
+    text-align: left;
+    vertical-align: baseline;
+    padding: 4px 6px;
+    display: table-cell;
+}
+
+.params, .props, .description table, .description td, .description th, .summary td, .summary th
+{
+    border-spacing: 0;
+    border: 0;
+}
+
+.params .name, .props .name, .name code, .params .type, .props .type {
+    color: #4D8053;
+    font-family: Consolas, Monaco, 'Andale Mono', monospace;
+    font-size: 9pt;
+    white-space: nowrap;
+}
+
+.params td, .params th, .props td, .props th, .description td, .description th
+{
+    border: 1px solid #ddd;
+    margin: 0px;
+    text-align: left;
+    vertical-align: baseline;
+    padding: 4px 6px;
+    display: table-cell;
+}
+
+.params thead tr, .props thead tr, .description thead tr
+{
+    background-color: #ddd;
+    font-weight: bold;
+}
+
+.params .params thead tr, .props .props thead tr
+{
+    background-color: #fff;
+    font-weight: bold;
+}
+
+.params th, .props th { border-right: 1px solid #aaa; }
+.description th { border: 1px solid #aaa; }
+.params thead .last, .props thead .last { border-right: 1px solid #ddd; }
+
+.params td.description > p:first-child,
+.props td.description > p:first-child
+{
+    margin-top: 0;
+    padding-top: 0;
+}
+
+.params td.description > p:last-child,
+.props td.description > p:last-child
+{
+    margin-bottom: 0;
+    padding-bottom: 0;
+}
+
+.list-index
+{
+    margin-top: 2em ;
+    border-top: 1px solid #ddd ;
+    border-bottom: 1px solid #ddd ;
+    margin-bottom: 2em ;
+}
+
+.list-item:hover
+{
+    background: #ddd ;
+}
+
+.list-title,
+.list-descr
+{
+    border: none;
+    margin: 0;
+    padding: 1px 20px 1px 0px ;
+}
+
+.list-separator
+{
+    border-top: 1px solid #ddd ;
+}
+
+.list-descr p
+{
+    padding: 0;
+    margin: 0;
+}
+
+.dimmed
+{
+    color: #bbb ;
+}
+
+.disabled {
+    color: #454545;
+}
diff --git a/ivette/src/dome/doc/template/static/styles/prettify-jsdoc.css b/ivette/src/dome/doc/template/static/styles/prettify-jsdoc.css
new file mode 100644
index 0000000000000000000000000000000000000000..5a2526e374846d0c80af0c06c25d25215463a0d9
--- /dev/null
+++ b/ivette/src/dome/doc/template/static/styles/prettify-jsdoc.css
@@ -0,0 +1,111 @@
+/* JSDoc prettify.js theme */
+
+/* plain text */
+.pln {
+  color: #000000;
+  font-weight: normal;
+  font-style: normal;
+}
+
+/* string content */
+.str {
+  color: #006400;
+  font-weight: normal;
+  font-style: normal;
+}
+
+/* a keyword */
+.kwd {
+  color: #000000;
+  font-weight: bold;
+  font-style: normal;
+}
+
+/* a comment */
+.com {
+  font-weight: normal;
+  font-style: italic;
+}
+
+/* a type name */
+.typ {
+  color: #000000;
+  font-weight: normal;
+  font-style: normal;
+}
+
+/* a literal value */
+.lit {
+  color: #006400;
+  font-weight: normal;
+  font-style: normal;
+}
+
+/* punctuation */
+.pun {
+  color: #000000;
+  font-weight: bold;
+  font-style: normal;
+}
+
+/* lisp open bracket */
+.opn {
+  color: #000000;
+  font-weight: bold;
+  font-style: normal;
+}
+
+/* lisp close bracket */
+.clo {
+  color: #000000;
+  font-weight: bold;
+  font-style: normal;
+}
+
+/* a markup tag name */
+.tag {
+  color: #006400;
+  font-weight: normal;
+  font-style: normal;
+}
+
+/* a markup attribute name */
+.atn {
+  color: #006400;
+  font-weight: normal;
+  font-style: normal;
+}
+
+/* a markup attribute value */
+.atv {
+  color: #006400;
+  font-weight: normal;
+  font-style: normal;
+}
+
+/* a declaration */
+.dec {
+  color: #000000;
+  font-weight: bold;
+  font-style: normal;
+}
+
+/* a variable name */
+.var {
+  color: #000000;
+  font-weight: normal;
+  font-style: normal;
+}
+
+/* a function name */
+.fun {
+  color: #000000;
+  font-weight: bold;
+  font-style: normal;
+}
+
+/* Specify class=linenums on a pre to get line numbering */
+ol.linenums {
+  margin-top: 0;
+  margin-bottom: 0;
+}
diff --git a/ivette/src/dome/doc/template/static/styles/prettify-tomorrow.css b/ivette/src/dome/doc/template/static/styles/prettify-tomorrow.css
new file mode 100644
index 0000000000000000000000000000000000000000..b6f92a78db990ec1dc52e032452a979b8c216a85
--- /dev/null
+++ b/ivette/src/dome/doc/template/static/styles/prettify-tomorrow.css
@@ -0,0 +1,132 @@
+/* Tomorrow Theme */
+/* Original theme - https://github.com/chriskempson/tomorrow-theme */
+/* Pretty printing styles. Used with prettify.js. */
+/* SPAN elements with the classes below are added by prettyprint. */
+/* plain text */
+.pln {
+  color: #4d4d4c; }
+
+@media screen {
+  /* string content */
+  .str {
+    color: #718c00; }
+
+  /* a keyword */
+  .kwd {
+    color: #8959a8; }
+
+  /* a comment */
+  .com {
+    color: #8e908c; }
+
+  /* a type name */
+  .typ {
+    color: #4271ae; }
+
+  /* a literal value */
+  .lit {
+    color: #f5871f; }
+
+  /* punctuation */
+  .pun {
+    color: #4d4d4c; }
+
+  /* lisp open bracket */
+  .opn {
+    color: #4d4d4c; }
+
+  /* lisp close bracket */
+  .clo {
+    color: #4d4d4c; }
+
+  /* a markup tag name */
+  .tag {
+    color: #c82829; }
+
+  /* a markup attribute name */
+  .atn {
+    color: #f5871f; }
+
+  /* a markup attribute value */
+  .atv {
+    color: #3e999f; }
+
+  /* a declaration */
+  .dec {
+    color: #f5871f; }
+
+  /* a variable name */
+  .var {
+    color: #c82829; }
+
+  /* a function name */
+  .fun {
+    color: #4271ae; } }
+/* Use higher contrast and text-weight for printable form. */
+@media print, projection {
+  .str {
+    color: #060; }
+
+  .kwd {
+    color: #006;
+    font-weight: bold; }
+
+  .com {
+    color: #600;
+    font-style: italic; }
+
+  .typ {
+    color: #404;
+    font-weight: bold; }
+
+  .lit {
+    color: #044; }
+
+  .pun, .opn, .clo {
+    color: #440; }
+
+  .tag {
+    color: #006;
+    font-weight: bold; }
+
+  .atn {
+    color: #404; }
+
+  .atv {
+    color: #060; } }
+/* Style */
+/*
+pre.prettyprint {
+  background: white;
+  font-family: Consolas, Monaco, 'Andale Mono', monospace;
+  font-size: 12px;
+  line-height: 1.5;
+  border: 1px solid #ccc;
+  padding: 10px; }
+*/
+
+/* Specify class=linenums on a pre to get line numbering */
+ol.linenums {
+  margin-top: 0;
+  margin-bottom: 0; }
+
+/* IE indents via margin-left */
+li.L0,
+li.L1,
+li.L2,
+li.L3,
+li.L4,
+li.L5,
+li.L6,
+li.L7,
+li.L8,
+li.L9 {
+  /* */ }
+
+/* Alternate shading for lines */
+li.L1,
+li.L3,
+li.L5,
+li.L7,
+li.L9 {
+  /* */ }
diff --git a/ivette/src/dome/doc/template/tmpl/augments.tmpl b/ivette/src/dome/doc/template/tmpl/augments.tmpl
new file mode 100644
index 0000000000000000000000000000000000000000..605c384364be4e131988ca4176f41a1a6fbd072b
--- /dev/null
+++ b/ivette/src/dome/doc/template/tmpl/augments.tmpl
@@ -0,0 +1,25 @@
+<?js
+    var data = obj;
+    var self = this;
+    const href = /^\[(.+)\]\((.+)\) */;
+?>
+
+<?js if (data.augments && data.augments.length) { ?>
+  <div class="summary">
+  <table class="summary">
+  <tbody>
+  <?js data.augments.forEach(function(a,descr) {
+          var m = href.exec(a) ; ?>
+       <tr>
+   <?js   if (m) { ?>
+       <td><a href="<?js= m[2] ?>"><?js= m[1] ?></a></td>
+   <?js   } else { ?>
+       <td><?js= a ?></td>
+   <?js   } ?>
+       <td>Super Class Methods</td>
+       </tr>
+  <?js }) ?>
+  </tbody>
+  </table>
+  </div>
+<?js } ?>
diff --git a/ivette/src/dome/doc/template/tmpl/container.tmpl b/ivette/src/dome/doc/template/tmpl/container.tmpl
new file mode 100644
index 0000000000000000000000000000000000000000..3fc3db4f08e64993c09305c9fc64fe96e24078a6
--- /dev/null
+++ b/ivette/src/dome/doc/template/tmpl/container.tmpl
@@ -0,0 +1,263 @@
+<?js
+    var self = this;
+    var isGlobalPage;
+
+    docs.forEach(function(doc, i) {
+?>
+
+<?js
+    // we only need to check this once
+    if (typeof isGlobalPage === 'undefined') {
+        isGlobalPage = (doc.kind === 'globalobj');
+    }
+?>
+<?js if (doc.kind === 'mainpage' || (doc.kind === 'package')) { ?>
+    <?js= self.partial('mainpage.tmpl', doc) ?>
+<?js } else if (doc.kind === 'source') { ?>
+    <?js= self.partial('source.tmpl', doc) ?>
+<?js } else { ?>
+
+<section>
+
+<header>
+    <?js if (!doc.longname || doc.kind !== 'module') { ?>
+        <h2><?js if (doc.ancestors && doc.ancestors.length) { ?>
+            <span class="ancestors"><?js= doc.ancestors.join('') ?></span><?js
+         }
+        ?><?js= (doc.name && doc.name.startsWith('module:')) ? doc.name.substring(7): doc.name ?><?js
+         if (doc.variation) { ?>
+            <sup class="variation"><?js= doc.variation ?></sup>
+        <?js } ?></h2>
+        <?js if (doc.classdesc) { ?>
+            <div class="class-description"><?js= doc.classdesc ?></div>
+        <?js } ?>
+    <?js } else if (doc.kind === 'module' && doc.modules) { ?>
+        <?js doc.modules.forEach(function(module) { ?>
+            <?js if (module.classdesc) { ?>
+                <div class="class-description"><?js= module.classdesc ?></div>
+            <?js } ?>
+        <?js }) ?>
+    <?js } ?>
+</header>
+
+<article>
+    <div class="container-overview">
+    <?js if (doc.kind === 'module' && doc.modules) { ?>
+        <?js if (doc.description) { ?>
+            <div class="description"><?js= doc.description ?></div>
+        <?js } ?>
+
+        <?js doc.modules.forEach(function(module) { ?>
+            <?js= self.partial('method.tmpl', module) ?>
+        <?js }) ?>
+    <?js } else if (doc.kind === 'class') { ?>
+        <?js= self.partial('method.tmpl', doc) ?>
+    <?js } else { ?>
+        <?js if (doc.description) { ?>
+            <div class="description"><?js= doc.description ?></div>
+        <?js } ?>
+
+        <?js= self.partial('details.tmpl', doc) ?>
+
+        <?js if (doc.examples && doc.examples.length) { ?>
+            <h3>Example<?js= doc.examples.length > 1? 's':'' ?></h3>
+            <?js= self.partial('examples.tmpl', doc.examples) ?>
+        <?js } ?>
+    <?js } ?>
+    </div>
+
+    <?js if (doc.augments && doc.augments.length) { ?>
+        <h3 class="subsection-title">Extends</h3>
+
+        <?js= self.partial('augments.tmpl', doc) ?>
+    <?js } ?>
+
+    <?js if (doc.requires && doc.requires.length) { ?>
+        <h3 class="subsection-title">Requires</h3>
+
+        <ul><?js doc.requires.forEach(function(r) { ?>
+            <li><?js= self.linkto(r, r) ?></li>
+        <?js }); ?></ul>
+    <?js } ?>
+
+    <?js
+        var classes = self.find({kind: 'class', memberof: doc.longname});
+        if (!isGlobalPage && classes && classes.length) {
+    ?>
+        <h3 class="subsection-title">Classes</h3>
+        <div class="summary">
+        <table class="summary">
+        <?js classes.forEach(function(c) { ?>
+        <tr>
+            <td><?js= self.linkto(c.longname, c.name) ?></td>
+            <td><?js if (c.summary) { ?><?js= c.summary ?><?js } ?></td>
+        </tr>
+        <?js }); ?>
+        </table>
+        </div>
+    <?js } ?>
+
+     <?js
+        var mixins = self.find({kind: 'mixin', memberof: doc.longname});
+        if (!isGlobalPage && mixins && mixins.length) {
+    ?>
+        <h3 class="subsection-title">Mixins</h3>
+
+        <dl><?js mixins.forEach(function(m) { ?>
+            <dt><?js= self.linkto(m.longname, m.name) ?></dt>
+            <dd><?js if (m.summary) { ?><?js= m.summary ?><?js } ?></dd>
+        <?js }); ?></dl>
+    <?js } ?>
+
+    <?js
+        var namespaces = self.find({kind: 'namespace', memberof: doc.longname});
+        if (!isGlobalPage && namespaces && namespaces.length) {
+    ?>
+        <h3 class="subsection-title">Namespaces</h3>
+
+        <dl><?js namespaces.forEach(function(n) { ?>
+            <dt><?js= self.linkto(n.longname, n.name) ?></dt>
+            <dd><?js if (n.summary) { ?><?js= n.summary ?><?js } ?></dd>
+        <?js }); ?></dl>
+    <?js } ?>
+
+    <?js
+        var members = self.find({kind: 'member', memberof: isGlobalPage ? {isUndefined: true} : doc.longname});
+        var methods = self.find({kind: 'function', memberof: isGlobalPage ? {isUndefined: true} : doc.longname});
+
+        var hasMembers = members && members.length && members.forEach ;
+        var hasMethods = methods && methods.length && methods.forEach ;
+
+        // symbols that are assigned to module.exports are not globals, even though they're not a memberof anything
+        if (isGlobalPage && hasMembers) {
+            members = members.filter(function(m) {
+                return m.longname && m.longname.indexOf('module:') !== 0;
+            });
+            hasMembers = members && members.length && members.forEach ;
+        }
+
+        var makeWarning = ( m , msg ) => {
+          let ident = m.longname + ':' ;
+          if (m.meta) {
+            let { path, filename, lineno } = m.meta ;
+            let root = process.env['DOME_PWD'];
+            let dir = root && path.startsWith(root) ? path.substring(root.length+1) : path ;
+            ident = dir + '/' + filename + ':' + lineno + ': (' + m.name + ')' ;
+          }
+          console.warn( ident, msg);
+        };
+
+        var makeSummary = (m) => {
+            if (m.summary) {
+               let s = m.summary.trim() ;
+               if ( s.startsWith('<p>') && s[3].toUpperCase() !== s[3] )
+                 makeWarning( m , "Not capitalized @summary" );
+               if ( s.startsWith('<p>') && ! s.endsWith('.</p>') )
+                 makeWarning( m , "Missing @summary final '.'" );
+               return s;
+            }
+            let descr = m.description ;
+            if (descr && typeof(descr)=='string' && descr.startsWith("<p>")) {
+              let pos = descr.indexOf("</p>");
+              if (0 <= pos && pos < 60) return descr.substring(0,pos)+"</p>";
+            }
+            makeWarning( m , "Missing @summary" );
+            return '<span class="dimmed">(details below)</span>' ;
+        };
+
+        if ( hasMembers || hasMethods ) {
+    ?>
+
+        <h3 class="subsection-title">Summary</h3>
+        <div class="summary">
+        <table class="summary">
+        <?js hasMembers && members.forEach(function(m) { ?>
+           <tr class="list-item">
+             <td class="list-title">
+                <a href="#<?js= m.id ?>"><code><?js= m.name ?></code></a>
+             </td>
+             <td class="list-descr">
+                <?js= makeSummary(m) ?>
+           </tr>
+        <?js }); ?>
+
+        <?js if (hasMethods && hasMembers) { ?>
+            <tr class="list-separator">
+                <td class="list-title"/>
+                <td class="list-descr"/>
+            </tr>
+        <?js } ?>
+
+        <?js hasMethods && methods.forEach(function(m) { ?>
+           <tr class="list-item">
+             <td class="list-title">
+                <a href="#<?js= m.id ?>"><code><?js= m.name ?>()</code></a>
+             </td>
+             <td class="list-descr">
+                <?js= makeSummary(m) ?>
+             </td>
+           </tr>
+        <?js }); ?>
+        </table>
+        </div>
+
+    <?js } ?>
+
+    <?js
+        if (hasMembers) {
+    ?>
+        <h3 class="subsection-title">Members</h3>
+
+        <?js members.forEach(function(p) { ?>
+            <?js= self.partial('members.tmpl', p) ?>
+        <?js }); ?>
+    <?js } ?>
+
+    <?js
+
+        if (hasMethods) {
+    ?>
+
+        <h3 class="subsection-title">Methods</h3>
+
+        <?js methods.forEach(function(m) { ?>
+            <?js= self.partial('method.tmpl', m) ?>
+        <?js }); ?>
+    <?js } ?>
+
+    <?js
+        var typedefs = self.find({kind: 'typedef', memberof: isGlobalPage ? {isUndefined: true} : doc.longname});
+        if (typedefs && typedefs.length && typedefs.forEach) {
+    ?>
+        <h3 class="subsection-title">Type Definitions</h3>
+
+        <?js typedefs.forEach(function(e) {
+                if (e.signature) {
+            ?>
+                <?js= self.partial('method.tmpl', e) ?>
+            <?js
+                }
+                else {
+            ?>
+                <?js= self.partial('members.tmpl', e) ?>
+            <?js
+                }
+            }); ?>
+    <?js } ?>
+
+    <?js
+        var events = self.find({kind: 'event', memberof: isGlobalPage ? {isUndefined: true} : doc.longname});
+        if (events && events.length && events.forEach) {
+    ?>
+        <h3 class="subsection-title">Events</h3>
+
+        <?js events.forEach(function(e) { ?>
+            <?js= self.partial('method.tmpl', e) ?>
+        <?js }); ?>
+    <?js } ?>
+</article>
+
+</section>
+<?js } ?>
+
+<?js }); ?>
diff --git a/ivette/src/dome/doc/template/tmpl/details.tmpl b/ivette/src/dome/doc/template/tmpl/details.tmpl
new file mode 100644
index 0000000000000000000000000000000000000000..3b1ee84c1324ff1399e2a81946bf19eeb9d5ced5
--- /dev/null
+++ b/ivette/src/dome/doc/template/tmpl/details.tmpl
@@ -0,0 +1,143 @@
+<?js
+var data = obj;
+var self = this;
+var defaultObjectClass = '';
+
+// Check if the default value is an object or array; if so, apply code highlighting
+if (data.defaultvalue && (data.defaultvaluetype === 'object' || data.defaultvaluetype === 'array')) {
+    data.defaultvalue = "<pre class=\"prettyprint\"><code>" + data.defaultvalue + "</code></pre>";
+    defaultObjectClass = ' class="object-value"';
+}
+?>
+<?js
+    var properties = data.properties;
+    if (properties && properties.length && properties.forEach) {
+?>
+
+    <h5 class="subsection-title">Properties</h5>
+
+    <?js= this.partial('properties.tmpl', data) ?>
+
+<?js } ?>
+
+<dl class="details">
+
+    <?js if (data.version) {?>
+    <dt class="tag-version">Version:</dt>
+    <dd class="tag-version"><ul class="dummy"><li><?js= version ?></li></ul></dd>
+    <?js } ?>
+
+    <?js if (data.since) {?>
+    <dt class="tag-since">Since:</dt>
+    <dd class="tag-since"><ul class="dummy"><li><?js= since ?></li></ul></dd>
+    <?js } ?>
+
+    <?js if (data.inherited && data.inherits && !data.overrides) { ?>
+    <dt class="inherited-from">Inherited From:</dt>
+    <dd class="inherited-from"><ul class="dummy"><li>
+        <?js= this.linkto(data.inherits, this.htmlsafe(data.inherits)) ?>
+    </li></ul></dd>
+    <?js } ?>
+
+    <?js if (data.overrides) { ?>
+    <dt class="tag-overrides">Overrides:</dt>
+    <dd class="tag-overrides"><ul class="dummy"><li>
+        <?js= this.linkto(data.overrides, this.htmlsafe(data.overrides)) ?>
+    </li></ul></dd>
+    <?js } ?>
+
+    <?js if (data.implementations && data.implementations.length) { ?>
+    <dt class="implementations">Implementations:</dt>
+    <dd class="implementations"><ul>
+        <?js data.implementations.forEach(function(impl) { ?>
+            <li><?js= self.linkto(impl, self.htmlsafe(impl)) ?></li>
+        <?js }); ?>
+    </ul></dd>
+    <?js } ?>
+
+    <?js if (data.implements && data.implements.length) { ?>
+    <dt class="implements">Implements:</dt>
+    <dd class="implements"><ul>
+        <?js data.implements.forEach(function(impl) { ?>
+            <li><?js= self.linkto(impl, self.htmlsafe(impl)) ?></li>
+        <?js }); ?>
+    </ul></dd>
+    <?js } ?>
+
+    <?js if (data.mixes && data.mixes.length) { ?>
+        <dt class="mixes">Mixes In:</dt>
+
+        <dd class="mixes"><ul>
+        <?js data.mixes.forEach(function(a) { ?>
+            <li><?js= self.linkto(a, a) ?></li>
+        <?js }); ?>
+        </ul></dd>
+    <?js } ?>
+
+    <?js if (data.deprecated) { ?>
+        <dt class="important tag-deprecated">Deprecated:</dt><?js
+            if (data.deprecated === true) { ?><dd class="yes-def tag-deprecated"><ul class="dummy"><li>Yes</li></ul></dd><?js }
+            else { ?><dd><ul class="dummy"><li><?js= data.deprecated ?></li></ul></dd><?js }
+        ?>
+    <?js } ?>
+
+    <?js if (data.author && author.length) {?>
+    <dt class="tag-author">Author:</dt>
+    <dd class="tag-author">
+        <ul><?js author.forEach(function(a) { ?>
+            <li><?js= self.resolveAuthorLinks(a) ?></li>
+        <?js }); ?></ul>
+    </dd>
+    <?js } ?>
+
+    <?js if (data.copyright) {?>
+    <dt class="tag-copyright">Copyright:</dt>
+    <dd class="tag-copyright"><ul class="dummy"><li><?js= copyright ?></li></ul></dd>
+    <?js } ?>
+
+    <?js if (data.license) {?>
+    <dt class="tag-license">License:</dt>
+    <dd class="tag-license"><ul class="dummy"><li><?js= license ?></li></ul></dd>
+    <?js } ?>
+
+    <?js if (data.defaultvalue) {?>
+    <dt class="tag-default">Default Value:</dt>
+    <dd class="tag-default"><ul class="dummy">
+            <li<?js= defaultObjectClass ?>><?js= data.defaultvalue ?></li>
+        </ul></dd>
+    <?js } ?>
+
+    <?js if (data.meta && self.outputSourceFiles) {?>
+    <dt class="tag-source">Source:</dt>
+    <dd class="tag-source"><ul class="dummy"><li>
+        <?js= self.linkto(meta.shortpath) ?>, <?js= self.linkto(meta.shortpath, 'line ' + meta.lineno, null, 'line' + meta.lineno) ?>
+    </li></ul></dd>
+    <?js } ?>
+
+    <?js if (data.tutorials && data.tutorials.length) {?>
+    <dt class="tag-tutorial">Tutorial<?js= data.tutorials.length > 1 ? 's' : ''  ?>:</dt>
+    <dd class="tag-tutorial">
+        <ul><?js tutorials.forEach(function(t) { ?>
+            <li><?js= self.tutoriallink(t) ?></li>
+        <?js }); ?></ul>
+    </dd>
+    <?js } ?>
+
+    <?js if (data.see && data.see.length) {?>
+    <dt class="tag-see">See:</dt>
+    <dd class="tag-see">
+        <ul><?js see.forEach(function(s) { ?>
+            <li><?js= self.linkto(s) ?></li>
+        <?js }); ?></ul>
+    </dd>
+    <?js } ?>
+
+    <?js if (data.todo && data.todo.length) {?>
+    <dt class="tag-todo">To Do:</dt>
+    <dd class="tag-todo">
+        <ul><?js todo.forEach(function(t) { ?>
+            <li><?js= t ?></li>
+        <?js }); ?></ul>
+    </dd>
+    <?js } ?>
+</dl>
diff --git a/ivette/src/dome/doc/template/tmpl/example.tmpl b/ivette/src/dome/doc/template/tmpl/example.tmpl
new file mode 100644
index 0000000000000000000000000000000000000000..e87caa5b72ca5b7830eb74495b831d237b3260c9
--- /dev/null
+++ b/ivette/src/dome/doc/template/tmpl/example.tmpl
@@ -0,0 +1,2 @@
+<?js var data = obj; ?>
+<pre><code><?js= data ?></code></pre>
diff --git a/ivette/src/dome/doc/template/tmpl/examples.tmpl b/ivette/src/dome/doc/template/tmpl/examples.tmpl
new file mode 100644
index 0000000000000000000000000000000000000000..04d975e96dc52016b0a0b7edb5139c14597b96ff
--- /dev/null
+++ b/ivette/src/dome/doc/template/tmpl/examples.tmpl
@@ -0,0 +1,13 @@
+<?js
+    var data = obj;
+    var self = this;
+
+    data.forEach(function(example) {
+        if (example.caption) {
+    ?>
+        <p class="code-caption"><?js= example.caption ?></p>
+    <?js } ?>
+    <pre class="prettyprint"><code><?js= self.htmlsafe(example.code) ?></code></pre>
+<?js
+    });
+?>
\ No newline at end of file
diff --git a/ivette/src/dome/doc/template/tmpl/exceptions.tmpl b/ivette/src/dome/doc/template/tmpl/exceptions.tmpl
new file mode 100644
index 0000000000000000000000000000000000000000..9cef6c7d9516f0df44b7081cb89609b388623a39
--- /dev/null
+++ b/ivette/src/dome/doc/template/tmpl/exceptions.tmpl
@@ -0,0 +1,32 @@
+<?js
+    var data = obj;
+?>
+<?js if (data.description && data.type && data.type.names) { ?>
+<dl>
+    <dt>
+        <div class="param-desc">
+        <?js= data.description ?>
+        </div>
+    </dt>
+    <dd></dd>
+    <dt>
+        <dl>
+            <dt>
+                Type
+            </dt>
+            <dd>
+                <?js= this.partial('type.tmpl', data.type.names) ?>
+            </dd>
+        </dl>
+    </dt>
+    <dd></dd>
+</dl>
+<?js } else { ?>
+    <div class="param-desc">
+    <?js if (data.description) { ?>
+        <?js= data.description ?>
+    <?js } else if (data.type && data.type.names) { ?>
+        <?js= this.partial('type.tmpl', data.type.names) ?>
+    <?js } ?>
+    </div>
+<?js } ?>
diff --git a/ivette/src/dome/doc/template/tmpl/layout.tmpl b/ivette/src/dome/doc/template/tmpl/layout.tmpl
new file mode 100644
index 0000000000000000000000000000000000000000..39dbc7889cba155499dcee4923c9df7cd4d1820e
--- /dev/null
+++ b/ivette/src/dome/doc/template/tmpl/layout.tmpl
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="utf-8">
+    <title><?js= title ?></title>
+
+    <script src="scripts/prettify/prettify.js"> </script>
+    <script src="scripts/prettify/lang-css.js"> </script>
+    <!--[if lt IE 9]>
+      <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
+    <![endif]-->
+    <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
+    <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
+</head>
+
+<body>
+  <div id="main">
+    <div id="content">
+      <div id="page">
+        <h1 class="page-title">
+          <?js=(
+           title.startsWith('Class: module:') ? 'Class ' + title.substring(14) :
+           title.startsWith('Class: ') ? 'Class ' + title.substring(7) :
+           title.startsWith('Event: ') ? 'Event ' + title.substring(7) :
+           title.startsWith('Module: ') ? 'Module ' + title.substring(8) :
+           title.startsWith('Tutorial: ') ? title.substring(10) + ' (tutorial)' :
+           title
+           )?>
+        </h1>
+        <?js= content ?>
+        <br class="clear">
+        <footer>
+          Copyright (&#169;) <?js= process.env.COPYRIGHT ?> <?js= (new Date()).getFullYear() ?>
+        </footer>
+      </div>
+    </div>
+
+    <div id="index">
+      <nav>
+        <?js= this.nav ?>
+      </nav>
+    </div>
+
+  </div>
+<script> prettyPrint(); </script>
+<script src="scripts/linenumber.js"> </script>
+</body>
+</html>
diff --git a/ivette/src/dome/doc/template/tmpl/mainpage.tmpl b/ivette/src/dome/doc/template/tmpl/mainpage.tmpl
new file mode 100644
index 0000000000000000000000000000000000000000..64e9e5943a1ddeb4a81dfa9d8b3fc1d957eb7f26
--- /dev/null
+++ b/ivette/src/dome/doc/template/tmpl/mainpage.tmpl
@@ -0,0 +1,14 @@
+<?js
+var data = obj;
+var self = this;
+?>
+
+<?js if (data.kind === 'package') { ?>
+    <h3><?js= data.name ?> <?js= data.version ?></h3>
+<?js } ?>
+
+<?js if (data.readme) { ?>
+    <section>
+        <article><?js= data.readme ?></article>
+    </section>
+<?js } ?>
diff --git a/ivette/src/dome/doc/template/tmpl/members.tmpl b/ivette/src/dome/doc/template/tmpl/members.tmpl
new file mode 100644
index 0000000000000000000000000000000000000000..66a2799d1738a5d7e26244d5da64d8da45b2868d
--- /dev/null
+++ b/ivette/src/dome/doc/template/tmpl/members.tmpl
@@ -0,0 +1,40 @@
+<?js
+var data = obj;
+var self = this;
+?>
+<h4 class="name" id="<?js= id ?>"><?js= data.attribs + name + (data.signature ? data.signature : '') ?>
+<a class="anchor" href="#<?js= id ?>">&sharp;</a>
+</h4>
+
+<?js if (data.summary) { ?>
+<p class="summary"><?js= data.summary ?></p>
+<?js } ?>
+
+<?js if (data.description) { ?>
+<div class="description">
+    <?js= data.description ?>
+</div>
+<?js } ?>
+
+<?js if (data.type && data.type.names) {?>
+    <h5>Type</h5>
+    <ul>
+        <li>
+            <?js= self.partial('type.tmpl', data.type.names) ?>
+        </li>
+    </ul>
+<?js } ?>
+
+<?js= this.partial('details.tmpl', data) ?>
+
+<?js if (data.fires && fires.length) { ?>
+    <h5>Fires</h5>
+    <ul><?js fires.forEach(function(f) { ?>
+        <li><?js= self.linkto(f) ?></li>
+    <?js }); ?></ul>
+<?js } ?>
+
+<?js if (data.examples && examples.length) { ?>
+    <h5>Example<?js= examples.length > 1? 's':'' ?></h5>
+    <?js= this.partial('examples.tmpl', examples) ?>
+<?js } ?>
diff --git a/ivette/src/dome/doc/template/tmpl/method.tmpl b/ivette/src/dome/doc/template/tmpl/method.tmpl
new file mode 100644
index 0000000000000000000000000000000000000000..c29c67b7ccd7707fd03d098a9a82b5c5d91dc571
--- /dev/null
+++ b/ivette/src/dome/doc/template/tmpl/method.tmpl
@@ -0,0 +1,103 @@
+<?js
+var data = obj;
+var self = this;
+?>
+
+<?js if (data.kind !== 'module') { ?>
+    <?js if (data.kind === 'class' && data.classdesc) { ?>
+    <h2>Constructor</h2>
+    <?js } ?>
+
+    <?js if (data.kind !== 'class') { ?>
+    <h4 class="name" id="<?js= id ?>"><?js= data.attribs + name + (data.signature || '') ?>
+    <a class="anchor" href="#<?js= id ?>">&sharp;</a>
+    </h4>
+    <?js } ?>
+
+    <?js if (data.summary) { if (data.kind === 'class') { ?>
+    <div class="summary"><p><?js= data.summary ?></p></div>
+    <?js } else { ?>
+    <p class="summary"><?js= data.summary ?></p>
+    <?js }} ?>
+<?js } ?>
+
+<?js if (data.kind !== 'module' && data.description) { ?>
+<div class="description">
+    <?js= data.description ?>
+</div>
+<?js } ?>
+
+<?js if (data.augments && data.alias && data.alias.indexOf('module:') === 0) { ?>
+    <h5>Extends</h5>
+    <?js= self.partial('augments.tmpl', data) ?>
+<?js } ?>
+
+<?js if (kind === 'event' && data.type && data.type.names) {?>
+    <h5>Type</h5>
+    <ul>
+        <li>
+            <?js= self.partial('type.tmpl', data.type.names) ?>
+        </li>
+    </ul>
+<?js } ?>
+
+<?js if (data['this']) { ?>
+    <h5>This</h5>
+    <ul><li><?js= this.linkto(data['this'], data['this']) ?></li></ul>
+<?js } ?>
+
+<?js if (data.params && params.length) { ?>
+    <h5>Parameters</h5>
+    <?js= this.partial('params.tmpl', params) ?>
+<?js } ?>
+
+<?js if (data.kind !== 'module' && data.requires && data.requires.length) { ?>
+<h5>Requires</h5>
+<ul><?js data.requires.forEach(function(r) { ?>
+    <li><?js= self.linkto(r) ?></li>
+<?js }); ?></ul>
+<?js } ?>
+
+<?js if (data.fires && fires.length) { ?>
+<h5>Fires</h5>
+<ul><?js fires.forEach(function(f) { ?>
+    <li><?js= self.linkto(f) ?></li>
+<?js }); ?></ul>
+<?js } ?>
+
+<?js if (data.listens && listens.length) { ?>
+<h5>Events</h5>
+<ul><?js listens.forEach(function(f) { ?>
+    <li><?js= self.linkto(f) ?></li>
+<?js }); ?></ul>
+<?js } ?>
+
+<?js if (data.listeners && listeners.length) { ?>
+<h5>Listeners</h5>
+<ul><?js listeners.forEach(function(f) { ?>
+    <li><?js= self.linkto(f) ?></li>
+<?js }); ?></ul>
+<?js } ?>
+
+<?js if (data.exceptions && exceptions.length) { ?>
+<h5>Throws</h5>
+<?js if (exceptions.length > 1) { ?><ul><?js
+    exceptions.forEach(function(r) { ?>
+        <li><?js= self.partial('exceptions.tmpl', r) ?></li>
+    <?js });
+?></ul><?js } else {
+    exceptions.forEach(function(r) { ?>
+        <?js= self.partial('exceptions.tmpl', r) ?>
+    <?js });
+} } ?>
+
+<?js if (data.returns && returns.length) { ?>
+    <h5>Returns</h5>
+    <?js= this.partial('returns.tmpl',returns) ?><?js } ?>
+
+<?js if (data.examples && examples.length) { ?>
+    <h5>Example<?js= examples.length > 1? 's':'' ?></h5>
+    <?js= this.partial('examples.tmpl', examples) ?>
+<?js } ?>
+
+<?js= this.partial('details.tmpl', data) ?>
diff --git a/ivette/src/dome/doc/template/tmpl/params.tmpl b/ivette/src/dome/doc/template/tmpl/params.tmpl
new file mode 100644
index 0000000000000000000000000000000000000000..000e36fb480364cb6c3cbb98cea8b4a6d5aecdc9
--- /dev/null
+++ b/ivette/src/dome/doc/template/tmpl/params.tmpl
@@ -0,0 +1,76 @@
+<?js
+    var params = obj;
+
+    /* sort subparams under their parent params (like opts.classname) */
+    var parentParam = null;
+    params.forEach(function(param, i) {
+        var paramRegExp;
+
+        if (!param) {
+            return;
+        }
+
+        if (parentParam && parentParam.name && param.name) {
+            paramRegExp = new RegExp('^(?:' + parentParam.name + '(?:\\[\\])*)\\.(.+)$');
+
+            if ( paramRegExp.test(param.name) ) {
+                param.name = RegExp.$1;
+                parentParam.subparams = parentParam.subparams || [];
+                parentParam.subparams.push(param);
+                params[i] = null;
+            }
+            else {
+                parentParam = param;
+            }
+        }
+        else {
+            parentParam = param;
+        }
+    });
+
+?>
+
+<table class="params">
+    <thead>
+    <tr>
+        <th>Name</th>
+        <th>Type</th>
+        <th class="last">Description</th>
+    </tr>
+    </thead>
+
+    <tbody>
+    <?js
+        var self = this;
+         const displayParam = (param,indent=0) => {
+            if (!param) { return; }
+    ?>
+
+        <tr>
+            <td class="name">
+                <code>
+                   <?js for( var k = 0; k < indent; k++) { ?>
+                          &nbsp;
+                   <?js } ?>
+                   <?js= (indent ? '.' : '') + param.name + (param.variable ? '...' : '') ?>
+                </code>
+            </td>
+            <td class="type">
+            <?js if (param.type && param.type.names) {?>
+                <?js= self.partial('type.tmpl', param.type.names) ?>
+                <?js if (param.optional) { ?>
+                    (opt.)
+                <?js } ?>
+            <?js } ?>
+            </td>
+            <td class="description last">
+               <?js= param.description ?>
+            </td>
+        </tr>
+        <?js if (param.subparams) param.subparams.forEach((p) => displayParam(p,indent+1)); ?>
+    <?js
+         };
+         params.forEach( (p) => displayParam(p) );
+     ?>
+    </tbody>
+</table>
diff --git a/ivette/src/dome/doc/template/tmpl/properties.tmpl b/ivette/src/dome/doc/template/tmpl/properties.tmpl
new file mode 100644
index 0000000000000000000000000000000000000000..4c73e65805e7496e79e5d1e2eb8935bf12214995
--- /dev/null
+++ b/ivette/src/dome/doc/template/tmpl/properties.tmpl
@@ -0,0 +1,75 @@
+<?js
+    var data = obj;
+    var props = data.subprops || data.properties;
+
+    /* sort subprops under their parent props (like opts.classname) */
+    var parentProp = null;
+    props.forEach(function(prop, i) {
+        if (!prop) { return; }
+        if ( parentProp && prop.name && prop.name.indexOf(parentProp.name + '.') === 0 ) {
+            prop.name = prop.name.substr(parentProp.name.length+1);
+            parentProp.subprops = parentProp.subprops || [];
+            parentProp.subprops.push(prop);
+            props[i] = null;
+        }
+        else {
+            parentProp = prop;
+        }
+    });
+?>
+
+<table class="props">
+    <thead>
+    <tr>
+        <th>Name</th>
+        <th>Type</th>
+        <th class="last">Description</th>
+    </tr>
+    </thead>
+
+    <tbody>
+    <?js
+         var self = this;
+         const displayProp = (prop,indent=0) => {
+            if (!prop) { return; }
+    ?>
+
+        <tr>
+            <td class="name">
+                <code>
+                   <?js for( var k = 0; k < indent; k++) { ?>
+                          &nbsp;
+                   <?js } ?>
+                   <?js= (indent ? '.' : '') + prop.name ?>
+                </code>
+            </td>
+            <td class="type">
+            <?js if (prop.type && prop.type.names) {?>
+                <?js= self.partial('type.tmpl', prop.type.names) ?>
+                <?js if (prop.optional) { ?>
+                    (opt.)
+                <?js } ?>
+            <?js } ?>
+            </td>
+            <td class="description last">
+                <?js= prop.description ?>
+            </td>
+        </tr>
+        <?js if (prop.subprops) prop.subprops.forEach((p) => displayProp(p,indent+1)); ?>
+
+    <?js
+         };
+         props.forEach( (p) => displayProp(p) );
+     ?>
+    <?js if (data.augments && data.augments.length) data.augments.forEach((a) => {
+             const href = /^\[(.+)\]\((.+)\) */;
+             const m = href.exec(a);
+             if (m) { ?>
+        <tr>
+            <td><tt>...</tt></td>
+            <td><a href="<?js= m[2] ?>"><?js= m[1] ?></a></td>
+            <td>Super Class Properties</td>
+        </tr>
+    <?js     }})?>
+    </tbody>
+</table>
diff --git a/ivette/src/dome/doc/template/tmpl/returns.tmpl b/ivette/src/dome/doc/template/tmpl/returns.tmpl
new file mode 100644
index 0000000000000000000000000000000000000000..0f24cbdb24a75db621659650dab9c23763b1037e
--- /dev/null
+++ b/ivette/src/dome/doc/template/tmpl/returns.tmpl
@@ -0,0 +1,25 @@
+<?js var returns = obj; const self = this; ?>
+
+<table class="params">
+    <thead>
+    <tr>
+        <th>Type</th>
+        <th class="last">Description</th>
+    </tr>
+    </thead>
+    <tbody>
+<?js returns.forEach(function (r) { ?>
+
+    <tr>
+    <td class="type">
+        <?js if (r.type && r.type.names) { ?>
+            <?js= self.partial('type.tmpl', r.type.names) ?>
+        <?js } ?>
+    </td>
+    <td class="description last">
+        <?js= r.description ?>
+    </td>
+
+<?js }); ?>
+    </tbody>
+</table>
diff --git a/ivette/src/dome/doc/template/tmpl/source.tmpl b/ivette/src/dome/doc/template/tmpl/source.tmpl
new file mode 100644
index 0000000000000000000000000000000000000000..e559b5d1038845d0cc0ee221fbd556fe85f0eb09
--- /dev/null
+++ b/ivette/src/dome/doc/template/tmpl/source.tmpl
@@ -0,0 +1,8 @@
+<?js
+    var data = obj;
+?>
+    <section>
+        <article>
+            <pre class="prettyprint source linenums"><code><?js= data.code ?></code></pre>
+        </article>
+    </section>
\ No newline at end of file
diff --git a/ivette/src/dome/doc/template/tmpl/tutorial.tmpl b/ivette/src/dome/doc/template/tmpl/tutorial.tmpl
new file mode 100644
index 0000000000000000000000000000000000000000..88a0ad52aa2ce51ad0da458d8cd7dff610b67921
--- /dev/null
+++ b/ivette/src/dome/doc/template/tmpl/tutorial.tmpl
@@ -0,0 +1,19 @@
+<section>
+
+<header>
+    <?js if (children.length > 0) { ?>
+    <ul><?js
+        var self = this;
+        children.forEach(function(t) { ?>
+        <li><?js= self.tutoriallink(t.name) ?></li>
+    <?js }); ?></ul>
+    <?js } ?>
+
+    <h2><?js= header ?></h2>
+</header>
+
+<article>
+    <?js= content ?>
+</article>
+
+</section>
diff --git a/ivette/src/dome/doc/template/tmpl/type.tmpl b/ivette/src/dome/doc/template/tmpl/type.tmpl
new file mode 100644
index 0000000000000000000000000000000000000000..ec2c6c0df7211a1791ad1b52be218ce00500d967
--- /dev/null
+++ b/ivette/src/dome/doc/template/tmpl/type.tmpl
@@ -0,0 +1,7 @@
+<?js
+    var data = obj;
+    var self = this;
+    data.forEach(function(name, i) { ?>
+<span class="param-type"><?js= self.linkto(name, self.htmlsafe(name)) ?></span>
+<?js if (i < data.length-1) { ?>|<?js } ?>
+<?js }); ?>
\ No newline at end of file
diff --git a/ivette/src/dome/doc/tutorials/application.md b/ivette/src/dome/doc/tutorials/application.md
new file mode 100644
index 0000000000000000000000000000000000000000..ec13570b91555ef2bc7ca83a7a859836bfb737fe
--- /dev/null
+++ b/ivette/src/dome/doc/tutorials/application.md
@@ -0,0 +1,119 @@
+<!-- Application Design -->
+
+This tutorial introduces how to design a **Dome** application within the
+**Electron** & **React** frameworks.  You shall be reasonably familiar with
+**React** concepts, but no knowledge of **Electron** is required.
+
+A desktop **Dome** application looks like a native application, but is actually
+a static web page rendered in a **Chrome** web browser. This is what provides
+the **Electron** framework by default. **Dome** is simply a library of **React**
+components and development templates tuned together to enable professional
+application development.
+
+This tutorial provides an overview of this environment and how to design a typical
+**Dome** application.
+
+## System Architecture
+
+Following the **Electron** framework design, your application will consists of
+two different processes interacting with each others.
+One is responsible for the management of the main GUI resources
+(windows, menu-bar, desktop interaction, user settings, etc.)
+and is named the `Main` process.
+The second one will be responsible for running the
+web page holding the main application window, and is named the `Renderer` process.
+Both kind of process communicates through the message-passing API provided by
+the **Electron** framework.
+
+When several instances of your application are running simultaneously, each invokation
+have its own window, running its owan, separate `Renderer` process. However, the
+**Dome** framework automatically makes them sharing the same `Main` process.
+
+Hence, each application instance has its own working directory and command-line arguments,
+depending on how its has been launched by the user. The **Dome** framework build
+both command-line and desktop entry points, depending on each target platform.
+
+## Event Driven Design
+
+On the renderer process side, the **React** framework induces a natural design where
+_Application State_ is separated from _Application Rendering_. Moreover,
+following a popular design introduced with **Redux**, application rendering tend
+to be written like a pure function (or _view_) on  the state, where user
+actions are just state updater callbacks. Each time the state is modified, the
+entire application rendering is recomputed, and **React** computes a minimal
+diff to apply on the web page displayed in the main application window.
+
+Putting everything together, its is recommended to design **Dome** application
+into two different parts, both running in the `Renderer` process of each
+application instance:
+
+- **Application Internals (State)**
+holding your application state and data and updating it in response to user or
+system events;
+
+- **Application Components (View)**
+responsible for rendering the application main window and
+binding callbacks to application services.
+
+<img src="dataflow.png" style="float: right; width: 30em"/>
+
+Such an architecture is typical of a _Model-View-Controller_ design, but
+revisited to scale. In particular, data flow between those three different
+layers shall follow a unique-direction pattern in order to avoid the
+combinatorial explosion of interactions between components that is generally
+observed in most Model-View-Controller designs.
+
+Hence, data-flow shall follow one of the following routes between these
+three layers, illustrated above:
+
+- from `State` to `View` : your rendering components shall only access the
+current application state and data;
+- from `View` to `State` or `System` : user action callbacks shall only trigger
+state update or system operation, not any other view direct modification.
+- from `System` to `State` : upon completion, system services shall trigger state
+updates that would in turn trigger new requests and view re-rendering.
+
+## Data Management
+
+To implement the recommended data-flow described above, you may use a full
+featured [Redux](https://redux.js.org) platform or any other framework of your
+choice. However, **Dome** provides you with many facilities for implementing
+your data flow.
+
+- **Global States** are necessary to implement the unidirectional data-flow. These
+  data are stored in the renderer process, but outside of the view hierarchy of
+  **React** components actually mounted in the DOM. Hence, it remains consistent whatever
+  the evolution of the view. See `Dome.State` class and the associated custom **React** hooks
+  to implement global states. You may also use global JavaScript variables and emit events
+  on your own.
+
+- **Local States** are necessary to maintain local states associated
+  to views.  We strongly encourage the use of the `Dome.useState()` hook for this
+  purpose, since it generalizes `React.useState()` with persistent window settings
+  (see below).
+
+- **View Updates** to make your views listening for updates of the various data
+  sources, we encourage to use the **React** hooks we provide, since they
+  transparently make your components to re-render when required. However,
+  sometimes you will need to respond to special events by hand. For this purpose,
+  you can use **Dome** as a central event emitter, by using `Dome.emit()`,
+  `Dome.on()` and `Dome.off()` functions. Moreover, the `Dome.useUpdate()` and
+  `Dome.useEvent()` hooks can be used to make your components being notified by
+  events.
+
+- **Window Settings** are stored in the user's home directory but remain
+  generally unnoticed by most users, although they are responsible for a good user
+  experience.  They typically include the window's position and dimension,
+  resizable items position, fold/unfold states, presentation options, etc. Most
+  **Dome** components with presentation options can be assigned a `settings` key
+  to make their state persistent. Contrary to Global Settings, however, they are
+  not shared across several windows. You may also access these data by using
+  `Dome.setWindowSetting()` and `Dome.getWindowSetting()`, or the **React** hook
+  `Dome.useWindowSetting()`.
+
+- **Global Settings** are stored in the user's home directory and automatically
+  saved and load with your application; they are typically modified _via_ the
+  Settings Window, which is accessible from the application menubar. In **Dome**,
+  you access these data by using `Dome.setGlobalSetting()` and
+  `Dome.getGlobalSetting()`, or the **React** hook `Dome.useGlobalSetting()`.
+  Settings must be JSON serializable JavaScript values.
diff --git a/ivette/src/dome/doc/tutorials/dataflow.png b/ivette/src/dome/doc/tutorials/dataflow.png
new file mode 100644
index 0000000000000000000000000000000000000000..648a290cadfa3d89ffe5861b76076ab18edfeac1
Binary files /dev/null and b/ivette/src/dome/doc/tutorials/dataflow.png differ
diff --git a/ivette/src/dome/doc/tutorials/development.md b/ivette/src/dome/doc/tutorials/development.md
new file mode 100644
index 0000000000000000000000000000000000000000..af424ab5c5b2725b6ade6abb67e8bd0f8f03a4f5
--- /dev/null
+++ b/ivette/src/dome/doc/tutorials/development.md
@@ -0,0 +1,204 @@
+<!-- Application Development -->
+
+The [Quick Start](tutorial-quickstart.html) tutorial introduces how to setup a new application project,
+and [Application Design](tutorial-application.html) draw a high-level view of how it works.
+This tutorial goes into more details about the development environment provided by the **Dome**
+framework.
+
+
+## HTML & JavaScript Environment
+
+The Web environment for designing your application is based on the following
+framework:
+
+- **Electron** [v5.0+](https://electronjs.org) runtime environment (packaging Chrome and Node)
+- **Node** [v12.0+](https://nodejs.org/dist/latest-v12.x/docs/api) provided by Electron
+- **Chromium** [v73.0+](https://www.chromium.org/Home) HTML rendering engine provided by Electron
+
+You will write your application in a an environment setup with the following
+JavaScript features:
+
+- **Webpack** for packaging the application from sources
+- **Babel** to parse language extensions
+- **Babel Presets Env** to use ECMA modules and JavaScript strict mode
+- **Babel Presets React** to use JSX react notation in all your `*.js` files
+- **Babel Object Rest Spread** to use object spread syntax `{ ...props }`
+- **React v16** with its latest features (including Hooks)
+- **React Hot Loader** for [live-editing](tutorial-hotreload.html) your application
+- **CSS Loader** to load and install `*.css` files
+
+## Available Packages
+
+The implementation and internals of the **Dome** framework rely on
+high-quality and mature popular packages. They are automatically packed
+into your dependencies by the provided template makefile. We list them here
+since you may re-use them for your own purpose:
+
+- [**Lodash**](https://lodash.com) convenient JavaScript utilities
+- [**React-Virtualized**](http://bvaughn.github.io/react-virtualized)
+for layout of large data set in list, tables and grids
+- [**React-Draggable**](https://github.com/mzabriskie/react-draggable)
+for straightforward drag-and-drop features
+- [**CodeMirror**](https://codemirror.net) for rich text capabilities and edition
+that scales on large document
+- [**ZeroMQ**](http://zeromq.org) for inter-process and distant communications
+(see also [System Services](tutorial-services.html)).
+
+
+## Project Files Organization
+
+The main directory holding your application project with **Dome** consists of
+several files and directories. Each is created and/or managed by various
+entities : yourself the **Developer**, the **Dome** template makefile, the
+**Yarn** JavaScript package manager and the **Electron** suite of
+utilities. Parts of those files are generated and updated on demand and shall or
+shall _not_ be tracked in your source version control system, be it based on `git`,
+`svn` or anything else.
+
+Most files are actually created and used by **Dome**, **Electron** and **Yarn**.
+However, that are _in fine_ under the responsibility of the **Developer**
+since you can edit and extend them to fit your needs.
+Those generated files generally contains comments to indicate how you
+can extend them. You shall put those files under version control only if you edit them.
+
+As depicted in the [Quick Start](tutorial-quickstart.html) tutorial, the only file you
+need to create by yourself is the main `Makefile`, with a minimal content
+limited to:
+
+```makefile
+DOME="..." ;; path to your dome installation
+include $(DOME)/template/makefile
+```
+
+The following table lists the main files and directories of your project, with
+the associated entity responsible for it, and whether they should be tracked by
+your source version control system (VCS).
+
+Optional files are tagged with « (Opt.) » in the corresponding `Entity` column,
+and are _not_ generated by default. Similarly, files mainly used by the system
+but that can still be configured by the developer are tagged with « (+Dev) ».
+Files are tagged with « ✓(*) » in the `VCS` column shall be tracked only if you
+actually modified them.
+
+| File or Directory | Description | Entity  | VCS |
+|:------------------|:------------|:-------:|:---:|
+| `./Makefile` | Drives the build system | **Developer** | ✓ |
+| `./src/main/` | Sources for the main process | **Developer** | ✓ |
+| `./src/main/index.js` | Main process entry point | **Electron** (+Dev) | ✓ |
+| `./src/renderer/` | Sources for the renderer process | **Developer** | ✓ |
+| `./src/renderer/index.js` | Renderer process entry point | **Electron** (+Dev) | ✓ |
+| `./src/renderer/Application.js` | Top React component of the main application window | **Developer** | ✓ |
+| `./src/renderer/Preferences.js` | Top React component of the user preferences window | **Developer** | ✓ |
+| `./src/common/` | Sources for _both_ processes | **Developer** (Opt.) | ✓ |
+| `./static/` | Static resources | **Developer** (Opt.) | ✓ |
+| `./package.json` | JavaScript packages configuration | **Yarn** (+Dev) | ✓ |
+| `./node_modules` | Downloaded packages | **Yarn** | 𐄂 |
+| `./yarn.lock` | Packages versions stamps | **Yarn** | ✓ |
+| `./electron-webpack.json` | Electron configuration | **Electron** (+Dev) | ✓(*) |
+| `./babelrc` | Babel configuration | **Electron** (+Dev) | ✓(*) |
+| `./webpack.main.json` | Webpack main process config. | **Electron** (+Dev) | ✓(*) |
+| `./webpack.renderer.json` | Webpack renderer process config. | **Electron** (+Dev) | ✓(*) |
+| `./.dome-*` | Dome makefile stamps | **Dome** | 𐄂 |
+| `$(DOME)/*` | Dome framework clone | **Dome** | (var.) |
+| `./dist/` | Packaged application | **Dome** | 𐄂 |
+| `./bin/` | Local command-lines | **Dome** | 𐄂 |
+
+The main entry points of the two **Electron** processes running your application,
+`src/main/index.js` and `src/renderer/index.js` shall be modified with extreme care,
+since they are in charge with the correct bootstrap of your application.
+The generated files from **Dome** templates contains explicit comments and guidelines.
+
+You may want to modify the name of your application top-level **React** components,
+namely `src/renderer/Application.js` and `src/render/Preferences.js` by default.
+To do this, you shall hook into the
+`src/renderer/index.js` file and follow the provided comments and guidelines,
+and set the `DOME_CUSTOM_ENTRIES` variable into your own `./Makefile` to make
+**Dome** aware about this.
+
+If you need static resource files to be packed with your application, put them in the `static` directory.
+You can then access to their actual path from your code by using the `System.getStatic(<path>)` utility function.
+
+The location of the **Dome** resources actually depends on the developer
+profile, as explained in more detailed in the [Live Editing](tutorial-hotreload.html) tutorial.
+To summarize:
+- for normal developers, it can be a local copy of the dome framework, for instance in your `/usr/local/lib/dome` directory;
+- for developers working with a specific or modified version of the dome framework, it can be in the local
+`./dome/` directory of your project, with or without versioning control;
+- for **Dome** developers, it is also possible to clone the Git repository inside the `./src/dome/` sub-directory of your
+project, hence also enabling live-editing of **Dome** itself;
+- for **Dome** maintainers, it is even possible to use the root of the **Dome** development repository as an application project
+in itself, for demos, examples or testing purposes.
+
+Most of the **Electron** configuration files depends on the definition of the
+`DOME` environment variable to find the necessary resources for your application
+during development. Commands initiated by **Dome** from `$(DOME)/template/makefile`
+will explicitly copy the `$(DOME)` makefile variable into the `$DOME` shell environment
+variable when calling the **Electron** builder utilities, and might use it to generate static
+files from templates, if necessary.
+
+If you ever _move_ the place where **Dome** is installed, you
+probably have to inspect the generated files that you have modified to check
+if they still behave as expected.
+
+Notice, however, that distributed versions of
+the application (those accessible from the `./dist/` directory) are
+self-contained: they are _not_ tied to your local installation of **Dome**.
+
+The command-line version of the application is accessible to the developer from the `./bin` directory.
+
+# Common Development Tasks
+
+Most development tasks shall be accessible from your `./Makefile`, and simply delegate to the
+predefined **Dome** targets in `$(DOME)/template/makefile` ; however, for very specific needs,
+you can borrow the content of the template makefile into your own one and modify it at your
+convenience. The **Dome** environment will not target this template makefile explicitly.
+
+The top target of the **Dome** template makefile is `make dome-help`,
+which is thus activated by default unless you put your own targets _before_
+its inclusion. The `dome-help` target simply echoes a short list of all the
+`dome-xxx` predefined targets in the template. It also reports on
+actual values of makefile variables used by the template.
+
+```
+$ make dome-help
+
+[Dome] Builtin Targets:
+  make dome-help    This help
+  make dome-dev     Run application in development mode
+  make dome-app     Compile application
+  make dome-dist    Package application for production
+  make dome-doc     Generate Dome documentation
+  make dome-api     Generate Application documentation
+  make dome-pkg     Install development packages
+  make dome-templ   Update templates
+  make dome-reboot  Rebuild templates from scratch
+  make dome-clean   Remove dome temporary files
+  make dome-plugins Package plugins for distribution
+
+[Dome] Development:
+  Dome framework  DOME = '…'
+  Local command   DOME_CLI  = '…'
+  Arguments (dev) DOME_ARGS = '…'
+  Export modules  DOME_EXPORTS = '…'
+  Plugin modules  DOME_PLUGINS = '…'
+
+[Dome] Documentation:
+  Application     APP          = '…'
+  Copyright       COPYRIGHT    = '…'
+  Dome framework  DOME_DOC     = '…'
+  Output dir      DOME_DOC     = '…'
+  Public API      DOME_API     = '…'
+
+```
+
+As depicted above, the **Dome** template also provides two extensible targets `help::` and `clean::`.
+By default, the `make clean` would only remove the **Dome** stamp files, not the actually generated files,
+since they can have been modified when developing the application. This can cause, however, an _update_ of
+configuration files that can be managed incrementally, typically for the `package.json` and the installed
+JavaScript packages.
+
+The **Dome** framework can generate its own documentation locally, hence you may consult it offline. It
+is also capable of generating internal **JSDoc** documentation of the application itself. The application
+documentation does _not_ contain the framework documentation, though.
+
+The environment variable can be redefined _via_ your own Makefile before the **Dome** template inclusion.
diff --git a/ivette/src/dome/doc/tutorials/glossary.md b/ivette/src/dome/doc/tutorials/glossary.md
new file mode 100644
index 0000000000000000000000000000000000000000..1368e8e25b91cf10a011d4c50ec85a4ec5f81a4f
--- /dev/null
+++ b/ivette/src/dome/doc/tutorials/glossary.md
@@ -0,0 +1,30 @@
+<!-- Glossary -->
+
+## Basic Concepts
+
+- **Component** a React Component definition (class of function)
+- **Element** a React Component instance of a Component, typically `<Component ...>`
+- **Elements** a fragment of any number of elements, typically the `children` properties of elements
+- **Render Prop** a function returning element(s), typically used instead of children elements
+
+## Component Properties
+
+- `id` an element identifier
+- `icon` an icon identifier, as listed in the [icon gallery](tutorial-icons.html)
+- `label` a short text used as the displayed title of a component
+- `text` textual content to be printed on screen
+- `title` a description for a component, usually provided in a tooltip box
+- `settings` a key for making local state(s) persistent in user's settings
+- `value` the value associated to some basic control (see `onChange`)
+- `state` an object holding some current state (see `onUpdate`)
+- `default` identifies the element selected by default in a list
+- `selected` whether an element is selected or not (never by default)
+- `disabled` whether an element will not responds to user actions (never by default)
+- `selection` the identifier or value of current selection, or `undefined` (see `onSelect`)
+- `children` the children elements of a component instance
+- `onClick` a callback function in response to a click event
+- `onClose` a callback function in response to closing action
+- `onChange` a callback function in response to value changes (see `value`)
+- `onUpdate` a callback function in response to (partial) state updates (see `state`)
+- `onSelection` a callback function in response to user selection (see `selection`)
+- `style` an inlined CSS style object to inject in a DOM element
diff --git a/ivette/src/dome/doc/tutorials/hooks.md b/ivette/src/dome/doc/tutorials/hooks.md
new file mode 100644
index 0000000000000000000000000000000000000000..00f2f0dd59b14159336ba94ba084bacb011d03a2
--- /dev/null
+++ b/ivette/src/dome/doc/tutorials/hooks.md
@@ -0,0 +1,20 @@
+<!-- Dome React Hooks --->
+
+The **Dome** framework provides several [React Hooks](https://reactjs.org/docs/hooks-intro.html)
+to ease your developpement process. Here is a summary of them.
+
+- [`useUpdate(evt)`](dome_.html#.useUpdate) to trigger re-rendering on **Dome** events;
+- [`useEvent(emitter,event,callback)`](dome_.html#.useEvent) to trigger callbacks on events;
+- [`useForceUpdate()`](dome_.html#.useForceUpdate) to trigger re-rendering on demand;
+- [`useState(settings,defaultValue)`](dome_.html#.useState)
+  similar to `React.useState()` with optional persistent _window_ settings;
+- [`useSwitch(settings,defaultValue)`](dome_.html#.useSwitch)
+  similar to `React.useState()` for boolean values with _window_ settings;
+- [`useGlobalSetting(settings,defaultValue)`](dome_.html#.useGlobalSetting)
+  similar to `React.useState()` with optional persistent _global_ settings;
+- [`useHistory(settings,defaultValue)`](dome_.html#.useHistory)
+  for managing histories
+- [`useClock(period,initStart)`](dome_.html#.useClock)
+returns start/stop clocks synchronized on period.
+- [`useCommand()`](dome_.html#.useCommand) for working with different application instances.
+- See also the [`Dome.State`](dome(renderer).State.html) and the associated hooks.
diff --git a/ivette/src/dome/doc/tutorials/hotreload.md b/ivette/src/dome/doc/tutorials/hotreload.md
new file mode 100644
index 0000000000000000000000000000000000000000..283ae63281cb34888bae4c3af77e18988b142710
--- /dev/null
+++ b/ivette/src/dome/doc/tutorials/hotreload.md
@@ -0,0 +1,45 @@
+<!-- Live Code Editing -->
+
+The **Dome** framework comes with live-editing facilities, provided by a
+combination of
+[Webpack Hot Module Reloading](https://webpack.js.org/concepts/hot-module-replacement)
+and [React Hot Loader](http://gaearon.github.io/react-hot-loader).
+This allows you to live edit your code and style sheets in real time while application
+is running. This feature is only enabled in development mode, not in production mode
+used when packaging the application for some platform.
+
+Hot reloading for **React** components is known to be a fragile feature and to
+suffer from certain limitations. Typically, files that can trigger live updates
+must be reachable from the `./src` directory, without traversing symbolic links.
+Style sheets in `*.css` files can be live edited, and **React** components in
+`*.js` files as well. Most **React** components can be live-edited, provided
+they are defined in top-level variables or exported by modules. Generally, the
+component states are preserved during live-editing.
+
+For live-editing to work properly, your source code must follow strict
+configuration settings. These rules are enforced when using configuration files
+and application entry points generated from **Dome** templates. Pay attention to
+the provided comments in generated files when modifying them.
+
+By default, we suggest to install the **Dome** framework in the sub-directory `./dome`
+of your project. Being outside `./src`, this normally prevents **Dome** files to
+be live-edited. If you want to also live-edit the framework, you shall move the
+`$(DOME)` directory inside your own `./src`.
+In particular, you might install the framework with `DOME=src/dome` for instance.
+
+**State Management:** hot-reloading on **React** components preserves application
+states _only_ when they are stored outside the reloaded module. This is the case
+for local states maintained in **React** components, as they live in the virtual DOM.
+However, for global application state stored in global variables of modules,
+they simply vanish when a module is hot-reloaded. Worst, the virtual DOM will
+be still bound to the _old_ versions of the modules. However, you usually don't
+want to live-edit the global state of your application and mix new data with
+old ones. A good practice is to separate files holding global state from files
+implementing views. Hence, live-editing the views will not cause the global state
+modules to reload, and views will stay in sync with your data. If you ever modify
+the global state, you will have to reload the application window (`CmdOrCtrl-R`).
+
+**Versioning Note:** the framework currently sticks on `webpack` version 3 and
+`react-hot-loader` version 3, because of limitations and issues when using
+`electron-webpack` with most recent versions of those libraries. However, we
+hope to be able to migrate to newer versions as soon as possible.
diff --git a/ivette/src/dome/doc/tutorials/icons.md b/ivette/src/dome/doc/tutorials/icons.md
new file mode 100644
index 0000000000000000000000000000000000000000..ee71eb82740a147dcffe4319e0c8526d2206af33
--- /dev/null
+++ b/ivette/src/dome/doc/tutorials/icons.md
@@ -0,0 +1,3 @@
+<!-- Icon Gallery -->
+
+<!-- (Generated) -->
diff --git a/ivette/src/dome/doc/tutorials/index.json b/ivette/src/dome/doc/tutorials/index.json
new file mode 100644
index 0000000000000000000000000000000000000000..981c5505336a6e6090b6f3ad086a017aafd69c84
--- /dev/null
+++ b/ivette/src/dome/doc/tutorials/index.json
@@ -0,0 +1,10 @@
+{
+  "quickstart":  { "order": 1, "title": "Quick Start" },
+  "application": { "order": 2, "title": "Application Design" },
+  "development": { "order": 3, "title": "Application Development" },
+  "hooks":       { "order": 4, "title": "Custom React Hooks" },
+  "hotreload":   { "order": 5, "title": "Live Editing" },
+  "styling":     { "order": 7, "title": "Styling Components" },
+  "icons":       { "order": 8, "title": "Icon Gallery" },
+  "glossary":    { "order": 9, "title": "Glossary" }
+}
diff --git a/ivette/src/dome/doc/tutorials/quickstart.md b/ivette/src/dome/doc/tutorials/quickstart.md
new file mode 100644
index 0000000000000000000000000000000000000000..44d9925fdd1da5072f1fd7491c69b6c58e7244eb
--- /dev/null
+++ b/ivette/src/dome/doc/tutorials/quickstart.md
@@ -0,0 +1,26 @@
+<!-- -------------------------------------------------------------------------- -->
+<!-- --- Quick Start                                                        --- -->
+<!-- -------------------------------------------------------------------------- -->
+
+We strongly recommand to use [**Yarn**](https://reactjs.org)
+for installing the necessary Node packages and their dependencies.
+
+Then, prepare a directory for developing your application, and download
+the Dome Application Framework into sub-directory `dome`.
+You shall now setup a makefile for your application as follows:
+
+<pre>
+  # Makefile
+  include dome/template/makefile
+  all: dome-dev
+</pre>
+
+Now, simply type `make` and all the necessary packages will be automatically
+installed and configured. This can take a while for the very first installation.
+You will be prompted for generating a default `package.json` file
+if you do not have some already.
+
+Eventually, your application will start with
+a « _Hello World_ » window.
+Now, you can _live edit_ the generated `src/renderer/Application.js` which is the main entry point
+of your application and see what happens into the main window.
diff --git a/ivette/src/dome/doc/tutorials/styling.md b/ivette/src/dome/doc/tutorials/styling.md
new file mode 100644
index 0000000000000000000000000000000000000000..9074a9373f0f2204fea054e8cdc182ce77564356
--- /dev/null
+++ b/ivette/src/dome/doc/tutorials/styling.md
@@ -0,0 +1,63 @@
+<!-- Styling Components -->
+
+This tutorial explains how **Dome** style sheets are organized and how
+to reuse them for styling your own components. Although modern styling
+usually rely on _style preprocessors_ to leverage the complexity of
+CSS style sheets development, the approach used here is based on separation
+of concerns and multi-class styling. The advantage is that you can
+develop your _own_ style sheets, while still being able
+to re-use the existing classes provided by the framework.
+
+All CSS classes provided by **Dome** start with prefix `dome-` ; you shall not
+use such a prefix when designing your own classes. Many class names are provided
+with optional or mandatory variants. Typically:
+
+- `dome-<base>[-<a>,<b>,...]` describes class `dome-<base>` and its variants
+`dome-<base>-<a>`, `dome-<base>-<b>` _etc_.
+
+- `dome-<name>-[<a>,<b>,...]` describes classes `dome-<name>-<a>`, `dome-<name>-<b>` _etc_.
+
+## Platform Context
+
+These classes can be used when writing selectors in order to style things
+with respect to the target platform. The main window container is attributed
+with one of the following class:
+
+- `dome-platform-[darwin,linux,windows]` derived from the `process.darwin`
+global variable (**Node.js** environment).
+
+- `dome-window-[active|inactive]` depending on whether the main application window
+has focus or not.
+
+## Color Palettes
+
+Attributing colors to graphical elements is often an issue, since a precise
+hex variants shall be tuned depending on the surrounding context. However,
+base colors are provided in public classes for several usages:
+
+- `dome-color-frame` background, foreground and borders for
+window frames, which includes tool-bars, status-bars, tabs and such.
+Those colors actually depend on the focus state of the main application window.
+
+- `dome-color-[dragzone|dragging]` for painting the background of a dragging
+zone, typically a splitting rule. The `dragzone` variant is completely
+transparent with a very transparent light gray when hovered.
+The `dragging` variant is semi-transparent blended blue with light gray.
+All these drag background effects are smoothly transitioned.
+
+## Text Properties
+
+- `dome-text-[label|title|descr]` for labels (_eg._ buttons, tabs, menu
+entries), titles (larger labels) and descriptions (_eg._ tooltips).
+Uses a sans-serif font, is non-selectable and truncated with an ellipsis.
+
+- `dome-text-[data|code]` for selectable text, typically input fields.
+Uses a sans-serif font (for `data`) or monospace font (for `code`),
+never wraps and is clipped when overflow.
+
+## Component Layout
+
+The entire application uses the `box-model` box-sizing property by default,
+since it is usually much more consistent with nested `<div>` components.
+Similarly, default margins and paddings are set to `0px` to avoid unwanted
+messy background issues.
diff --git a/ivette/src/dome/src/main/dome.js b/ivette/src/dome/src/main/dome.js
new file mode 100644
index 0000000000000000000000000000000000000000..104e7cc24c68ab525e6f5cc0c368abcfc25e5311
--- /dev/null
+++ b/ivette/src/dome/src/main/dome.js
@@ -0,0 +1,506 @@
+/**
+   @module dome(main)
+   @description
+
+   ## Dome Application (Main Process)
+
+   This module manages the electron main-process of the application.
+   Your application will launch on `Dome.start()`:
+
+   @example
+   // src/main/index.js:
+   import Dome from 'dome' ;
+   Dome.start();
+*/
+
+import {
+  app,
+  ipcMain,
+  BrowserWindow
+} from 'electron' ;
+
+import _ from 'lodash' ;
+import fs from 'fs' ;
+import path from 'path' ;
+import Menubar from './menubar.js' ;
+import System from 'dome/system' ;
+import installExtension , { REACT_DEVELOPER_TOOLS } from 'dome/devtools' ;
+
+// --------------------------------------------------------------------------
+// --- System Helpers
+// --------------------------------------------------------------------------
+
+function fstat(path) {
+  try {
+    return fs.statSync(path);
+  } catch(_error) {
+    return undefined;
+  }
+}
+
+// --------------------------------------------------------------------------
+// --- Helpers
+// --------------------------------------------------------------------------
+
+const LOCAL = process.env.DOME_LOCAL ;
+
+/** Development mode flag */
+export const DEVEL = System.DEVEL ;
+
+/** System platform */
+export const platform = System.platform ;
+
+// --------------------------------------------------------------------------
+// --- Settings
+// --------------------------------------------------------------------------
+
+const APP_DIR = app.getPath('userData');
+const APP_SETTINGS = path.join( APP_DIR , 'Settings.json' );
+
+var s_frames = {} ;
+var s_globals = {} ;
+var s_application = {} ;
+var s_preferences = {} ;
+
+function loadSettings() {
+  try {
+    if (!fstat( APP_SETTINGS )) return;
+    const content = fs.readFileSync( APP_SETTINGS, { encoding: 'utf8' } );
+    const loaded = JSON.parse( content );
+    const MERGE = (store,field) => _.merge( store , _.get( loaded , field ));
+    s_frames = MERGE( s_frames , 'frames' );
+    s_globals = MERGE( s_globals, 'globals' );
+    s_application = MERGE( s_application, 'application' );
+    s_preferences = MERGE( s_preferences, 'preferences' );
+  } catch(err) {
+    console.error("[Dome] Can not load application settings\n" + err);
+  }
+}
+
+function saveSettings() {
+  try {
+    if (!fstat( APP_DIR )) fs.mkdirSync( APP_DIR );
+    const saved = {
+      globals: s_globals,
+      application: s_application,
+      preferences: s_preferences,
+      frames: s_frames
+    };
+    const content = JSON.stringify( saved, undefined, DEVEL ? 2 : 0 );
+    fs.writeFileSync( APP_SETTINGS, content, { encoding: 'utf8' }, errorSettings );
+  } catch(err) {
+    errorSettings(err);
+  }
+}
+
+const fireSaveSettings = _.debounce( saveSettings , 50 );
+
+function errorSettings(err) {
+  if (err) console.error("[Dome] Can not save application settings\n" + err);
+}
+
+function remoteSyncSettings(event)
+{
+  const isSetting = windowSettings && windowSettings.id === event.frameId ;
+  event.returnValue = {
+    globals: s_globals,
+    settings: isSetting ? s_preferences : s_application
+  };
+}
+
+function remoteSaveWindowSettings(event,patches)
+{
+  const isSetting = windowSettings && windowSettings.id === event.frameId ;
+  _.merge( isSetting ? s_preferences : s_application , patches );
+  saveSettings();
+}
+
+function remoteSaveGlobalSettings(event,patches)
+{
+  _.merge( s_globals , patches );
+  saveSettings();
+  BrowserWindow.getAllWindows().forEach((win) => {
+    if (win.id !== event.frameId)
+      win.send('dome.ipc.settings.update',patches);
+  });
+}
+
+ipcMain.on('dome.ipc.settings.sync', remoteSyncSettings );
+ipcMain.on('dome.ipc.settings.window', remoteSaveWindowSettings );
+ipcMain.on('dome.ipc.settings.global', remoteSaveGlobalSettings );
+
+// --------------------------------------------------------------------------
+// --- Active Windows
+// --------------------------------------------------------------------------
+
+var appName = 'Dome' ;
+const MODIFIED = '(*) ' ;
+
+/**
+   Sets application window name
+   @param {string} title - application name
+ */
+export function setName(title) {
+  appName = title;
+}
+
+function setTitle(event,title) {
+  let w = BrowserWindow.fromId( event.frameId );
+  w.setTitle( title || appName );
+}
+
+function setModified(event,modified) {
+  let w = BrowserWindow.frameId( event.frameId );
+  if (platform == 'macos')
+    w.setDocumentEdited( modified );
+  else {
+    let title = w.getTitle();
+    if (title.startsWith(MODIFIED))
+      title = title.substring(MODIFIED.length);
+    if (modified)
+      title = MODIFIED + title ;
+    w.setTitle(title);
+  }
+}
+
+ipcMain.on('dome.ipc.window.title',setTitle);
+ipcMain.on('dome.ipc.window.modified',setModified);
+
+// --------------------------------------------------------------------------
+// --- Main Window Web Navigation
+// --------------------------------------------------------------------------
+
+import { shell } from 'electron' ;
+import { URL } from 'url' ;
+
+function getURL()
+{
+  if (DEVEL)
+    return `http://localhost:${process.env.ELECTRON_WEBPACK_WDS_PORT}` ;
+  if (LOCAL)
+    return 'file://' + path.join(__dirname,'../renderer/index.html') ;
+  return 'file://' + __dirname + '/index.html' ;
+}
+
+function navigateURL( event , url ) {
+  event.preventDefault();
+  const href = new URL( url );
+  const main = new URL( getURL() );
+  if (href.origin == main.origin)
+  {
+    const query = href.pathname.substring(1) + href.query + href.hash ;
+    event.sender.send('dome.ipc.href', query);
+  } else {
+    shell.openExternal( url );
+  }
+}
+
+// --------------------------------------------------------------------------
+// --- Browser Window SetUp
+// --------------------------------------------------------------------------
+
+const windowsHandle = {} ; // Prevent live windows to be garbage collected
+
+function createBrowserWindow( config, isMain=true )
+{
+
+  const argv = isMain
+        ? System.WINDOW_APPLICATION_ARGV
+        : System.WINDOW_PREFERENCES_ARGV ;
+
+  const options = _.merge(
+    {
+      show: false,
+      backgroundColor: '#f0f0f0',
+      webPreferences: {
+        nodeIntegration:true,
+        additionalArguments: [ argv ]
+      }
+    }
+    , config );
+
+  const frameId = isMain ? 'application' : 'preferences' ;
+  const frame = _.get( s_frames, frameId );
+  const getInt = (v) => v && _.toSafeInteger(v);
+  if (frame) {
+    options.x = getInt(frame.x);
+    options.y = getInt(frame.y);
+    options.width = getInt(frame.width);
+    options.height = getInt(frame.height);
+  }
+
+  const theWindow = new BrowserWindow( options );
+
+  // Load the index.html of the app.
+  if (DEVEL || LOCAL)
+    process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = 'true';
+
+  theWindow.loadURL(getURL());
+
+  // Load Finished
+  theWindow.once('ready-to-show' , () => {
+    if (DEVEL || LOCAL)
+      process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = 'false';
+    if (DEVEL)
+      theWindow.openDevTools();
+    theWindow.show();
+  });
+
+  // Focus Management
+  theWindow.on('focus', () => theWindow.send('dome.ipc.focus',true));
+  theWindow.on('blur',  () => theWindow.send('dome.ipc.focus',false));
+
+  // URL Navigation
+  theWindow.webContents.on('will-navigate', navigateURL );
+  theWindow.webContents.on('did-navigate-in-page', navigateURL );
+  theWindow.webContents.on('did-finish-load', () => broadcast('dome.ipc.reload'));
+
+  // Emitted when the window want's to close.
+  theWindow.on('close', (evt) => {
+    theWindow.send('dome.ipc.closing');
+    const frame = theWindow.getBounds();
+    _.set( s_frames, frameId , frame );
+  });
+
+  // Keep track of frame positions (in DEVEL)
+  if (DEVEL) {
+    const reframe = _.debounce( (evt) => {
+      const frame = theWindow.getBounds();
+      _.set( s_frames, frameId , frame );
+      saveSettings();
+    } , 300);
+    theWindow.on('resize',reframe);
+    theWindow.on('moved',reframe);
+  }
+
+  // Keep the window reference to prevent destruction
+  const wid = theWindow.id ;
+  windowsHandle[ wid ] = theWindow ;
+
+  // Emitted when the window is closed.
+  theWindow.on('closed', () => {
+    // Dereference the window object to actually destroy it
+    delete windowsHandle[ wid ] ;
+  });
+
+  return theWindow ;
+}
+
+// --------------------------------------------------------------------------
+// --- Application Window(s)
+// --------------------------------------------------------------------------
+
+function filterArgv( argv ) {
+  return argv.slice( DEVEL ? 3 : (LOCAL ? 2 : 1) ).filter((p) => p);
+}
+
+function sendCommand( win, argv, wdir ) {
+  win.webContents.on('did-finish-load', () => {
+    win.webContents.send('dome.ipc.command',argv,wdir);
+  });
+}
+
+function createPrimaryWindow()
+{
+  // Initialize Menubar
+  Menubar.install();
+
+  // Initialize Settings
+  loadSettings();
+
+  // React Developper Tools
+  if (DEVEL)
+    installExtension(REACT_DEVELOPER_TOOLS,true);
+
+  const primary = createBrowserWindow({ title: appName } , true);
+  const wdir = process.cwd() === '/' ? app.getPath('home') : process.cwd() ;
+  sendCommand( primary , filterArgv(process.argv) , wdir );
+}
+
+var appCount = 1;
+
+function createSecondaryWindow(_event,argv,wdir)
+{
+  const secondary = createBrowserWindow({ title: `${appName} #${++appCount}` }, true);
+  sendCommand( secondary, filterArgv(argv), wdir );
+}
+
+function createDesktopWindow()
+{
+  const instance = appCount++ ;
+  const secondary = createBrowserWindow({ title: `${appName} #${++appCount}` }, true);
+  sendCommand( secondary , [] , app.getPath('home') );
+}
+
+// --------------------------------------------------------------------------
+// --- Activate Windows (macOS)
+// --------------------------------------------------------------------------
+
+function activateWindows() {
+  var isFocused = false ;
+  var toFocus = undefined ;
+  BrowserWindow.getAllWindows().forEach((w) => {
+    w.show();
+    if (w.isFocused()) isFocused = true ;
+    else if (!toFocus) toFocus = w;
+  });
+  if (!isFocused) {
+    if (toFocus) toFocus.focus();
+    else {
+      // No focusable nor focused window
+      createDesktopWindow();
+    }
+  }
+}
+
+// --------------------------------------------------------------------------
+// --- Settings Window
+// --------------------------------------------------------------------------
+
+var windowSettings = undefined ; // Preference Window
+
+function showSettingsWindow()
+{
+  if (!windowSettings)
+    windowSettings = createBrowserWindow({
+      title: appName + ' Settings',
+      width: 256,
+      height: 248,
+      fullscreen: false,
+      maximizable: false,
+      minimizable: false
+    }, false);
+  windowSettings.show();
+  windowSettings.on('closed',() => windowSettings = undefined);
+}
+
+function restoreDefaultSettings()
+{
+  s_globals = {} ;
+  s_preferences = {} ;
+  s_application = {} ;
+  s_frames = {} ;
+  fireSaveSettings();
+  fireSaveSettings.flush();
+  broadcast( 'dome.ipc.settings.defaults' );
+}
+
+// --------------------------------------------------------------------------
+// --- Main Application Starter
+// --------------------------------------------------------------------------
+
+/** Starts the main process. */
+export function start() {
+
+  // Ensures second instance triggers the main one
+  if (!app.requestSingleInstanceLock()) app.quit();
+
+  // Change default locale
+  app.commandLine.appendSwitch('lang','en');
+
+  // Listen to window events
+  app.on( 'ready', createPrimaryWindow ); // Wait for Electron init
+  app.on( 'activate', activateWindows ); // Mac OSX response to dock
+  app.on( 'second-instance', createSecondaryWindow );
+  app.on( 'dome.menu.settings', showSettingsWindow );
+  app.on( 'dome.menu.defaults', restoreDefaultSettings );
+
+  // Performing on-exit callbacks
+  app.on( 'will-quit' , () => {
+    System.doExit() ;
+    fireSaveSettings();
+    fireSaveSettings.flush();
+  });
+
+  // On OS X menu bar stay active until the user quits explicitly from menu.
+  // On other systems, quit when all windows are closed.
+  // Warning: when no event handler is registered, the app automatically
+  // quit when all windows are closed.
+  app.on( 'window-all-closed', () => {
+    if (System.platform !== 'macos') app.quit();
+  });
+
+}
+
+// --------------------------------------------------------------------------
+// --- Renderer-Process Communication
+// --------------------------------------------------------------------------
+
+function broadcast( event, ...args )
+{
+  BrowserWindow.getAllWindows().forEach((w) => {
+    w.send( event, ...args );
+  });
+}
+
+// --------------------------------------------------------------------------
+// --- MenuBar Management
+// --------------------------------------------------------------------------
+
+const MENU_CLICK = (id,callback) => (_item,win,_event) => {
+  callback && callback();
+  app.emit('dome.menu.clicked', id );
+  win.send('dome.ipc.menu.clicked', id );
+};
+
+/**
+    @summary Define a custom main window menu.
+    @description
+    Cf. [addMenu](dome_.html#.addMenu) in the renderer process.
+*/
+export function addMenu( label ) { Menubar.addMenu( label ); }
+
+/**
+   @summary Define a custom menu item.
+   @description
+   Cf. [addMenuItem](dome_.html#.addMenuItem) in the renderer process.
+*/
+export function addMenuItem( spec )
+{
+  if ( spec.type === 'separator' ) {
+    Menubar.addMenuItem( spec );
+  } else {
+    const id = spec.id ;
+    if (!id) {
+      console.error('[Dome] Missing menu-item identifier:',spec);
+      return;
+    }
+    const { callback , ...options } = spec ;
+    options.click = MENU_CLICK( id, callback );
+    Menubar.addMenuItem( options );
+  }
+}
+
+/**
+   @summary Update a menu item.
+   @description
+   Cf. [setMenuItem](dome_.html#.setMenuItem) in the renderer process.
+*/
+export function setMenuItem( options ) {
+  const { callback , ...updates } = options ;
+  if (callback !== undefined) {
+    const id = updates.id ;
+    updates.click = MENU_CLICK( id, callback );
+  }
+  Menubar.setMenuItem( updates );
+}
+
+ipcMain.on( 'dome.ipc.menu.addmenu' , addMenu );
+ipcMain.on( 'dome.ipc.menu.addmenuitem' , addMenuItem );
+ipcMain.on( 'dome.ipc.menu.setmenuitem' , setMenuItem );
+
+// --------------------------------------------------------------------------
+// --- Export Default
+// --------------------------------------------------------------------------
+
+export default {
+  platform,
+  DEVEL,
+  setName,
+  start,
+  addMenu,
+  addMenuItem,
+  setMenuItem
+} ;
+
+// --------------------------------------------------------------------------
diff --git a/ivette/src/dome/src/main/menubar.js b/ivette/src/dome/src/main/menubar.js
new file mode 100644
index 0000000000000000000000000000000000000000..d1aa40fddb6217bb84af427e8c48c5f23f588c6e
--- /dev/null
+++ b/ivette/src/dome/src/main/menubar.js
@@ -0,0 +1,406 @@
+// --------------------------------------------------------------------------
+// --- Menus & MenuBar Management
+// --------------------------------------------------------------------------
+
+import { app, BrowserWindow, Menu, MenuItem, shell } from 'electron' ;
+import System from 'dome/system' ;
+
+// --------------------------------------------------------------------------
+// --- Special Callbacks
+// --------------------------------------------------------------------------
+
+function reloadWindow(item, focusedWindow)
+{
+  if (focusedWindow) {
+    reset(); // declared below
+    focusedWindow.send('dome.ipc.closing');
+    focusedWindow.reload();
+  }
+}
+
+function toggleFullScreen(item, focusedWindow)
+{
+  if (focusedWindow)
+    focusedWindow.setFullScreen(!focusedWindow.isFullScreen());
+}
+
+function toggleDevTools(item, focusedWindow)
+{
+  if (focusedWindow)
+    focusedWindow.toggleDevTools();
+}
+
+// --------------------------------------------------------------------------
+// --- Menu Utilities
+// --------------------------------------------------------------------------
+
+const separator = { type: 'separator' } ;
+
+function concatSep( ...menus ) {
+  var submenu = [] ;
+  var needsep = false ;
+  menus.forEach((items) => {
+    const n = items.length ;
+    if (n > 0) {
+      if (needsep)
+        submenu.push( separator );
+      submenu = submenu.concat(items);
+      needsep = (items[n-1].type !== 'separator') ;
+    }
+  });
+  return submenu ;
+}
+
+// --------------------------------------------------------------------------
+// --- MacOS Menu Items
+// --------------------------------------------------------------------------
+
+const macosAppMenuItems = (name) => [
+  {
+    label: `About ${name}`,
+    role: 'about'
+  },
+  separator,
+  {
+    label: `Preferences…`,
+    accelerator: 'Command+,',
+    click: () => app.emit('dome.menu.settings')
+  },
+  {
+    label: `Restore Defaults`,
+    click: () => app.emit('dome.menu.defaults')
+  },
+  separator,
+  {
+    label: `Services`,
+    submenu: [],
+    role: 'services'
+  },
+  separator,
+  {
+    label: `Hide ${name}`,
+    accelerator: 'Command+H',
+    role: 'hide'
+  }, {
+    label: 'Hide Others',
+    accelerator: 'Command+Alt+H',
+    role: 'hideothers'
+  }, {
+    label: 'Show All',
+    role: 'unhide'
+  },
+  separator,
+  {
+    label: 'Quit',
+    accelerator: 'Command+Q',
+    role: 'quit'
+  }
+];
+
+// --------------------------------------------------------------------------
+// --- File Menu Items (platform dependant)
+// --------------------------------------------------------------------------
+
+var fileMenuItems_custom = [];
+
+const fileMenuItems_linux = [
+  {
+    label: `Preferences…`,
+    click: () => app.emit('dome.menu.settings')
+  },
+  {
+    label: `Restore Defaults`,
+    click: () => app.emit('dome.menu.defaults')
+  },
+  separator,
+  {
+    label: 'Exit',
+    accelerator: 'Ctrl+Q',
+    role: 'quit'
+  }
+];
+
+// --------------------------------------------------------------------------
+// --- Edit Menu Items
+// --------------------------------------------------------------------------
+
+const editMenuItems = [
+  { label: 'Undo',
+    accelerator: 'CmdOrCtrl+Z',
+    role: 'undo'
+  }, {
+    label: 'Redo',
+    accelerator: 'Shift+CmdOrCtrl+Z',
+    role: 'redo'
+  },
+  separator,
+  {
+    label: 'Cut',
+    accelerator: 'CmdOrCtrl+X',
+    role: 'cut'
+  }, {
+    label: 'Copy',
+    accelerator: 'CmdOrCtrl+C',
+    role: 'copy'
+  }, {
+    label: 'Paste',
+    accelerator: 'CmdOrCtrl+V',
+    role: 'paste'
+  }, {
+    label: 'Select All',
+    accelerator: 'CmdOrCtrl+A',
+    role: 'selectall'
+  }
+];
+
+var editMenuItems_custom = [] ;
+
+// --------------------------------------------------------------------------
+// --- View Menu Items
+// --------------------------------------------------------------------------
+
+var viewMenuItems_custom = [] ;
+
+const viewMenuItems = (osx) => [
+  {
+    label: 'Reload',
+    accelerator: 'CmdOrCtrl+R',
+    click: reloadWindow
+  }, {
+    label: 'Toggle Full Screen',
+    accelerator: (osx ? 'Ctrl+Command+F' : 'F11'),
+    click: toggleFullScreen
+  }, {
+    label: 'Toggle Developer Tools',
+    accelerator: (osx ? 'Alt+Command+I' : 'Ctrl+Shift+I'),
+    click: toggleDevTools
+  }
+];
+
+// --------------------------------------------------------------------------
+// --- Window Menu Items
+// --------------------------------------------------------------------------
+
+const windowMenuItems_linux = [
+  {
+    label: 'Minimize',
+    accelerator: 'CmdOrCtrl+M',
+    role: 'minimize'
+  }, {
+    label: 'Close',
+    accelerator: 'CmdOrCtrl+W',
+    role: 'close'
+  },
+  separator,
+  {
+    label: 'Reopen Window',
+    accelerator: 'CmdOrCtrl+Shift+T',
+    enabled: false, /*?*/
+    key: 'reopenMenuItem', /*?*/
+    click: () => { app.emit('activate'); }
+  }
+];
+
+const windowMenuItems_macos = windowMenuItems_linux.concat([
+  {
+    label: 'Bring All to Front',
+    role: 'front'
+  }
+]);
+
+// --------------------------------------------------------------------------
+// --- Help Menu Items
+// --------------------------------------------------------------------------
+
+const helpMenuItems = [
+  {
+    label: 'Learn More',
+    click: function () {
+      shell.openExternal('http://electron.atom.io');
+    }
+  }
+];
+
+// --------------------------------------------------------------------------
+// --- Update MenuBar (async)
+// --------------------------------------------------------------------------
+
+var updateRequired = false ;
+var updateTriggered = false ;
+
+function requestUpdate() {
+  if (updateRequired && !updateTriggered) {
+    updateTriggered = true ;
+    setImmediate( install );
+  }
+}
+
+// --------------------------------------------------------------------------
+// --- CustomMenus
+// --------------------------------------------------------------------------
+
+var customMenus = [] ;
+var customItems = {} ;
+
+function findMenu( label ) {
+  switch( label ) {
+  case 'File': return fileMenuItems_custom;
+  case 'Edit': return editMenuItems_custom;
+  case 'View': return viewMenuItems_custom;
+  default:
+    var theMenu = customMenus.find((m) => m.label === label);
+    return theMenu && theMenu.submenu ;
+  }
+}
+
+export function addMenu( label )
+{
+  if (findMenu(label)) {
+    console.warn(`Already defined menu '${menu}'`);
+  } else {
+    customMenus.push( { label , submenu:[] } );
+  }
+  requestUpdate();
+}
+
+export function addMenuItem( { menu , key, ...spec } )
+{
+  var submenu = findMenu( menu );
+  if (!submenu) {
+    console.error(`[Dome] Unknown menu '${menu}' (menu item #${spec.id || spec.label} undefined)`);
+    return;
+  }
+  if (!spec || spec.type === 'separator') {
+    submenu.push( separator );
+  } else {
+    const id = spec.id ;
+    if (!id) {
+      console.error('[Dome] Invalid menu item:',spec);
+      return;
+    }
+    if (key) {
+      switch(System.platform) {
+      case 'macos':
+        if (key.startsWith('Cmd+')) spec.accelerator = "Cmd+" + key.substring(4) ;
+        if (key.startsWith('Alt+')) spec.accelerator = "Cmd+Alt+" + key.substring(4) ;
+        if (key.startsWith('Meta+')) spec.acceperator = "Cmd+Shift+" + key.substring(5) ;
+        break;
+      case 'windows':
+      case 'linux':
+      default:
+        if (key.startsWith('Cmd+')) spec.accelerator = "Ctrl+" + key.substring(4) ;
+        if (key.startsWith('Alt+')) spec.accelerator = "Alt+" + key.substring(4) ;
+        if (key.startsWith('Meta+')) spec.acceperator = "Ctrl+Alt+" + key.substring(5) ;
+        break;
+      }
+    }
+    const entry = customItems[id] ;
+    if (entry) {
+      if (!System.DEVEL) {
+        console.error('[Dome] Duplicate menu item:',spec);
+        return;
+      } else {
+        if (entry.spec) Object.assign( entry.spec , spec );
+        if (entry.item) Object.assign( entry.item , spec );
+      }
+    } else {
+      customItems[id] = { spec } ;
+      submenu.push( spec );
+    }
+  }
+  requestUpdate();
+}
+
+export function setMenuItem({ id, ...options })
+{
+  const entry = customItems[id] ;
+  if (entry) {
+    if (entry.spec) Object.assign( entry.spec , options );
+    if (entry.item) Object.assign( entry.item , options );
+    if ( options.label || options.type || options.click ) requestUpdate();
+  } else
+    console.warn(`[Dome] unknown menu item #${id}`);
+}
+
+// --------------------------------------------------------------------------
+// --- Menu Bar Template
+// --------------------------------------------------------------------------
+
+function template() {
+  switch(System.platform) {
+  case 'macos':
+    return [].concat(
+      [
+        { label: app.name, submenu: macosAppMenuItems(app.name) },
+        { label: 'File', submenu: fileMenuItems_custom },
+        { label: 'Edit', submenu: concatSep(editMenuItems,editMenuItems_custom) },
+        { label: 'View', submenu: concatSep(viewMenuItems_custom,viewMenuItems(true)) }
+      ],
+      customMenus,
+      [
+        { label: 'Window', role: 'window', submenu: windowMenuItems_macos },
+        { label: 'Help', role: 'help', submenu: helpMenuItems }
+      ]
+    );
+  case 'windows':
+  case 'linux':
+  default:
+    return [].concat(
+      [
+        { label: 'File', submenu: concatSep(fileMenuItems_custom,fileMenuItems_linux) },
+        { label: 'Edit', submenu: concatSep(editMenuItems,editMenuItems_custom) },
+        { label: 'View', submenu: concatSep(viewMenuItems_custom,viewMenuItems(false)) }
+      ],
+      customMenus,
+      [
+        { label: 'Window', submenu: windowMenuItems_linux },
+        { label: 'Help', submenu: helpMenuItems }
+      ]
+    );
+  }
+}
+
+// --------------------------------------------------------------------------
+// --- MenuBar SetUp
+// --------------------------------------------------------------------------
+
+var menubar ;
+
+function registerCustomItems( menu ) {
+  menu.items.forEach((item) => {
+    const entry = customItems[item.id];
+    if (entry) entry.item = item ;
+    item.submenu && registerCustomItems( item.submenu );
+  });
+}
+
+export function install() {
+  updateRequired = true;
+  updateTriggered = false;
+  menubar = Menu.buildFromTemplate(template());
+  registerCustomItems( menubar );
+  Menu.setApplicationMenu( menubar );
+}
+
+// Called by reload above
+function reset() {
+  fileMenuItems_custom = [] ;
+  editMenuItems_custom = [] ;
+  viewMenuItems_custom = [] ;
+  customMenus = [] ;
+  customItems = {} ;
+  install();
+}
+
+// --------------------------------------------------------------------------
+// --- Export Default
+// --------------------------------------------------------------------------
+
+export default {
+  install,
+  addMenu,
+  addMenuItem,
+  setMenuItem
+};
+
+// --------------------------------------------------------------------------
diff --git a/ivette/src/dome/src/misc/devtools.js b/ivette/src/dome/src/misc/devtools.js
new file mode 100644
index 0000000000000000000000000000000000000000..222b66d9d3d159ba7703dbf7d822519abc0b5ab8
--- /dev/null
+++ b/ivette/src/dome/src/misc/devtools.js
@@ -0,0 +1,12 @@
+// --------------------------------------------------------------------------
+// --- Dummy clone of electron-dev-installer
+// --------------------------------------------------------------------------
+
+// No need to export dummy identifiers (undefined is ok)
+
+export const REACT_DEVELOPER_TOOLS = undefined ;
+
+// Shall not be used in non-development mode
+export default function installExtension(_id) {
+  return Promise.reject();
+}
diff --git a/ivette/src/dome/src/misc/events.js b/ivette/src/dome/src/misc/events.js
new file mode 100644
index 0000000000000000000000000000000000000000..16c0b380da32541801b4d3873c351045ef3128ad
--- /dev/null
+++ b/ivette/src/dome/src/misc/events.js
@@ -0,0 +1,81 @@
+// --------------------------------------------------------------------------
+// --- A Ghost Component aware of its enclosing positionned container
+// --------------------------------------------------------------------------
+
+import React from 'react' ;
+import Props from 'prop-types' ;
+
+const SIZED = {
+  display: 'block',
+  position: 'absolute',
+  top: 0,
+  left: 0,
+  height: '100%',
+  width: '100%',
+  overflow: 'hidden',
+  pointerEvents: 'none',
+  zIndex: -1
+};
+
+// --------------------------------------------------------------------------
+// --- Components
+// --------------------------------------------------------------------------
+
+/*
+   This component is a ghost element to be inserted inside a _positionned_
+   container.  It makes the container aware of its bounding client rectangle
+   _via_ the `onResize` property.
+
+   The `onResize` callback is invoked whenever the parent container is
+   positionned or resized. The callback is passed the
+   `container.getBoundingClientRect()` rectangle.
+*/
+export class Layout extends React.Component {
+
+  // Init the element
+  componentDidMount() {
+    this.element.data = 'about:blank';
+    this.handleResize = this.handleResize.bind(this);
+  }
+
+  // Callback on Resize
+  handleResize() {
+    if (this.props.onResize) {
+      const r = this.element.getBoundingClientRect();
+      this.props.onResize(r);
+    }
+  }
+
+  componentWillUnmount() {
+    if (this.state.view)
+      this.state.view.removeEventListener('resize', this.handleResize);
+  }
+
+  render() {
+    return React.createElement('object', {
+      type: 'text/html', style: SIZED,
+      ref: elt => { this.element = elt; },
+      onLoad: evt => {
+        this.setState(
+          { view: evt.target.contentDocument.defaultView },
+          () => {
+            this.state.view.addEventListener('resize', this.handleResize);
+            this.handleResize();
+          }
+        );
+      }
+    });
+  }
+
+}
+
+// --------------------------------------------------------------------------
+// --- Props & Defaults
+// --------------------------------------------------------------------------
+
+Layout.propTypes = { onResize: Props.func };
+Layout.defaultProps = { };
+
+export default { Layout };
+
+// --------------------------------------------------------------------------
diff --git a/ivette/src/dome/src/misc/plugins.js b/ivette/src/dome/src/misc/plugins.js
new file mode 100644
index 0000000000000000000000000000000000000000..5d468d4017a308f65f0dfad93f3ef2dcc6c17aea
--- /dev/null
+++ b/ivette/src/dome/src/misc/plugins.js
@@ -0,0 +1,97 @@
+// --------------------------------------------------------------------------
+// --- Dome Plugins Management
+// --------------------------------------------------------------------------
+
+import fs from 'fs' ;
+import path from 'path' ;
+
+const registry = {} ;
+
+// --------------------------------------------------------------------------
+// --- Installing Bundle
+// --------------------------------------------------------------------------
+
+export function register( id, exports )
+{
+  registry[id] = Object.assign( {} , exports );
+}
+
+const WRAPPER_OPEN = '(function(module,require,__static){\n' ;
+const WRAPPER_CLOSE = '\n})' ;
+const PLUGINS = '@plugins/' ;
+
+export function install( name )
+{
+  // Resolve plugin directory
+  let dir = path.resolve( __static , "plugins" , name );
+  if (!fs.isDirectory())
+    throw `Plugin '${name}' not installed` ;
+
+  // Resolve plugin configuration
+  let pkg = path.resolve( dir , 'package.json' );
+  if (!fs.isFile(pkg))
+    throw `Plugin '${name}' has no 'package.json' file` ;
+  let config ;
+  try { config = JSON.pargse(fs.readFileSync( pkg , 'UTF-8' )); }
+  catch(err) {
+    console.error( `[Dome] reading '${pkg}':\n`, err );
+    throw `Plugin '${name}' has invalid 'package.json' file` ;
+  }
+
+  // Resolve plugin entry points
+  let bundlejs = path.resolve( dir, config.main || 'bundle.js' );
+  if (!fs.isFile(bundlejs))
+    throw `Plugin '${name}' entry point not found` ;
+  let static_d = path.resolve( dir, 'static' );
+  if (!fs.isDirectory(static_d)) static_d = undefined;
+
+  // Load bundle file
+  let bundle ;
+  try { bundle = fs.readFileSync( bundlejs , 'UTF-8' ); }
+  catch(err) {
+    console.error( `[Dome] loading '${bundlejs}':\n`, err );
+    throw `Plugin '${name}' can not load its entry point` ;
+  }
+
+  // Install bundle file
+  let id = PLUGINS + name ;
+  let exports = {} ;
+  register( id, exports ); // cut circularities
+  try {
+    let wrapped = WRAPPER_OPEN + bundle + WRAPPER_CLOSE ;
+    let compiled ; eval(wrapped);
+    let module = { id, exports };
+    compiled( module, require, static_d );
+  } catch(err) {
+    console.error( `[Dome] running '${bundlejs}':\n`, err );
+    throw `Plugin '${name}' can not install bundle` ;
+  }
+  register( id, exports ); // final exports
+
+  // Finally return exports
+  return exports ;
+}
+
+// --------------------------------------------------------------------------
+// --- Resolving Modules
+// --------------------------------------------------------------------------
+
+export function require(id)
+{
+  let exports = registry[id];
+  if (exports) return Object.assign( {} , exports );
+
+  // Resolving plugin
+
+  if (id.startsWith(PLUGINS))
+  {
+    let exports = install( id.substring(PLUGINS.length) );
+    return Object.assign( {} , exports );
+  }
+
+  // Trap
+
+  throw `Module '${id}' not found` ;
+}
+
+// --------------------------------------------------------------------------
diff --git a/ivette/src/dome/src/misc/register.js b/ivette/src/dome/src/misc/register.js
new file mode 100644
index 0000000000000000000000000000000000000000..17d3be008b3d659a052fb754d9578d4599cdc2ff
--- /dev/null
+++ b/ivette/src/dome/src/misc/register.js
@@ -0,0 +1,100 @@
+// --------------------------------------------------------------------------
+// --- Dome Classes
+// --------------------------------------------------------------------------
+
+import React from 'react' ;
+import _ from 'lodash' ;
+
+// --------------------------------------------------------------------------
+// --- Register a Dome Class
+// --------------------------------------------------------------------------
+
+export function register(Component,NAME) {
+  Component._DOME = NAME ;
+  Component.istanceOf = (elt) => (elt.type ? elt.type._DOME === NAME : false) ;
+}
+
+// --------------------------------------------------------------------------
+// --- Get the Dome Class of a Component
+// --------------------------------------------------------------------------
+
+export function classOf(elt) {
+  return elt && elt.type && elt.type._DOME ;
+}
+
+// --------------------------------------------------------------------------
+// --- Dispatch Children components among different Dome Classes
+// --------------------------------------------------------------------------
+
+/*
+   Split a React Children structure according to filter.
+   For each field { key: spec } in the filter, returns
+   a field { key: selection } with all children satisfying
+   the given spec (undefined if none).
+   Filter specification can be: a Dome class name,
+   an array of Dome class names, or 'undefined' for all non-matched
+   children in the filter.
+
+   Relies on React.Children.map instead ot React.Children.toArray
+   in order to avoid putting keys in original children when not necessary.
+*/
+export function dispatch(children,filter)
+{
+
+  var others = undefined;
+
+  const acceptor = _.mapValues( filter , (spec,field) => {
+    switch(typeof(spec)) {
+    case 'string':
+      return (elt) => classOf(elt) === spec ;
+    case 'undefined':
+      others = field ;
+      return undefined ;
+    default:
+      console.error('[Dome.common.register] non-supported filter spec',spec);
+      return undefined ;
+    }
+  } );
+
+  var count = 0 , first = true , accepted = [] ;
+  const collected = _.mapValues( acceptor , (accept,_field) => {
+    if (accept) {
+      var empty = true ;
+      var filtered =
+          React.Children.map( children , (elt) => {
+            var ok = elt && accept(elt);
+            if (ok) empty = false ;
+            if (others) {
+              if (first) accepted.push(ok);
+              else {
+                if (ok) accepted[count] = ok ;
+                count++;
+              }
+            }
+            return ok && elt;
+          });
+      first = false ; count = 0 ;
+      return empty ? undefined : filtered ;
+    } else
+      return undefined ;
+  });
+
+  if (others) {
+    var empty = true ;
+    var remaining = React.Children.map( children , (elt) => {
+      var ok = elt && !accepted[count] ;
+      if (ok) empty = false ;
+      count++;
+      return ok ? elt : null ;
+    });
+    if (!empty) collected[others] = remaining ;
+  }
+
+  return collected;
+}
+
+// --------------------------------------------------------------------------
+
+export default { register , classOf , dispatch };
+
+// --------------------------------------------------------------------------
diff --git a/ivette/src/dome/src/misc/system.js b/ivette/src/dome/src/misc/system.js
new file mode 100644
index 0000000000000000000000000000000000000000..0e609adc0f9d521e6fa7b34472dc1c6b4d0f3286
--- /dev/null
+++ b/ivette/src/dome/src/misc/system.js
@@ -0,0 +1,660 @@
+/** @module dome/system */
+
+// --------------------------------------------------------------------------
+// --- Evolved Spawn Process
+// --------------------------------------------------------------------------
+
+import _ from 'lodash' ;
+import Emitter from 'events' ;
+import Exec from 'child_process' ;
+import fspath from 'path' ;
+import fs from 'fs' ;
+import { app, remote } from 'electron' ;
+
+// --------------------------------------------------------------------------
+// --- Platform Specificities
+// --------------------------------------------------------------------------
+
+var thePlatform = 'linux' ;
+{
+  switch( process.platform ) {
+  case 'darwin':
+    thePlatform = 'macos'; break;
+  case 'win32':
+  case 'win64':
+    thePlatform = 'windows'; break;
+  case 'aix':
+  case 'freebsd':
+  case 'linux':
+  case 'openbsd':
+  case 'sunos':
+    thePlatform = 'linux'; break;
+  default:
+    console.warn(`Unkwnon OS platform '${process.platform}' (fallback to 'linux')`);
+    thePlatform = 'linux'; break;
+  }
+}
+
+/**
+   @summary System platform.
+   @description
+   Similar to `process.platform`, but fall into fewer categories:
+   - `'macos'` for Mac OSX,
+   - `'windows'` for Windows (32 or 64)
+   - `'linux'` for most unix-like platforms
+
+Non-recognized platforms will fallback to `'linux'` with the emission of a warning.
+*/
+export const platform = thePlatform ;
+
+// --------------------------------------------------------------------------
+// --- Logging
+// --------------------------------------------------------------------------
+
+/** Development mode flag */
+export const DEVEL = process.env.NODE_ENV !== 'production' ;
+
+// --------------------------------------------------------------------------
+// --- At Exit
+// --------------------------------------------------------------------------
+
+const exitJobs = [];
+
+function exitJob(job,...args) {
+  try { job(...args); }
+  catch(err) { console.err('[Dome] atExit:',err); }
+}
+
+/**
+   @summary Execute a routine at exit.
+   @param {function} callback - the function to be called when application is closing
+   @param {...any} [args] - the argument to be provided to the callback
+   @description
+   Exceptions thrown by the function are captured and reported on the console.
+ */
+export function atExit(callback,...args) {
+  exitJobs.push(() => exitJob(callback,...args));
+}
+
+/**
+   @summary Execute a callback at exit on each elements of a collection.
+   @param {array|object} elements - the collection to iterate over
+   @param {function} callback - the function to be called on each element
+   @description
+   The function will be invoked with `callback(value,key,elements)` via
+   the [Lodash `_.forOwn()`](https://lodash.com/docs/4.17.10#forOwn) iterator.
+
+   Exceptions thrown by the function are individually captured and reported
+   on the console for each element in the object or array.
+ */
+export function atExitForEach(elements,callback) {
+  atExit(() =>
+         _.forOwn(elements,(value,key,data) => exitJob(callback,value,key,data))
+        );
+}
+
+/** Execute all pending exit jobs (and flush the list). */
+export function doExit() {
+  exitJobs.forEach((f) => f());
+  exitJobs.length = 0;
+}
+
+// --------------------------------------------------------------------------
+// --- Command Line Arguments
+// --------------------------------------------------------------------------
+
+var command_wdir = undefined ;
+var command_argv = undefined ;
+
+function setCommand(argv,wdir) {
+  command_argv = argv ;
+  command_wdir = wdir ;
+}
+
+// --------------------------------------------------------------------------
+// --- User's Directories
+// --------------------------------------------------------------------------
+
+const appProxy = app || remote.app ;
+
+/** Returns user's home directory. */
+export function getHome() { return appProxy.getPath('home'); }
+
+/** Returns user's desktop directory. */
+export function getDesktop() { return appProxy.getPath('desktop'); }
+
+/** Returns user's documents directory. */
+export function getDocuments() { return appProxy.getPath('documents'); }
+
+/** Returns user's downloads directory. */
+export function getDownloads() { return appProxy.getPath('downloads'); }
+
+/**
+   @summary Working directory (Application Window).
+   @return {string} absolute path
+   @description
+   This the current working directory from where the application window
+   was opened.
+
+   The function returns `undefined` until the `dome.command` event has been emitted
+   from the `Main` process.
+
+   See also [Dome.onCommand](dome_.html#.onCommand) event handler.
+*/
+export function getWorkingDir() { return command_wdir; }
+
+/**
+   @summary Returns the current process ID.
+   @return {number} `process.pid`
+ */
+export function getPID() { return process.pid; }
+
+/**
+   @summary Command-line arguments (Application Window).
+   @return {Array.<string>} command-line arguments
+   @description
+   This the command-line arguments used to open the application window.
+
+   The function returns `undefined` until the `dome.command` event has been emitted
+   from the `Main` process.
+
+   See also [Dome.onCommand](dome_.html#.onCommand) event handler.
+*/
+export function getArguments() { return command_argv; }
+
+/** @summary Returns static assets.
+    @param {string} [...path] - a sequecne of path segments
+    @description
+    Returns the path to the associated `./static/<...path>` of your application.
+    The `./static/` directory is automatically packed into your application
+    by Dome thanks to `electron-webpack` default configuration.
+*/
+export function getStatic(...path) { return fspath.join( __static, ...path ); }
+
+// --------------------------------------------------------------------------
+// --- File Join
+// --------------------------------------------------------------------------
+
+/**
+   @summary Join file paths.
+   @param {string} [...paths] - a sequence of path segments
+   @return {string} the joined filepath
+   @description
+   Same as [Node `path.join`](https://nodejs.org/dist/latest-v12.x/docs/api/path.html#path_path_join_paths)
+*/
+export const join = fspath.join ;
+
+/**
+   @summary Absolute (joined) file paths.
+   @param {string} [...paths] - a sequence of path segments
+   @return {string} the corresponding absolute path
+   @description
+   Same as [Node `path.resolve`](https://nodejs.org/dist/latest-v12.x/docs/api/path.html#path_path_resolve_paths)
+*/
+export const resolve = fspath.resolve ;
+
+/**
+   @summary Dirname of path.
+   @param {string} path - a file path
+   @return {string} the dirname of the path
+   @description
+   Same as [Node `path.dirname`](https://nodejs.org/dist/latest-v12.x/docs/api/path.html#path_path_dirname_path)
+*/
+export const dirname = fspath.dirname ;
+
+/**
+   @summary Basename of path.
+   @param {string} path - a file path
+   @param {string} [ext] - file extension to remove
+   @return {string} the basename of the path
+   @description
+   Same as [Node `path.basename`](https://nodejs.org/dist/latest-v12.x/docs/api/path.html#path_path_basename_path_ext)
+*/
+export const basename = fspath.basename ;
+
+/**
+   @summary File extension of path.
+   @param {string} path - a file path
+   @return {string} the file extension of the path
+   @description
+   Same as [Node `path.extname`](https://nodejs.org/dist/latest-v12.x/docs/api/path.html#path_path_extname_path)
+*/
+export const extname = fspath.extname ;
+
+// --------------------------------------------------------------------------
+// --- File Stats
+// --------------------------------------------------------------------------
+
+/**
+   @summary Return an `fs.stat()` object for the path.
+   @param {string} path - the file path
+   @return {Promise<fs.Stats>} the file stats
+   @description
+Promisified [Node `fs.stat`](https://nodejs.org/dist/latest-v12.x/docs/api/fs.html#fs_fs_stat_path_callback).
+
+Returns a (promised) [Node `fs.Stats`](https://nodejs.org/dist/latest-v12.x/docs/api/fs.html#fs_class_fs_stats) object,
+including the following usefull fields and methods (and more):
+ - `stats.isFile()` returns `true` for a regular file
+ - `stats.isDirectory()` returns `true` for a directory
+ - `stats.mode` the bitfield (integer) of the file access mode
+ - `stats.size` the size of the file (in bytes)
+ - `stats.mtime` last modification time stamp (javascript `Date` object)
+
+The promise is rejected if the file does not exists.
+*/
+export function fileStat( path ) {
+  return new Promise((resolve,reject) => {
+    fs.stat( path, (err,data) => err ? reject(err) : resolve(data) );
+  });
+}
+
+/**
+   @summary Check is a path exists and is a regular file.
+   @param {string} path - the file path
+   @return {boolean} synchronous check
+*/
+export function isFile( path )
+{
+  try {
+    return path && fs.statSync( path ).isFile();
+  } catch(_err) {
+    return false;
+  }
+}
+
+/**
+   @summary Check is a path exists and is a directory.
+   @param {string} path - the dir path
+   @return {boolean} synchronous check
+*/
+export function isDirectory( path )
+{
+  try {
+    return path && fs.statSync( path ).isDirectory();
+  } catch(_err) {
+    return false;
+  }
+}
+
+/**
+   @summary Check is a path exists and is a file or directory.
+   @param {string} path - the dir path
+   @return {boolean} synchronous check
+*/
+export function exists( path )
+{
+  try {
+    if (!path) return false;
+    let stats = fs.statSync( path );
+    return stats.isFile() || stats.isDirectory();
+  } catch(_err) {
+    return false;
+  }
+}
+
+// --------------------------------------------------------------------------
+// --- Read File
+// --------------------------------------------------------------------------
+
+/**
+   @summary Reads a textual file contents.
+   @param {string} path - the file path
+   @return {Promise<string>} the file's content
+   @description
+   Promisified
+   [Node `fs.readFile`](https://nodejs.org/dist/latest-v12.x/docs/api/fs.html#fs_fs_readfile_path_options_callback)
+   using `UTF-8` encoding.
+ */
+export function readFile( path ) {
+  return new Promise((resolve,reject) => {
+    fs.readFile( path, 'UTF-8', (err,data) => err ? reject(err) : resolve(data) );
+  });
+}
+
+// --------------------------------------------------------------------------
+// --- Write File
+// --------------------------------------------------------------------------
+
+/**
+   @summary Writes a textual content in a file.
+   @param {string} path - the file path
+   @param {string} content - the content to write in
+   @return {Promise} to handle the request.
+   @description
+   Promisified
+   [Node `fs.writeFile`](https://nodejs.org/dist/latest-v12.x/docs/api/fs.html#fs_fs_writefile_file_data_options_callback)
+   using `UTF-8` encoding.
+ */
+export function writeFile( path , content ) {
+  return new Promise((resolve,reject) => {
+    fs.writeFile( path, content, 'UTF-8', (err) => err ? reject(err) : resolve() );
+  });
+}
+
+// --------------------------------------------------------------------------
+// --- Copy File
+// --------------------------------------------------------------------------
+
+/**
+   @summary Copy file to a new path.
+   @param {string} srcPath - the source file path
+   @param {string} tgtPath - the target file path
+   @return {Promise} to handle the request.
+   @description
+   Promisified
+   [Node `fs.copyFile`](https://nodejs.org/dist/latest-v12.x/docs/api/fs.html#fs_fs_copyfile_src_dest_flags_callback)
+   using `UTF-8` encoding.
+ */
+export function copyFile( srcPath , tgtPath ) {
+  return new Promise((resolve,reject) => {
+    fs.copyFile( srcPath, tgtPath, (err) => err ? reject(err) : resolve() );
+  });
+}
+
+// --------------------------------------------------------------------------
+// --- Read Directory
+// --------------------------------------------------------------------------
+
+/**
+   @summary Reads a directory.
+   @param {string} path - the directory path
+   @return {Promise<string[]>} the directory content as an array of its file names (not full path)
+   @description
+   Promisified
+   [Node `fs.readdir`](https://nodejs.org/dist/latest-v12.x/docs/api/fs.html#fs_fs_readdir_path_options_callback).
+
+   Uses `UTF-8` encoding to obtain (relative) file names instead of byte buffers. On MacOS, `.DS_Store` entries
+   are filtered out.
+*/
+export function readDir( path ) {
+  const filterDir = (f) => f !== '.DS_Store' ;
+  return new Promise((resolve,reject) => {
+    fs.readdir( path, 'UTF-8', (err,files) => err ? reject(err) : resolve(files.filter(filterDir)) );
+  });
+}
+
+// --------------------------------------------------------------------------
+// --- Make Directory
+// --------------------------------------------------------------------------
+
+/**
+   @summary Creates a new directory.
+   @param {string} path - the directory path
+   @param {object} options - permissions and mode (defaults to recursive, `0o777`)
+   @return {Promise} to handle the request.
+   @description
+   Promisified
+   [Node `fs.mkdir`](https://nodejs.org/dist/latest-v12.x/docs/api/fs.html#fs_fs_mkdir_path_options_callback).
+   Options:
+   - `mode:number` permission modes (default is `0o777`)
+   - `recursive:boolean` recursively create parent directories (default is `true`)
+*/
+export function mkDir( path, { recursive=true, mode=0o777 }={} )
+{
+  return new Promise((resolve,reject) => {
+    fs.mkdir( path, { recursive, mode }, (err) => err ? reject(err) : resolve() );
+  });
+}
+
+// --------------------------------------------------------------------------
+// --- Remove File
+// --------------------------------------------------------------------------
+
+/**
+   @summary Remove a file.
+   @param {string} path - the file path to unlink
+   @return {Promise} to handle the request.
+   @description
+   Promisified
+   [Node `fs.unlink`](https://nodejs.org/dist/latest-v12.x/docs/api/fs.html#fs_fs_unlink_path_callback)
+*/
+export function remove( path )
+{
+  return new Promise((resolve,reject) => {
+    fs.unlink( path, (err) => err ? reject(err) : resolve() );
+  });
+}
+
+// --------------------------------------------------------------------------
+// --- Remove Directory
+// --------------------------------------------------------------------------
+
+// Not (yet) implemented in Node for Electron
+function rmDirNonRec(path) {
+  return new Promise((resolve,reject) => {
+    fs.rmdir( path, (err) => err ? reject(err) : resolve() );
+  });
+}
+
+// Not (yet) implemented in Node for Electron
+function rmDirRec(path) {
+  try {
+    let stats = fs.statSync( path );
+    if (stats.isFile()) {
+      return remove(path);
+    }
+    if (stats.isDirectory()) {
+      const rmDirSub = (name) => rmDirRec(fspath.join(path,name));
+      return readDir(path)
+        .then((names) => Promise.all(names.map(rmDirSub)))
+        .then(() => rmDirNonRec(path));
+    }
+    return Promise.resolve();
+  } catch(_err) {
+    return Promise.resolve();
+  }
+}
+
+/**
+   @summary Remove a directory.
+   @param {string} path - the directory path
+   @param {object|boolean} [options] - deletion mode (defaults to non-recursive)
+   @return {Promise} to handle the request.
+   @description
+   Promisified
+   [Node `fs.rmdir`](https://nodejs.org/dist/latest-v12.x/docs/api/fs.html#fs_fs_mkdir_path_options_callback).
+   Options:
+   - `recursive:boolean` recursively remove sub-directories (default is `true`)
+*/
+export function rmDir( path, { recursive=true }={} )
+{
+  return recursive ? rmDirRec( path ) : rmDirNonRec( path );
+}
+
+// --------------------------------------------------------------------------
+// --- Rename File
+// --------------------------------------------------------------------------
+
+/**
+   @summary Rename of file of direcory.
+   @param {string} oldPath - the old path
+   @param {string} newPath - the new path
+   @return {Promise} to handle the request.
+   @description
+   Promisified
+   [Node `fs.rename`](https://nodejs.org/dist/latest-v12.x/docs/api/fs.html#fs_fs_rename_oldpath_newpath_callback)
+*/
+export function rename( oldPath, newPath )
+{
+  return new Promise((resolve,reject) => {
+    fs.rename( oldPath, newPath, (err) => err ? reject(err) : resolve() );
+  });
+}
+
+// --------------------------------------------------------------------------
+// --- Child Process
+// --------------------------------------------------------------------------
+
+const childprocess = {} ;
+
+atExitForEach(childprocess,(process) => process.kill());
+
+function stdSpec( spec , isOutput ) {
+  switch(spec) {
+  case undefined:
+    return { io: isOutput ? 'pipe' : 'ignore' };
+  case null:
+  case 'null':
+  case 'ignore':
+    return { io: 'ignore' };
+  case 'pipe':
+    return { io: 'pipe' };
+  default:
+    const fd = spec.path ? fs.openSync( spec.path , spec.mode || (isOutput ? 'w' : 'r') ) : undefined ;
+    return (isOutput && spec.pipe) ? { io: 'pipe', fd } : { io: fd } ;
+  }
+}
+
+function pipeTee( std , fd )
+{
+  if (!fd) return;
+  const out = fs.createWriteStream(null,{ fd, encoding: 'UTF-8' });
+  out.on('error',(err) => {
+    console.warn("[Dome] can not pipe:",err);
+    std.unpipe(out);
+  });
+  std.pipe(out);
+}
+
+/**
+   @summary Spawn a child process.
+   @param {string} command - the command to spawn
+   @param {string[]} [args] - the command arguments
+   @param {object} [options] - spawning options (see above)
+   @return {Promise<ChildProcess>} unless rejected, returns a process
+   object to interact with the spawned command
+   @description
+Based on [Node `child_process.spawn`](https://nodejs.org/dist/latest-v12.x/docs/api/child_process.html#child_process_child_process_spawn_command_args_options). The promised process object is a regular [Node `ChildProcess`](https://nodejs.org/api/child_process.html#child_process_class_childprocess) object, for which we recall the main useful methods below:
+
+ - `child.on('exit',(code) => {...})` emitted event when the process is terminated
+ - `child.on('close',(code) => {...})` emitted event when the process is fully terminated (all pipes closed)
+ - `child.on('message',(...data) => {...})` emitted from the _forked_ process (if applicable)
+ - `child.stdout.on('data',(text) => {...})` emitted when the process writes on piped stdout (receives `UTF-8` strings)
+ - `child.stderr.on('data',(text) => {...})` emitted when the process writes on piped stderr (receives `UTF-8` strings)
+ - `child.kill()` sends a `'SIGTERM'` unix message to the process
+
+Options is an object similar to the original Node options, with small adaptations.
+The possible option fields are described as follows:
+
+| Option | Type | Default | Description |
+|--------|------|---------|-------------|
+| `cwd`  | `string` | `process.cmd` | Working directory for the command |
+| `env`  | `object` | `{}` | _additional_ environment variables |
+| `stdin` | _stdio_ | `'ignored'` | Standard input stream |
+| `stdout` | _stdio_ | `'pipe'` | Standard output stream |
+| `stderr` | _stdio_ | `'pipe'` | Standard error stream |
+| `fork` | `boolean` | `false` | Fork a Node-child process |
+
+Environment variables are _added_ to the default `process.env` environment.
+
+Input and output streams are defined according to the following table:
+
+| _stdio_  | Description |
+|----------|-------------|
+| `'pipe'` | Accessible _via_ the promised child-process object |
+| `'ignored'` | Connected to `/dev/null` |
+| `{ path[,mode][,pipe] }` | Connected to file `path` |
+
+All pipes have their encoding set to `UTF-8`,
+hence all callbacks on process events will receive natural strings instead of raw byte buffers.
+
+When specifying a file for a process standard stream, an optional mode can be specified.
+Default is `'r'` for input streams and `'w'` for output ones.
+If option `pipe:true` is provided (output streams only), the output of the process is
+also piped through the Process object. The file-path is relative to the current working directory
+of the _application_, not be confused with the `cwd` option of the spawned command.
+
+When the `fork` flag is set, the child process is spawned using
+[Node `child_process.fork`](https://nodejs.org/dist/latest-v12.x/docs/api/child_process.html#child_process_child_process_fork_modulepath_args_options). This enables Node inter-process communication _via_ the
+`process.send()` and `process.on('message')` methods of the child process object.
+*/
+
+export function spawn(command,args,options) {
+  return new Promise((resolve,reject) => {
+
+    const cwd = options ? options.cwd : undefined ;
+    const env = options && options.env ? Object.assign( {} , process.env , options.env ) : undefined ;
+    const stdin = stdSpec( options && options.stdin , false );
+    const stdout = stdSpec( options && options.stdout , true );
+    const stderr = stdSpec( options && options.stderr , true );
+    const stdio = [ stdin.io , stdout.io , stderr.io ] ;
+    const opt = { cwd , env , stdio , windowsHide: true };
+    const fork = options && options.fork ;
+    var process ;
+
+    if (fork) {
+      opt.stdio.push( 'ipc' );
+      process = Exec.fork(command,args,opt);
+    } else {
+      process = Exec.spawn(command,args,opt);
+    }
+
+    if ( !process ) {
+      throw "[Dome] Can not create process ('"+command+"')";
+      return;
+    }
+
+    const pid = process.pid ;
+
+    if ( !pid ) {
+      // Must defer rejection, otherwize an uncaught exception is raised.
+      process.on('error',(err) => reject(err));
+      return;
+    }
+
+    childprocess[pid] = process ;
+    process.on('exit',() => delete childprocess[pid]);
+
+    const out = process.stdout ;
+    const err = process.stderr ;
+
+    if (out) {
+      out.setEncoding('UTF-8');
+      pipeTee( out , stdout.fd );
+    }
+    if (err) {
+      err.setEncoding('UTF-8');
+      pipeTee( err , stderr.fd );
+    }
+
+    resolve(process);
+  });
+}
+
+// --------------------------------------------------------------------------
+// --- Window Management
+// --------------------------------------------------------------------------
+
+const WINDOW_APPLICATION_ARGV = '--dome-application-window' ;
+const WINDOW_PREFERENCES_ARGV = '--dome-preferences-window' ;
+
+// --------------------------------------------------------------------------
+// --- Export Default
+// --------------------------------------------------------------------------
+
+export default {
+  platform,
+  DEVEL,
+  atExit,
+  atExitForEach,
+  doExit,
+  getHome,
+  getDesktop,
+  getDocuments,
+  getDownloads,
+  getWorkingDir,
+  getArguments,
+  getStatic,
+  getPID,
+  join, resolve, dirname, basename, extname,
+  fileStat,
+  isFile, readFile, writeFile, copyFile,
+  isDirectory, readDir, mkDir, rmDir,
+  rename, remove, exists,
+  spawn,
+  setCommand,
+  WINDOW_APPLICATION_ARGV,
+  WINDOW_PREFERENCES_ARGV
+};
+
+// --------------------------------------------------------------------------
diff --git a/ivette/src/dome/src/renderer/controls/buttons.css b/ivette/src/dome/src/renderer/controls/buttons.css
new file mode 100644
index 0000000000000000000000000000000000000000..83e792b3dfb9a266ec2a5ecdc6f7c5fda16ae68e
--- /dev/null
+++ b/ivette/src/dome/src/renderer/controls/buttons.css
@@ -0,0 +1,309 @@
+/* -------------------------------------------------------------------------- */
+/* --- Base Styling                                                       --- */
+/* -------------------------------------------------------------------------- */
+
+.dome-control-enabled {
+    color: #333 ;
+    font-weight: normal ;
+}
+
+.dome-control-disabled {
+    color: #b0b0b0 ;
+    fill: #b0b0b0 ;
+    font-weight: lighter ;
+}
+
+.dome-control-hidden {
+    visibility: hidden ;
+}
+
+.dome-control-erased {
+    display: none ;
+}
+
+/* -------------------------------------------------------------------------- */
+/* --- Styling Buttons                                                    --- */
+/* -------------------------------------------------------------------------- */
+
+.dome-xButton {
+    flex: 0 0 auto ;
+    align-self: baseline ;
+}
+
+.dome-xBoxButton {
+    padding: 3px 8px ;
+    margin: 2px 4px ;
+    line-height: 1.4;
+    cursor: default;
+    border: 1px solid transparent;
+    border-radius: 6px;
+    box-shadow: 0 1px 1px rgba(0, 0, 0, 0.06);
+}
+
+.dome-xCircButton {
+    padding: 2px ;
+    margin: 2px 4px ;
+    cursor: default;
+    border: 1px solid transparent;
+    border-radius: 20px;
+    box-shadow: 0 1px 1px rgba(0, 0, 0, 0.06);
+}
+
+.dome-xButton:disabled,
+.dome-xButton:active:disabled,
+.dome-window-inactive .dome-xButton
+{
+    fill: #b0b0b0 ;
+    border-color: #ccc ;
+    background-color: #f4f4f4 ;
+    background-image: none;
+}
+
+.dome-xButton:active {
+    background-color: #ddd;
+    background-image: none;
+}
+
+.dome-xButton-default {
+    border-color: #c2c0c2 ;
+    background-color: #f4f4f4 ;
+}
+
+.dome-xButton-default:hover:not(:disabled) {
+    background-color: #fff ;
+}
+
+.dome-xButton-selected {
+    border-color: #c2c0c2 ;
+    background-color: #ccc ;
+}
+
+.dome-xButton-selected:hover:not(:disabled) {
+    background-color: #fff ;
+}
+
+.dome-xButton-primary {
+    border-color: #888;
+    background-image: linear-gradient(to bottom, #449bef 0%, #4990e2 100%);
+}
+
+.dome-xButton-primary:hover:not(:disabled) {
+    background-color: #00b6ff ;
+    background-image: none ;
+}
+
+.dome-xButton-positive {
+    border-color: #888;
+    background-image: linear-gradient(to bottom, #60f577 0%, #3efb5b 100%);
+}
+
+.dome-xButton-positive:hover:not(:disabled) {
+    background-color: #00ff00 ;
+    background-image: none ;
+}
+
+.dome-xButton-negative {
+    border-color: #888;
+    fill: #333;
+    color: #333 ;
+    background-image: linear-gradient(to bottom, #ff827d 0%, #fb3832 100%);
+}
+
+.dome-xButton-negative:hover:not(:disabled) {
+    background-color: #ff0000 ;
+    background-image: none ;
+}
+
+.dome-xButton-warning {
+    border-color: #888;
+    background-image: linear-gradient(to bottom, #ffd483 0%, #ffb426 100%);
+}
+
+.dome-xButton-warning:hover:not(:disabled) {
+    background-color: #ecdd09 ;
+    background-image: none ;
+}
+
+.dome-xButton-label {
+    position: relative ;
+    padding: 2px ;
+    display: inline ;
+    font-size: 12px ;
+}
+
+.dome-xButton-label.dome-control-enabled {
+    position: absolute ; left: 0 ; top: 0 ; width: 100% ;
+    /* font is smaller than disabled => absolute position */
+}
+
+.dome-xButton-label.dome-control-disabled {
+    position: relative ; width: 100% ;
+    /* font is larger than enabled => relative position */
+}
+
+/* -------------------------------------------------------------------------- */
+/* --- Styling LED & LCD                                                  --- */
+/* -------------------------------------------------------------------------- */
+
+.dome-xButton-lcd {
+    text-align: right ;
+    color: #61611a ;
+    background-color: #b2b798 ;
+    border-color: #c2c0c2 ;
+}
+
+.dome-xButton-led {
+    display: inline ;
+    position: relative ;
+    bottom: -2px ;
+    border-color: #999 ;
+    border-style: solid ;
+    border-width: 1px ;
+    border-radius: 50% ;
+    margin: 2px 4px 2px 4px ;
+    padding: 2px ;
+    width: 11px ;
+    height: 11px ;
+}
+
+.dome-xButton-led-inactive {
+    background: none ;
+}
+
+.dome-xButton-led-active {
+    background: radial-gradient( circle at center , #9fd2f9 , #81c9ff );
+}
+
+.dome-xButton-led-positive {
+    background: radial-gradient( circle at center , #00ff0091 , #00ff00 );
+}
+
+.dome-xButton-led-negative {
+    background: radial-gradient( circle at center , #ff5b56 , #f75a55c7 );
+}
+
+.dome-xButton-led-warning {
+    background: radial-gradient( circle at center , #ffc749 , #ecd44f );
+}
+
+/* -------------------------------------------------------------------------- */
+/* --- Styling Checkbox                                                   --- */
+/* -------------------------------------------------------------------------- */
+
+.dome-xCheckbox {
+    padding: 0 ;
+    margin: 1px 4px ;
+}
+
+.dome-xCheckbox input {
+    margin-right: 8px ;
+    position: relative ;
+    left: 2px ;
+}
+
+.dome-xRadio-group {
+    display: flex ;
+    overflow: hidden ;
+    flex-flow: column nowrap ;
+    align-items: flex-start ;
+    justify-contents: flex-start ;
+}
+
+/* -------------------------------------------------------------------------- */
+/* --- Styling IconButton                                                 --- */
+/* -------------------------------------------------------------------------- */
+
+.dome-xIconButton {
+    margin: 4px ;
+    background: transparent ;
+}
+
+.dome-xIconButton:hover {
+    fill: #00b6ff ;
+}
+
+.dome-xIconButton-default {
+    fill: #777 ;
+}
+
+.dome-xIconButton-selected { fill: #449bef ; }
+
+.dome-xIconButton-positive { fill: #00d000 ; }
+
+.dome-xIconButton-negative { fill: #fb3832 ; }
+
+.dome-xIconButton-warning {
+    fill: #449bef ;
+}
+
+.dome-xIconButton.dome-control-disabled,
+.dome-xIconButton.dome-control-disabled:hover
+{
+    fill: #eee ;
+}
+
+.dome-xIconButton.dome-control-hidden {
+    visibility: hidden ;
+}
+
+.dome-xIconButton.dome-control-erased {
+    display: none ;
+}
+
+/* -------------------------------------------------------------------------- */
+/* --- Styling Switches                                                   --- */
+/* -------------------------------------------------------------------------- */
+
+.dome-xSwitch {
+    flex: 0 0 auto ;
+    padding: 0px ;
+    margin: 1px 4px ;
+}
+
+.dome-xSwitch .dome-xIcon {
+    position: relative ;
+    height: 0px ;
+    top: 2px ;
+    margin-right: 3px ;
+}
+
+.dome-xSwitch.dome-control-enabled:hover:not(:disabled) {
+    fill: blue ;
+}
+
+/* -------------------------------------------------------------------------- */
+/* --- Styling Select                                                     --- */
+/* -------------------------------------------------------------------------- */
+
+.dome-xSelect {
+    width: max-content ;
+}
+
+/* -------------------------------------------------------------------------- */
+/* --- Styling Field                                                     --- */
+/* -------------------------------------------------------------------------- */
+
+.dome-xField {
+    font-size: 10pt ;
+    width: 120px ;
+    padding-top:    1px ;
+    padding-bottom: 1px ;
+    padding-right: 4px ;
+    padding-left:  4px ;
+}
+
+/* -------------------------------------------------------------------------- */
+/* --- Blinker
+/* -------------------------------------------------------------------------- */
+
+.dome-xButton-blink {
+    animation: dome-xButton-blinker 1s linear infinite;
+}
+
+@keyframes dome-xButton-blinker {
+  50% {
+    background: none;
+  }
+}
+
+/* -------------------------------------------------------------------------- */
diff --git a/ivette/src/dome/src/renderer/controls/buttons.js b/ivette/src/dome/src/renderer/controls/buttons.js
new file mode 100644
index 0000000000000000000000000000000000000000..63c06e8eae131c4b91515872ba36450e672b712e
--- /dev/null
+++ b/ivette/src/dome/src/renderer/controls/buttons.js
@@ -0,0 +1,560 @@
+// --------------------------------------------------------------------------
+// --- Buttons, Check Boxes and Radio Groups
+// --------------------------------------------------------------------------
+
+/**
+   @module dome/controls/buttons
+   @description
+
+   Those controls may work as _controlled_ or _uncontrolled_ components,
+   although you shall not use both modes at the same times.
+
+*/
+
+import React from 'react' ;
+import { Icon } from './icons' ;
+import './buttons.css' ;
+
+const DISABLED = ({ disabled=false, enabled=true }) => !!disabled || !enabled ;
+const TRIGGER = (onClick) => (evt) => {
+  evt.stopPropagation();
+  if (onClick) onClick();
+};
+
+// --------------------------------------------------------------------------
+// --- LCD
+// --------------------------------------------------------------------------
+
+const LCDCLASS = 'dome-xButton dome-xBoxButton dome-text-code dome-xButton-lcd ' ;
+
+/**
+   @class
+   @summary Button-like label.
+   @property {string} [text] - Textual content (prepend to children components, if any)
+   @property {string} [icon] - Label icon (optional, on left side)
+   @property {string} [title] - Label tooltip (optional)
+   @property {string} [className] - Additional class
+   @property {object} [style] - Additional CSS style
+*/
+export const LCD = (props) => (
+  <label className={LCDCLASS + (props.className || '')}
+         title={props.title}
+         style={props.style} >
+    {props.icon && <Icon id={props.icon}/>}
+    {props.label}
+    {props.text}
+    {props.children}
+  </label>
+);
+
+// --------------------------------------------------------------------------
+// --- Led
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @summary Led indicator
+   @property {string} [status] - Led status and color (default: inactive)
+   @property {boolean} [blink] - Led blinking (default: false)
+   @property {string} [title] - Led tooltip (optional)
+   @property {string} [className] - Additional class
+   @property {object} [style] - Additional CSS style
+   @description
+   LED status can be:
+   - `'inactive'`: off (or any falsy value)
+   - `'active'`: blue color
+   - `'positive'`: green color
+   - `'negative'`: red color
+   - `'warning'`: orange color
+*/
+export const LED = (props) => {
+  const classes = 'dome-xButton-led dome-xButton-led-'
+        + (props.status || 'inactive')
+        + (props.blink ? ' dome-xButton-blink' : '')
+        + (props.className ? ' ' + props.className : '') ;
+  return (<div className={classes} title={props.title} style={props.style} />);
+};
+
+// --------------------------------------------------------------------------
+// --- Button
+// --------------------------------------------------------------------------
+
+const VISIBLE = { visibility:'visible' };
+const HIDDEN  = { visibility:'hidden'  };
+
+const LABEL = ({disabled,label}) => (
+  <div className="dome-xButton-label" >
+    <div className="dome-xButton-label dome-control-enabled"
+         style={disabled ? HIDDEN : VISIBLE} >{label}</div>
+    <div className="dome-xButton-label dome-control-disabled"
+         style={disabled ? VISIBLE : HIDDEN}>{label}</div>
+  </div>
+);
+
+/**
+   @class
+   @summary Standard Button.
+   @property {string} [icon] - Label icon (optional, on left side)
+   @property {string} [label] - Button label
+   @property {string} [title] - Optional tooltip
+   @property {boolean} [selected] - Selected button
+   @property {boolean} [enabled] - Defaults to `true`
+   @property {boolean} [disabled] - Defaults to `false`
+   @property {boolean} [visible] - Defaults to `true`
+   @property {boolean} [display] - Defaults to `true`
+   @property {boolean} [focusable] - May gain focus
+   @property {string} [kind] - Styled button (see below)
+   @property {boolean} [blink] - Blinking button
+   @property {function} [onClick] - On-click callback
+   @property {string} [className] - Additional class
+   @property {object} [style] - Additional style
+   @description
+   The different available kinds for styling a button are:
+   - `'default'`: normal button;
+   - `'active'`: active normal button;
+   - `'primary'`: primary button, in blue;
+   - `'warning'`: warning button, in orange;
+   - `'positive'`: positive button, in green;
+   - `'negative'`: negative button, in red.
+
+   Buttons without focus can not be triggered with the Enter key.
+*/
+export const Button = (props) => {
+  const disabled = DISABLED(props);
+  const { focusable=false, kind='default',
+          visible=true, display=true, blink=false,
+          selected, icon, label, className='' } = props;
+  const theClass = 'dome-xButton dome-xBoxButton dome-xButton-'
+        + (selected ? 'selected' : kind)
+        + (!blink ? '' : ' dome-xButton-blink')
+        + (visible ? '' : ' dome-control-hidden')
+        + (display ? '' : ' dome-control-erased')
+        + (className ? ' ' + className : '')
+  ;
+  const nofocus = focusable ? undefined : true ;
+  return (
+    <button type='button'
+            className={theClass}
+            disabled={disabled}
+            onClick={TRIGGER(props.onClick)}
+            title={props.title}
+            style={props.style}
+            tabIndex={ nofocus && -1 }
+            onMouseDown={ nofocus && ((evt) => evt.preventDefault()) }
+      >
+      {icon && <Icon id={icon}/>}
+      {label && <LABEL disabled={disabled} label={label}/>}
+    </button>
+  );
+};
+
+// --------------------------------------------------------------------------
+// --- Icon Button
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @summary Circled Icon Button.
+   @property {string} [icon] - Label icon (optional, on left side)
+   @property {string} [title] - Optional tooltip
+   @property {boolean} [selected] - Selected button
+   @property {boolean} [enabled] - Defaults to `true`
+   @property {boolean} [disabled] - Defaults to `false`
+   @property {boolean} [visible] - Defaults to `true`
+   @property {boolean} [display] - Defaults to `true`
+   @property {boolean} [focusable] - May gain focus
+   @property {string} [kind] - Styled button (see below)
+   @property {function} [onClick] - On-click callback
+   @property {string} [className] - Additional class
+   @property {object} [style] - Additional style
+   @description
+   The different available kinds for styling a button are:
+   - `'default'`: normal button;
+   - `'active'`: active normal button;
+   - `'primary'`: primary button, in blue;
+   - `'warning'`: warning button, in orange;
+   - `'positive'`: positive button, in green;
+   - `'negative'`: negative button, in red.
+
+   Buttons without focus can not be triggered with the Enter key.
+*/
+
+export const CircButton = (props) => {
+  const disabled = DISABLED(props);
+  const { focusable=false, kind='default',
+          visible=true, display=true,
+          selected, icon, label, className='' } = props;
+  const theClass = 'dome-xButton dome-xCircButton dome-xButton-'
+        + (selected ? 'selected' : kind)
+        + (visible ? '' : ' dome-control-hidden')
+        + (display ? '' : ' dome-control-erased')
+        + (className ? ' ' + className : '') ;
+  const nofocus = focusable ? undefined : true ;
+  return (
+    <button type='button'
+            className={theClass}
+            disabled={disabled}
+            onClick={TRIGGER(props.onClick)}
+            title={props.title}
+            style={props.style}
+            tabIndex={ nofocus && -1 }
+            onMouseDown={ nofocus && ((evt) => evt.preventDefault()) }
+      >
+      {icon && <Icon offset={-1} id={icon}/>}
+    </button>
+  );
+};
+
+// --------------------------------------------------------------------------
+// --- Icon Button
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @summary Borderless Icon Button.
+   @property {string} icon - Icon identifier
+   @property {number} size - Icon size (default `12`)
+   @property {number} offset - Icon offset
+   @property {string} [title] - Optional tooltip
+   @property {boolean} [enabled] - Defaults to `true`
+   @property {boolean} [disabled] - Defaults to `false`
+   @property {boolean} [visible] - Defaults to `true`
+   @property {boolean} [display] - Defaults to `true`
+   @property {boolean} [selected] - Defaults to `false`
+   @property {function} [onClick] - Callback (receives the newly expected value)
+   @property {string} [kind] - Styled button (see below)
+   @property {string} [className] - Additional class
+   @property {object} [style] - Additional style
+   @description
+   The different available kinds for styling a button are:
+   - `'default'`: normal button;
+   - `'active'`: active normal button;
+   - `'warning'`: warning button, in orange;
+   - `'positive'`: positive button, in green;
+   - `'negative'`: negative button, in red.
+*/
+
+export const IconButton = (props) => {
+  const disabled = DISABLED(props);
+  const {
+    icon, size, title,
+    visible=true, display=true, selected,
+    kind='default'
+  } = props ;
+  const className = 'dome-xIconButton'
+        + ' dome-xIconButton-' + (selected ? 'selected' : kind)
+        + (disabled ? ' dome-control-disabled' : ' dome-control-enabled')
+        + (visible ? '' : ' dome-control-hidden')
+        + (display ? '' : ' dome-control-erased')
+        + (props.className ? ' ' + props.className : '') ;
+  return (
+    <Icon
+      size={size} id={icon} title={title}
+      style={props.style}
+      offset={props.offset}
+      className={className}
+      onClick={TRIGGER(disabled ? undefined : props.onClick)}
+      />
+  );
+};
+
+// --------------------------------------------------------------------------
+// --- CheckBox
+// --------------------------------------------------------------------------
+
+const CHECKBOX_ENABLED = 'dome-control-enabled dome-xCheckbox ' ;
+const CHECKBOX_DISABLED = 'dome-control-disabled dome-xCheckbox ' ;
+
+/**
+   @class
+   @summary CheckBox with its label.
+   @property {string} [label] - Button label
+   @property {string} [title] - Optional tooltip
+   @property {boolean} [enabled] - Defaults to `true`
+   @property {boolean} [disabled] - Defaults to `false`
+   @property {boolean} [value] - Checked or not (defaults to internal state)
+   @property {function} [onChange] - Callback (receives the newly expected value)
+   @property {string} [className] - Additional class
+   @property {object} [style] - Additional style
+*/
+export const Checkbox = (props) => {
+  const disabled = DISABLED(props);
+  const onChange = props.onChange && ((evt) => props.onChange(evt.target.checked)) ;
+  const baseClass = disabled ? CHECKBOX_DISABLED : CHECKBOX_ENABLED ;
+  const labelClass = props.className || '' ;
+  return (
+    <label
+      title={props.title}
+      style={props.style}
+      className={baseClass + labelClass} >
+      <input type="checkbox"
+             disabled={disabled}
+             checked={props.value}
+             onChange={onChange} />
+      {props.label}
+    </label>
+  );
+};
+
+// --------------------------------------------------------------------------
+// --- Switch
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @summary Switch with its label.
+   @property {string} [label] - Button label
+   @property {string} [title] - Optional tooltip
+   @property {boolean} [enabled] - Defaults to `true`
+   @property {boolean} [disabled] - Defaults to `false`
+   @property {boolean} [visible] - Defaults to `true`
+   @property {boolean} [display] - Defaults to `true`
+   @property {boolean} [value] - Checked or not (defaults to internal state)
+   @property {function} [onChange] - Callback (receives the newly expected value)
+   @property {string} [className] - Additional class
+   @property {object} [style] - Additional style
+*/
+export const Switch = (props) => {
+  const disabled = DISABLED(props);
+  const { visible=true, display=true } = props ;
+  const onChange = props.onChange && ((evt) => props.onChange(evt.target.checked)) ;
+  const iconId = props.value ? 'SWITCH.ON' : 'SWITCH.OFF' ;
+  const onClick = (disabled || !props.onChange)
+        ? undefined : () => props.onChange(!props.value) ;
+  const className = 'dome-xSwitch '
+        + (disabled ? 'dome-control-disabled' : 'dome-control-enabled')
+        + (visible ? '' : ' dome-control-hidden')
+        + (display ? '' : ' dome-control-erased')
+        + (props.className ? ' ' + props.className : '') ;
+  return (
+    <label
+      title={props.title}
+      style={props.style}
+      className={className}
+      onClick={TRIGGER(onClick)}
+      >
+      <Icon size={16} id={iconId} />
+      {props.label}
+    </label>
+  );
+};
+
+// --------------------------------------------------------------------------
+// --- Radio Button
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @summary Radio Button with its label.
+   @property {string} [label] - Button label
+   @property {string} [title] - Optional tooltip
+   @property {boolean} [enabled] - Defaults to `true`
+   @property {boolean} [disabled] - Defaults to `false`
+   @property {any} [value] - Associated value to the radio
+   @property {any} [selection] - Currently selected value
+   @property {function} [onSelection] - On-click callback
+   @property {string} [className] - Additional class
+   @property {object} [style] - Additional style
+   @description
+   See also [RadioGroup](module-dome_controls_buttons.RadioGroup.html).
+
+   <strong>Note:</strong> property `value` and `selection` are consistent
+   with HTML standards and DOM element properties.
+*/
+export const Radio = (props) => {
+  const disabled = DISABLED(props);
+  const checked = props.value === props.selection ;
+  const onChange = props.onSelection && (() => props.onSelection(props.value)) ;
+  const baseClass = disabled ? CHECKBOX_DISABLED : CHECKBOX_ENABLED ;
+  const labelClass = props.className || '' ;
+  return (
+    <label
+      title={props.title}
+      style={props.style}
+      className={baseClass + labelClass} >
+      <input type="radio" disabled={disabled} checked={checked} onChange={onChange} />
+      {props.label}
+    </label>
+  );
+};
+
+// --------------------------------------------------------------------------
+// --- Radio Group
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @summary Selector of Radio Buttons.
+   @property {any} [value] - Currently selected value
+   @property {boolean} [enabled] - Defaults to `true`
+   @property {boolean} [disabled] - Defaults to `false`
+   @property {function} [onChange] - On-selection callback
+   @property {string} [className] - Additional class of the container `<div>`
+   @property {object} [style] - Additional style of the container `<div>`
+   @description
+   Childrens of the `RadioGroup` shall be [Radio](module-dome_controls_buttons.Radio.html)
+   buttons.
+
+   The selected value of the group is broadcasted to the radio buttons. Their
+   callbacks are activated _before_ the radio group one, if any.
+
+   If the radio group is disabled, all the radio buttons are disabled also and
+   their `disabled` properties are ignored. On the contrary, when the radio
+   group is enabled, the `disabled` property of each radio button is taken into
+   account.
+
+   The radio buttons inside a group are laidout in a vertical box with the additional
+   styling properties.
+*/
+export class RadioGroup extends React.Component {
+
+  constructor(props) {
+    super(props);
+    this.state = { value: props.value };
+    this.onChange = this.onChange.bind(this);
+  }
+
+  componentDidUpdate() {
+    let v = this.props.value ;
+    if (v && this.state.value !== v) this.setState( { value: v } );
+  }
+
+  onChange(value) {
+    this.setState( { value } );
+    this.props.onChange && this.props.onChange( value );
+  }
+
+  render() {
+    const groupdisabled = DISABLED(this.props);
+    const { className='', style } = this.props ;
+    const selection = this.state.value ;
+    const makeRadio = (radio) => {
+      const disabled = groupdisabled || DISABLED(radio.props) ;
+      const { onSelection: onRadioSelect } = radio.props ;
+      const onSelection =
+            onRadioSelect
+            ? (v) => { onRadioSelect(v) ; this.onChange(v); }
+            : this.handleSelect ;
+      return React.cloneElement( radio , { selection , disabled , onSelection } , null );
+    } ;
+    return (
+      <div className={ 'dome-xRadio-group ' + className } style={style}>
+        {React.Children.map( this.props.children , makeRadio )}
+      </div>
+    );
+  }
+
+}
+
+// --------------------------------------------------------------------------
+// --- Selector
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @summary Menu Button.
+   @property {string} [placeholder] - Label of undefined selection
+   @property {string} [title] - Optional tooltip
+   @property {any} [value] - Currently selected value
+   @property {boolean} [enabled] - Defaults to `true`
+   @property {boolean} [disabled] - Defaults to `false`
+   @property {function} [onChange] - On-selection callback
+   @property {string} [className] - Additional class
+   @property {object} [style] - Additional style
+   @property {string} [id] - Select identifier
+   @description
+   Childrens of the `Select` shall be standard `<option>` or `<optgroup>` HTML
+   elements. When the `placeholder` property is set, an additional option is provided
+   and associated with any `undefined` value.
+
+   The selected value of the group is broadcasted to the provided options, and their callbacks
+   linked to the button automatically.
+
+   **Remark:** most non-positionning CSS properties might not work on the `<select>` element due
+   to the native rendering used by Chrome.
+   You might use `-webkit-appearance: none` to cancel this behavior, you will have to restyle the
+   component entirely, which is quite ugly by default.
+*/
+export const Select = (props) => {
+  const disabled = DISABLED(props);
+  const onChange = props.onChange && ((e) => props.onChange(e.target.value));
+  const className = props.className || '' ;
+  return (
+    <select
+      id={props.id}
+      disabled={disabled}
+      className={'dome-xSelect ' + className}
+      style={props.style}
+      title={props.title}
+      value={props.value || ''}
+      onChange={onChange}
+      >
+      {props.placeholder && props.placeholder !=='' && (
+        <option disabled value=''>— {props.placeholder} —</option>
+      )}
+      {props.children}
+    </select>
+  );
+};
+
+// --------------------------------------------------------------------------
+// --- Text Input
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @summary Text Field
+   @property {string} [value] - Field initial content
+   @property {string} [edited] - Field current value
+   @property {boolean} [enabled] - Defaults to `true`
+   @property {boolean} [disabled] - Defaults to `false`
+   @property {boolean} [autoFocus] - Defaults to `false`
+   @property {function} [onChange] - Update callback (on every edit, debounced)
+   @property {function} [onEnter] - Enter callback (on Enter or Return key)
+   @property {string} [title] - Field tooltip text
+   @property {string} [placeholder] - Input field place holder
+   @property {string} [className] - Input field additional class
+   @property {object} [style] - Input field additional style
+   @property {string} [id] - Input field identifier
+   @description
+   Field with a Text Input element. The default latency is set to 600ms.
+*/
+export const Field = (props) => {
+  const [ current, setCurrent ] = React.useState(props.value);
+  const disabled = DISABLED(props);
+  const className = props.className || '' ;
+  const { edited, onChange, onEnter } = props ;
+  const value = edited === undefined ? current : edited ;
+  const ONCHANGE = (evt) => {
+    let text = evt.target.value || '' ;
+    if (edited === undefined) setCurrent(text);
+    onChange && onChange(text);
+  };
+  const ONKEYPRESS = (evt) => {
+    if (evt.key === 'Enter' && onEnter) onEnter(value);
+  };
+  return (
+    <input id={props.id} type='text'
+           autoFocus={!disabled && props.autoFocus}
+           value={value}
+           className={'dome-xField ' + className}
+           style={props.style}
+           disabled={disabled}
+           placeholder={props.placeholder}
+           onKeyPress={ONKEYPRESS}
+           onChange={ONCHANGE} />
+  );
+};
+
+// --------------------------------------------------------------------------
+// --- Exports
+// --------------------------------------------------------------------------
+
+export default {
+  LCD, LED,
+  Button, CircButton, IconButton,
+  Switch, Checkbox, Radio, RadioGroup,
+  Select
+};
+
+// --------------------------------------------------------------------------
diff --git a/ivette/src/dome/src/renderer/controls/icons.css b/ivette/src/dome/src/renderer/controls/icons.css
new file mode 100644
index 0000000000000000000000000000000000000000..066887a39172745fa58aa671f1c6ff496652a440
--- /dev/null
+++ b/ivette/src/dome/src/renderer/controls/icons.css
@@ -0,0 +1,42 @@
+/* -------------------------------------------------------------------------- */
+/* --- Styling Icons                                                      --- */
+/* -------------------------------------------------------------------------- */
+
+.dome-xIcon {
+    flex: 0 0 auto ;
+    display: inline ;
+    margin: 1px ;
+    padding: 1px 0px 0px 0px ;
+}
+
+.dome-xIcon svg {
+    position:relative ;
+}
+
+.dome-xBadge {
+    flex: 0 0 auto ;
+    display: inline ;
+    margin: 1px ;
+    padding: 1px 6px 1px 6px ;
+    height: 16px ;
+    fill: #dedede ;
+    color: #dedede ;
+    font-family: sans-serif ;
+    font-size: smaller ;
+    font-weight: bold ;
+    border-radius: 8px ;
+    background: #777 ;
+}
+
+.dome-window-inactive .dome-xBadge,
+.dome-disabled .dome-xBadge
+{
+    background: #ccc ;
+    color: #eee ;
+    fill: #eee ;
+}
+
+.dome-xBadge label {
+    position: relative ;
+    bottom: -1px ;
+}
diff --git a/ivette/src/dome/src/renderer/controls/icons.js b/ivette/src/dome/src/renderer/controls/icons.js
new file mode 100644
index 0000000000000000000000000000000000000000..808cd0f2ce7db39939a9c8ab7860d121fb09090d
--- /dev/null
+++ b/ivette/src/dome/src/renderer/controls/icons.js
@@ -0,0 +1,156 @@
+// --------------------------------------------------------------------------
+// --- SVG Icons
+// --------------------------------------------------------------------------
+
+/**
+   @module dome/controls/icons
+   @description
+   Consult the [Icon Gallery](gallery-icons.html) for default icons.
+   You can [register](#.register) new icons or override existing ones
+   and [iterate](#.forEach) over the icon base.
+*/
+
+import React from 'react' ;
+import Icons from './icons.json' ;
+import './icons.css' ;
+import _ from 'lodash' ;
+
+// --------------------------------------------------------------------------
+// --- Raw SVG element
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @property {string} id - icon's identifier (mandatory)
+   @property {string} [title] - icon's tool-tip (optional)
+   @property {number} [size] - icon's dimension in pixels (default: `12`)
+   @property {number} [offset] - vertical alignment offset (default: `- size * 0.125`)
+   @summary Raw SVG element.
+   @description
+   Consult the [Icon Gallery](gallery-icons.html) for default icons.
+*/
+export function SVG( { id , title, size, offset } )
+{
+  if (!id) return null;
+  const icon = Icons[id];
+  if (!icon) return id;
+  if (size===undefined) size = 12 ;
+  if (offset===undefined) offset = _.floor( - size * 0.125 );
+  return (
+    <svg height={size}
+         width={size}
+         style={{bottom: offset }}
+         viewBox={icon.viewBox || "0 0 24 24"} >
+      { title && <title>{title}</title> }
+      <path d={icon.path}/>
+    </svg>
+  );
+}
+
+// --------------------------------------------------------------------------
+// --- Icon Component
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @summary Icon Component.
+   @property {string} id - icon's identifier (mandatory)
+   @property {string} [title] - icon's tool-tip (optional)
+   @property {function} [onClick] - callback when icon is clicked (optional)
+   @property {string} [fill] - icon's fill color (optional)
+   @property {number} [size] - icon's dimension in pixels (default `12`)
+   @property {number} [offset] - vertical alignment offset (default `-size * 0.125`)
+   @property {string} [className] - additional class name
+   @property {object} [style] - additional styles
+   @description
+   Consult the [Icon Gallery](gallery-icons.html) for default icons.
+*/
+export function Icon( { id, title, onClick, fill, size, className='', offset, style } )
+{
+  const divClass = 'dome-xIcon ' + className  ;
+  const divStyle = fill ? Object.assign({fill},style) : style ;
+  return (
+    <div className={divClass}
+         style={divStyle}
+         onClick={onClick}>
+      <SVG id={id} size={size} title={title} offset={offset} />
+    </div>
+  );
+}
+
+// --------------------------------------------------------------------------
+// ---  Badge Component
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @summary Rounded icon, number or letter.
+   @property {icon|number|string} [value] - badge content
+   @property {string} [title] - badge tool-tip (optional)
+   @property {function} [onClick] - badge on-click callback (optional)
+   @description
+   Depending on the type of value, display either a number,
+   a label, or the corresponding named icon.
+   Consult the [Icon Gallery](gallery-icons.html) for default icons.
+*/
+export function Badge( { value, title, onClick } )
+{
+  var content ;
+  if (Icons[value]) {
+    content = <Icon id={value} size={10} /> ;
+  } else {
+    const style =
+          ( typeof(value) === 'number' && value < 10 ) ||
+          ( typeof(value) === 'string' && value.length == 1 ) ?
+          { paddingLeft: 2 , paddingRight: 2 } : {} ;
+    content = <label style={style} className='dome-text-label'>{value}</label>;
+  }
+  return (
+    <div className="dome-xBadge"
+         title={title} onClick={onClick}>
+      {content}
+    </div>
+  );
+}
+
+// --------------------------------------------------------------------------
+// --- Icon Database
+// --------------------------------------------------------------------------
+
+/**
+   @summary Register a new icon.
+   @param {Object} icon - Icon's data to register (see below)
+   @description
+   The icon specification is an object with the following properties:
+   - `name`: icon's name
+   - `path`: svg path to draw the icon
+   - `title`: title for the icon (optional)
+   - `section`: section of the Icons (optional)
+   - `viewBox`: svg view-box property (optional, `"0 0 24 24"` by default)
+*/
+
+export function register(icon) {
+  const { name , ...deficon } = icon ;
+  if (!name) console.error(`[Dome] Icon has no name (skipped).`);
+  if (!deficon.path) console.error(`[Dome] Icon '${name}' has no path (skipped).`);
+  Icons[name] = deficon ;
+}
+
+/**
+   @summary Iterate over registered icons.
+   @param {func} job - function applied to each icon
+   @description
+   See [register](#.register) for properties of the icon objects.
+*/
+
+export function forEach(job) {
+  for( var name in Icons ) {
+    job( Object.assign( { name } , Icons[name] ));
+  }
+}
+
+// --------------------------------------------------------------------------
+
+export default { Icon , Badge, SVG, register , forEach } ;
+
+// --------------------------------------------------------------------------
diff --git a/ivette/src/dome/src/renderer/controls/icons.json b/ivette/src/dome/src/renderer/controls/icons.json
new file mode 100644
index 0000000000000000000000000000000000000000..1afd562e8e04d7a5fdfbb1255a5345dbd1e86987
--- /dev/null
+++ b/ivette/src/dome/src/renderer/controls/icons.json
@@ -0,0 +1,319 @@
+{
+  "SEARCH": {
+    "section": "Tools",
+    "title": "Search",
+    "viewBox": "0 0 22 24",
+    "path": "M15.429 11.143q0-2.478-1.761-4.239t-4.239-1.761-4.239 1.761-1.761 4.239 1.761 4.239 4.239 1.761 4.239-1.761 1.761-4.239zM22.286 22.286q0 0.696-0.509 1.205t-1.205 0.509q-0.723 0-1.205-0.509l-4.594-4.58q-2.397 1.661-5.344 1.661-1.915 0-3.663-0.743t-3.013-2.009-2.009-3.013-0.743-3.663 0.743-3.663 2.009-3.013 3.013-2.009 3.663-0.743 3.663 0.743 3.013 2.009 2.009 3.013 0.743 3.663q0 2.946-1.661 5.344l4.594 4.594q0.496 0.496 0.496 1.205z"
+  },
+  "EXECUTE": {
+    "section": "Tools",
+    "viewBox": "0 0 16 16",
+    "path": "M5.683 11.282l0.645-0.903-0.707-0.707-0.903 0.645c-0.168-0.094-0.347-0.168-0.535-0.222l-0.183-1.095h-1l-0.183 1.095c-0.188 0.053-0.368 0.128-0.535 0.222l-0.903-0.645-0.707 0.707 0.645 0.903c-0.094 0.168-0.168 0.347-0.222 0.535l-1.095 0.183v1l1.095 0.183c0.053 0.188 0.128 0.368 0.222 0.535l-0.645 0.903 0.707 0.707 0.903-0.645c0.168 0.094 0.347 0.168 0.535 0.222l0.183 1.095h1l0.183-1.095c0.188-0.053 0.368-0.128 0.535-0.222l0.903 0.645 0.707-0.707-0.645-0.903c0.094-0.168 0.168-0.347 0.222-0.535l1.095-0.182v-1l-1.095-0.183c-0.053-0.188-0.128-0.368-0.222-0.535zM3.5 13.5c-0.552 0-1-0.448-1-1s0.448-1 1-1 1 0.448 1 1-0.448 1-1 1zM16 6v-1l-1.053-0.191c-0.019-0.126-0.044-0.25-0.074-0.372l0.899-0.58-0.383-0.924-1.046 0.226c-0.066-0.108-0.136-0.213-0.211-0.315l0.609-0.88-0.707-0.707-0.88 0.609c-0.102-0.074-0.207-0.145-0.315-0.211l0.226-1.046-0.924-0.383-0.58 0.899c-0.122-0.030-0.246-0.054-0.372-0.074l-0.191-1.053h-1l-0.191 1.053c-0.126 0.019-0.25 0.044-0.372 0.074l-0.58-0.899-0.924 0.383 0.226 1.046c-0.108 0.066-0.213 0.136-0.315 0.211l-0.88-0.609-0.707 0.707 0.609 0.88c-0.074 0.102-0.145 0.207-0.211 0.315l-1.046-0.226-0.383 0.924 0.899 0.58c-0.030 0.122-0.054 0.246-0.074 0.372l-1.053 0.191v1l1.053 0.191c0.019 0.126 0.044 0.25 0.074 0.372l-0.899 0.58 0.383 0.924 1.046-0.226c0.066 0.108 0.136 0.213 0.211 0.315l-0.609 0.88 0.707 0.707 0.88-0.609c0.102 0.074 0.207 0.145 0.315 0.211l-0.226 1.046 0.924 0.383 0.58-0.899c0.122 0.030 0.246 0.054 0.372 0.074l0.191 1.053h1l0.191-1.053c0.126-0.019 0.25-0.044 0.372-0.074l0.58 0.899 0.924-0.383-0.226-1.046c0.108-0.066 0.213-0.136 0.315-0.211l0.88 0.609 0.707-0.707-0.609-0.88c0.074-0.102 0.145-0.207 0.211-0.315l1.046 0.226 0.383-0.924-0.899-0.58c0.030-0.122 0.054-0.246 0.074-0.372l1.053-0.191zM10.5 7.675c-1.201 0-2.175-0.974-2.175-2.175s0.974-2.175 2.175-2.175 2.175 0.974 2.175 2.175c0 1.201-0.974 2.175-2.175 2.175z"
+  },
+  "TUNINGS": {
+    "section": "Tools",
+    "title": "Tunings",
+    "viewBox": "0 0 24 24",
+    "path": "M4.714 18.857v1.714h-4.714v-1.714h4.714zM9.429 17.143q0.348 0 0.603 0.254t0.254 0.603v3.429q0 0.348-0.254 0.603t-0.603 0.254h-3.429q-0.348 0-0.603-0.254t-0.254-0.603v-3.429q0-0.348 0.254-0.603t0.603-0.254h3.429zM11.571 12v1.714h-11.571v-1.714h11.571zM3 5.143v1.714h-3v-1.714h3zM20.571 18.857v1.714h-9.857v-1.714h9.857zM7.714 3.429q0.348 0 0.603 0.254t0.254 0.603v3.429q0 0.348-0.254 0.603t-0.603 0.254h-3.429q-0.348 0-0.603-0.254t-0.254-0.603v-3.429q0-0.348 0.254-0.603t0.603-0.254h3.429zM16.286 10.286q0.348 0 0.603 0.254t0.254 0.603v3.429q0 0.348-0.254 0.603t-0.603 0.254h-3.429q-0.348 0-0.603-0.254t-0.254-0.603v-3.429q0-0.348 0.254-0.603t0.603-0.254h3.429zM20.571 12v1.714h-3v-1.714h3zM20.571 5.143v1.714h-11.571v-1.714h11.571z"
+  },
+  "SETTINGS": {
+    "section": "Tools",
+    "title": "Configure",
+    "viewBox": "0 0 24 24",
+    "path": "M15.428 12q0-1.42-1.004-2.424t-2.424-1.004-2.424 1.004-1.004 2.424 1.004 2.424 2.424 1.004 2.424-1.004 1.004-2.424zM22.285 10.54v2.973q0 0.161-0.107 0.308t-0.268 0.174l-2.478 0.375q-0.254 0.723-0.522 1.219 0.469 0.67 1.433 1.848 0.134 0.161 0.134 0.335t-0.121 0.308q-0.362 0.496-1.326 1.446t-1.259 0.951q-0.161 0-0.348-0.121l-1.848-1.446q-0.589 0.308-1.219 0.509-0.214 1.821-0.388 2.491-0.094 0.375-0.482 0.375h-2.973q-0.188 0-0.328-0.114t-0.154-0.288l-0.375-2.464q-0.656-0.214-1.205-0.496l-1.888 1.433q-0.134 0.121-0.335 0.121-0.188 0-0.335-0.147-1.688-1.527-2.21-2.25-0.094-0.134-0.094-0.308 0-0.161 0.107-0.308 0.201-0.281 0.683-0.891t0.723-0.944q-0.362-0.67-0.549-1.326l-2.451-0.362q-0.174-0.027-0.281-0.167t-0.107-0.315v-2.973q0-0.161 0.107-0.308t0.254-0.174l2.491-0.375q0.188-0.616 0.522-1.232-0.536-0.763-1.433-1.848-0.134-0.161-0.134-0.321 0-0.134 0.121-0.308 0.348-0.482 1.319-1.44t1.266-0.958q0.174 0 0.348 0.134l1.848 1.433q0.589-0.308 1.219-0.509 0.214-1.821 0.388-2.491 0.094-0.375 0.482-0.375h2.973q0.188 0 0.328 0.114t0.154 0.288l0.375 2.464q0.656 0.214 1.205 0.496l1.902-1.433q0.121-0.121 0.321-0.121 0.174 0 0.335 0.134 1.728 1.594 2.21 2.277 0.094 0.107 0.094 0.295 0 0.161-0.107 0.308-0.201 0.281-0.683 0.891t-0.723 0.944q0.348 0.67 0.549 1.313l2.451 0.375q0.174 0.027 0.281 0.167t0.107 0.315z"
+  },
+  "PRINT": {
+    "section": "Tools",
+    "title": "Print",
+    "viewBox": "0 0 22 24",
+    "path": "M5.143 20.571h12v-3.429h-12v3.429zM5.143 12h12v-5.143h-2.143q-0.536 0-0.911-0.375t-0.375-0.911v-2.143h-8.571v8.571zM20.571 12.857q0-0.348-0.254-0.603t-0.603-0.254-0.603 0.254-0.254 0.603 0.254 0.603 0.603 0.254 0.603-0.254 0.254-0.603zM22.286 12.857v5.571q0 0.174-0.127 0.301t-0.301 0.127h-3v2.143q0 0.536-0.375 0.911t-0.911 0.375h-12.857q-0.536 0-0.911-0.375t-0.375-0.911v-2.143h-3q-0.174 0-0.301-0.127t-0.127-0.301v-5.571q0-1.058 0.757-1.815t1.815-0.757h0.857v-7.286q0-0.536 0.375-0.911t0.911-0.375h9q0.536 0 1.179 0.268t1.018 0.643l2.036 2.036q0.375 0.375 0.643 1.018t0.268 1.179v3.429h0.857q1.058 0 1.815 0.757t0.757 1.815z"
+  },
+  "RELOAD": {
+    "section": "Tools",
+    "title": "Reload",
+    "viewBox": "0 0 16 16",
+    "path": "M13.901 2.599c-1.463-1.597-3.565-2.599-5.901-2.599-4.418 0-8 3.582-8 8h1.5c0-3.59 2.91-6.5 6.5-6.5 1.922 0 3.649 0.835 4.839 2.161l-2.339 2.339h5.5v-5.5l-2.099 2.099z M14.5 8c0 3.59-2.91 6.5-6.5 6.5-1.922 0-3.649-0.835-4.839-2.161l2.339-2.339h-5.5v5.5l2.099-2.099c1.463 1.597 3.565 2.599 5.901 2.599 4.418 0 8-3.582 8-8h-1.5z"
+  },
+  "SAVE": {
+    "section": "Tools",
+    "title": "Save",
+    "viewBox": "0 0 16 16",
+    "path": "M14 0h-14v16h16v-14l-2-2zM8 2h2v4h-2v-4zM14 14h-12v-12h1v5h9v-5h1.172l0.828 0.828v11.172z"
+  },
+  "DUPLICATE": {
+    "section": "Tools",
+    "title": "Duplicate",
+    "viewBox": "0 0 16 16",
+    "path": "M10 4v-4h-7l-3 3v9h6v4h10v-12h-6zM3 1.414v1.586h-1.586l1.586-1.586zM1 11v-7h3v-3h5v3l-3 3v4h-5zM9 5.414v1.586h-1.586l1.586-1.586zM15 15h-8v-7h3v-3h5v10z"
+  },
+  "LOCK": {
+    "section": "Tools",
+    "title":"Lock",
+    "viewBox":"0 0 16 16",
+    "path": "M12.249 6.5h-0.25v-3c0-1.654-1.346-3-3-3h-2c-1.654 0-3 1.346-3 3v3h-0.25c-0.413 0-0.75 0.338-0.75 0.75v7.5c0 0.412 0.337 0.75 0.75 0.75h8.5c0.412 0 0.75-0.338 0.75-0.75v-7.5c0-0.412-0.338-0.75-0.75-0.75zM5.999 3.5c0-0.551 0.449-1 1-1h2c0.551 0 1 0.449 1 1v3h-4v-3z"
+  },
+  "EDIT": {
+    "section": "Tools",
+    "title":"Edit",
+    "viewBox":"0 0 16 16",
+    "path": "M13.5 0c1.381 0 2.5 1.119 2.5 2.5 0 0.563-0.186 1.082-0.5 1.5l-1 1-3.5-3.5 1-1c0.418-0.314 0.937-0.5 1.5-0.5zM1 11.5l-1 4.5 4.5-1 9.25-9.25-3.5-3.5-9.25 9.25zM11.181 5.681l-7 7-0.862-0.862 7-7 0.862 0.862z"
+  },
+  "CHECK": {
+    "section": "Signs",
+    "title": "Check",
+    "viewBox": "0 0 24 24",
+    "path": "M22.379 7.58q0 0.536-0.375 0.911l-11.518 11.518q-0.375 0.375-0.911 0.375t-0.911-0.375l-6.67-6.67q-0.375-0.375-0.375-0.911t0.375-0.911l1.821-1.821q0.375-0.375 0.911-0.375t0.911 0.375l3.938 3.951 8.786-8.799q0.375-0.375 0.911-0.375t0.911 0.375l1.821 1.821q0.375 0.375 0.375 0.911z"
+  },
+  "CROSS": {
+    "section": "Signs",
+    "title": "Cross",
+    "viewBox": "0 0 19 24",
+    "path": "M17.384 17.705q0 0.536-0.375 0.911l-1.821 1.821q-0.375 0.375-0.911 0.375t-0.911-0.375l-3.938-3.938-3.938 3.938q-0.375 0.375-0.911 0.375t-0.911-0.375l-1.821-1.821q-0.375-0.375-0.375-0.911t0.375-0.911l3.938-3.938-3.938-3.938q-0.375-0.375-0.375-0.911t0.375-0.911l1.821-1.821q0.375-0.375 0.911-0.375t0.911 0.375l3.938 3.938 3.938-3.938q0.375-0.375 0.911-0.375t0.911 0.375l1.821 1.821q0.375 0.375 0.375 0.911t-0.375 0.911l-3.938 3.938 3.938 3.938q0.375 0.375 0.375 0.911z"
+  },
+  "WARNING": {
+    "section": "Signs",
+    "title": "Warning",
+    "viewBox": "0 0 24 24",
+    "path": "M13.714 18.415v-2.545q0-0.188-0.127-0.315t-0.301-0.127h-2.571q-0.174 0-0.301 0.127t-0.127 0.315v2.545q0 0.188 0.127 0.315t0.301 0.127h2.571q0.174 0 0.301-0.127t0.127-0.315zM13.688 13.406l0.241-6.147q0-0.161-0.134-0.254-0.174-0.147-0.321-0.147h-2.946q-0.147 0-0.321 0.147-0.134 0.094-0.134 0.281l0.228 6.121q0 0.134 0.134 0.221t0.321 0.087h2.478q0.188 0 0.315-0.087t0.141-0.221zM13.5 0.897l10.286 18.857q0.469 0.844-0.027 1.688-0.228 0.388-0.623 0.616t-0.85 0.228h-20.571q-0.455 0-0.85-0.228t-0.623-0.616q-0.496-0.844-0.027-1.688l10.286-18.857q0.228-0.415 0.629-0.656t0.871-0.241 0.871 0.241 0.629 0.656z"
+  },
+  "INFO": {
+    "section": "Signs",
+    "title": "Information",
+    "viewBox": "0 0 6 16",
+    "path": "M5.717 12.571v1.143q0 0.232-0.17 0.402t-0.402 0.17h-4.571q-0.232 0-0.402-0.17t-0.17-0.402v-1.143q0-0.232 0.17-0.402t0.402-0.17h0.571v-3.429h-0.571q-0.232 0-0.402-0.17t-0.17-0.402v-1.143q0-0.232 0.17-0.402t0.402-0.17h3.429q0.232 0 0.402 0.17t0.17 0.402v5.143h0.571q0.232 0 0.402 0.17t0.17 0.402zM4.574 2.286v1.714q0 0.232-0.17 0.402t-0.402 0.17h-2.286q-0.232 0-0.402-0.17t-0.17-0.402v-1.714q0-0.232 0.17-0.402t0.402-0.17h2.286q0.232 0 0.402 0.17t0.17 0.402z"
+  },
+  "HELP": {
+    "section": "Signs",
+    "title": "Help",
+    "viewBox": "0 0 9 16",
+    "path": "M5.874 11.214v2.143q0 0.143-0.107 0.25t-0.25 0.107h-2.143q-0.143 0-0.25-0.107t-0.107-0.25v-2.143q0-0.143 0.107-0.25t0.25-0.107h2.143q0.143 0 0.25 0.107t0.107 0.25zM8.696 5.857q0 0.482-0.138 0.902t-0.313 0.683-0.491 0.531-0.513 0.388-0.545 0.317q-0.366 0.205-0.612 0.58t-0.246 0.598q0 0.152-0.107 0.29t-0.25 0.138h-2.143q-0.134 0-0.228-0.165t-0.094-0.335v-0.402q0-0.741 0.58-1.397t1.277-0.969q0.527-0.241 0.75-0.5t0.223-0.679q0-0.375-0.415-0.661t-0.96-0.286q-0.58 0-0.964 0.259-0.313 0.223-0.955 1.027-0.116 0.143-0.277 0.143-0.107 0-0.223-0.071l-1.464-1.116q-0.116-0.089-0.138-0.223t0.049-0.25q1.429-2.375 4.143-2.375 0.714 0 1.438 0.277t1.304 0.741 0.946 1.138 0.366 1.415z"
+  },
+  "ATTENTION": {
+    "section": "Signs",
+    "viewBox": "0 0 6 16",
+    "path": "M4.574 11.714v2q0 0.232-0.17 0.402t-0.402 0.17h-2.286q-0.232 0-0.402-0.17t-0.17-0.402v-2q0-0.232 0.17-0.402t0.402-0.17h2.286q0.232 0 0.402 0.17t0.17 0.402zM4.841 2.286l-0.25 6.857q-0.009 0.232-0.183 0.402t-0.406 0.17h-2.286q-0.232 0-0.406-0.17t-0.183-0.402l-0.25-6.857q-0.009-0.232 0.156-0.402t0.397-0.17h2.857q0.232 0 0.397 0.17t0.156 0.402z"
+  },
+  "PLUS": {
+    "section": "Signs",
+    "viewBox": "0 0 13 16",
+    "path": "M12.575 7.143v1.714q0 0.357-0.25 0.607t-0.607 0.25h-3.714v3.714q0 0.357-0.25 0.607t-0.607 0.25h-1.714q-0.357 0-0.607-0.25t-0.25-0.607v-3.714h-3.714q-0.357 0-0.607-0.25t-0.25-0.607v-1.714q0-0.357 0.25-0.607t0.607-0.25h3.714v-3.714q0-0.357 0.25-0.607t0.607-0.25h1.714q0.357 0 0.607 0.25t0.25 0.607v3.714h3.714q0.357 0 0.607 0.25t0.25 0.607z"
+  },
+  "MINUS": {
+    "section": "Signs",
+    "viewBox": "0 0 13 16",
+    "path": "M12.575 7.143v1.714q0 0.357-0.25 0.607t-0.607 0.25h-10.857q-0.357 0-0.607-0.25t-0.25-0.607v-1.714q0-0.357 0.25-0.607t0.607-0.25h10.857q0.357 0 0.607 0.25t0.25 0.607z"
+  },
+  "TRASH": {
+    "section": "Desktop",
+    "title": "Trash",
+    "viewBox": "0 0 19 24",
+    "path": "M6.857 9.857v7.714q0 0.188-0.121 0.308t-0.308 0.121h-0.857q-0.188 0-0.308-0.121t-0.121-0.308v-7.714q0-0.188 0.121-0.308t0.308-0.121h0.857q0.188 0 0.308 0.121t0.121 0.308zM10.286 9.857v7.714q0 0.188-0.121 0.308t-0.308 0.121h-0.857q-0.188 0-0.308-0.121t-0.121-0.308v-7.714q0-0.188 0.121-0.308t0.308-0.121h0.857q0.188 0 0.308 0.121t0.121 0.308zM13.714 9.857v7.714q0 0.188-0.121 0.308t-0.308 0.121h-0.857q-0.188 0-0.308-0.121t-0.121-0.308v-7.714q0-0.188 0.121-0.308t0.308-0.121h0.857q0.188 0 0.308 0.121t0.121 0.308zM15.429 19.554v-12.696h-12v12.696q0 0.295 0.094 0.542t0.194 0.362 0.141 0.114h11.143q0.040 0 0.141-0.114t0.194-0.362 0.094-0.542zM6.429 5.143h6l-0.643-1.567q-0.094-0.121-0.228-0.147h-4.246q-0.134 0.027-0.228 0.147zM18.857 5.571v0.857q0 0.188-0.121 0.308t-0.308 0.121h-1.286v12.696q0 1.112-0.629 1.922t-1.513 0.81h-11.143q-0.884 0-1.513-0.783t-0.629-1.895v-12.75h-1.286q-0.188 0-0.308-0.121t-0.121-0.308v-0.857q0-0.188 0.121-0.308t0.308-0.121h4.138l0.938-2.237q0.201-0.496 0.723-0.844t1.058-0.348h4.286q0.536 0 1.058 0.348t0.723 0.844l0.938 2.237h4.138q0.188 0 0.308 0.121t0.121 0.308z"
+  },
+  "FOLDER": {
+    "section": "Desktop",
+    "title": "Folder",
+    "viewBox": "0 0 22 24",
+    "path": "M20.573 18.428v-9.429q0-0.536-0.375-0.911t-0.911-0.375h-9.429q-0.536 0-0.911-0.375t-0.375-0.911v-0.857q0-0.536-0.375-0.911t-0.911-0.375h-4.286q-0.536 0-0.911 0.375t-0.375 0.911v12.857q0 0.536 0.375 0.911t0.911 0.375h16.286q0.536 0 0.911-0.375t0.375-0.911zM22.287 9v9.429q0 1.232-0.884 2.116t-2.116 0.884h-16.286q-1.232 0-2.116-0.884t-0.884-2.116v-12.857q0-1.232 0.884-2.116t2.116-0.884h4.286q1.232 0 2.116 0.884t0.884 2.116v0.429h9q1.232 0 2.116 0.884t0.884 2.116z"
+  },
+  "FOLDER.OPEN": {
+    "section": "Desktop",
+    "title": "Folder (open)",
+    "viewBox": "0 0 26 24",
+    "path": "M23.853 12.469q0-0.469-0.71-0.469h-14.571q-0.536 0-1.145 0.288t-0.958 0.703l-3.938 4.862q-0.241 0.321-0.241 0.536 0 0.469 0.71 0.469h14.571q0.536 0 1.152-0.295t0.951-0.71l3.938-4.862q0.241-0.295 0.241-0.522zM8.571 10.286h10.286v-2.143q0-0.536-0.375-0.911t-0.911-0.375h-7.714q-0.536 0-0.911-0.375t-0.375-0.911v-0.857q0-0.536-0.375-0.911t-0.911-0.375h-4.286q-0.536 0-0.911 0.375t-0.375 0.911v11.424l3.429-4.219q0.589-0.71 1.554-1.172t1.875-0.462zM25.567 12.469q0 0.83-0.616 1.607l-3.951 4.862q-0.576 0.71-1.554 1.172t-1.875 0.462h-14.571q-1.232 0-2.116-0.884t-0.884-2.116v-12.857q0-1.232 0.884-2.116t2.116-0.884h4.286q1.232 0 2.116 0.884t0.884 2.116v0.429h7.286q1.232 0 2.116 0.884t0.884 2.116v2.143h2.571q0.723 0 1.326 0.328t0.897 0.944q0.201 0.429 0.201 0.911z"
+  },
+  "DISPLAY": {
+    "section": "Desktop",
+    "viewBox": "0 0 24 24",
+    "title": "Display",
+    "path": "M21 15.984v-12h-18v12h18zM21 2.016q0.797 0 1.406 0.586t0.609 1.383v12q0 0.797-0.609 1.406t-1.406 0.609h-6.984v2.016h1.969v1.969h-7.969v-1.969h1.969v-2.016h-6.984q-0.797 0-1.406-0.609t-0.609-1.406v-12q0-0.797 0.609-1.383t1.406-0.586h18z"
+  },
+  "ITEMS.GRID": {
+    "section": "Desktop",
+    "viewBox": "0 0 16 16",
+    "title": "Items (grid)",
+    "path": "M4.571 11.143v1.714q0 0.357-0.25 0.607t-0.607 0.25h-2.857q-0.357 0-0.607-0.25t-0.25-0.607v-1.714q0-0.357 0.25-0.607t0.607-0.25h2.857q0.357 0 0.607 0.25t0.25 0.607zM4.571 6.571v1.714q0 0.357-0.25 0.607t-0.607 0.25h-2.857q-0.357 0-0.607-0.25t-0.25-0.607v-1.714q0-0.357 0.25-0.607t0.607-0.25h2.857q0.357 0 0.607 0.25t0.25 0.607zM10.286 11.143v1.714q0 0.357-0.25 0.607t-0.607 0.25h-2.857q-0.357 0-0.607-0.25t-0.25-0.607v-1.714q0-0.357 0.25-0.607t0.607-0.25h2.857q0.357 0 0.607 0.25t0.25 0.607zM4.571 2v1.714q0 0.357-0.25 0.607t-0.607 0.25h-2.857q-0.357 0-0.607-0.25t-0.25-0.607v-1.714q0-0.357 0.25-0.607t0.607-0.25h2.857q0.357 0 0.607 0.25t0.25 0.607zM10.286 6.571v1.714q0 0.357-0.25 0.607t-0.607 0.25h-2.857q-0.357 0-0.607-0.25t-0.25-0.607v-1.714q0-0.357 0.25-0.607t0.607-0.25h2.857q0.357 0 0.607 0.25t0.25 0.607zM16 11.143v1.714q0 0.357-0.25 0.607t-0.607 0.25h-2.857q-0.357 0-0.607-0.25t-0.25-0.607v-1.714q0-0.357 0.25-0.607t0.607-0.25h2.857q0.357 0 0.607 0.25t0.25 0.607zM10.286 2v1.714q0 0.357-0.25 0.607t-0.607 0.25h-2.857q-0.357 0-0.607-0.25t-0.25-0.607v-1.714q0-0.357 0.25-0.607t0.607-0.25h2.857q0.357 0 0.607 0.25t0.25 0.607zM16 6.571v1.714q0 0.357-0.25 0.607t-0.607 0.25h-2.857q-0.357 0-0.607-0.25t-0.25-0.607v-1.714q0-0.357 0.25-0.607t0.607-0.25h2.857q0.357 0 0.607 0.25t0.25 0.607zM16 2v1.714q0 0.357-0.25 0.607t-0.607 0.25h-2.857q-0.357 0-0.607-0.25t-0.25-0.607v-1.714q0-0.357 0.25-0.607t0.607-0.25h2.857q0.357 0 0.607 0.25t0.25 0.607z"
+  },
+  "ITEMS.LIST": {
+    "section": "Desktop",
+    "viewBox": "0 0 16 16",
+    "title": "Items (list)",
+    "path": "M4.571 11.143v1.714q0 0.357-0.25 0.607t-0.607 0.25h-2.857q-0.357 0-0.607-0.25t-0.25-0.607v-1.714q0-0.357 0.25-0.607t0.607-0.25h2.857q0.357 0 0.607 0.25t0.25 0.607zM4.571 6.571v1.714q0 0.357-0.25 0.607t-0.607 0.25h-2.857q-0.357 0-0.607-0.25t-0.25-0.607v-1.714q0-0.357 0.25-0.607t0.607-0.25h2.857q0.357 0 0.607 0.25t0.25 0.607zM16 11.143v1.714q0 0.357-0.25 0.607t-0.607 0.25h-8.571q-0.357 0-0.607-0.25t-0.25-0.607v-1.714q0-0.357 0.25-0.607t0.607-0.25h8.571q0.357 0 0.607 0.25t0.25 0.607zM4.571 2v1.714q0 0.357-0.25 0.607t-0.607 0.25h-2.857q-0.357 0-0.607-0.25t-0.25-0.607v-1.714q0-0.357 0.25-0.607t0.607-0.25h2.857q0.357 0 0.607 0.25t0.25 0.607zM16 6.571v1.714q0 0.357-0.25 0.607t-0.607 0.25h-8.571q-0.357 0-0.607-0.25t-0.25-0.607v-1.714q0-0.357 0.25-0.607t0.607-0.25h8.571q0.357 0 0.607 0.25t0.25 0.607zM16 2v1.714q0 0.357-0.25 0.607t-0.607 0.25h-8.571q-0.357 0-0.607-0.25t-0.25-0.607v-1.714q0-0.357 0.25-0.607t0.607-0.25h8.571q0.357 0 0.607 0.25t0.25 0.607z"
+  },
+  "HOME": {
+    "section": "Desktop",
+    "title": "Home",
+    "viewBox": "0 0 22 24",
+    "path": "M18.857 13.286v6.429q0 0.348-0.254 0.603t-0.603 0.254h-5.143v-5.143h-3.429v5.143h-5.143q-0.348 0-0.603-0.254t-0.254-0.603v-6.429q0-0.013 0.007-0.040t0.007-0.040l7.701-6.348 7.701 6.348q0.013 0.027 0.013 0.080zM21.844 12.362l-0.83 0.991q-0.107 0.121-0.281 0.147h-0.040q-0.174 0-0.281-0.094l-9.268-7.728-9.268 7.728q-0.161 0.107-0.321 0.094-0.174-0.027-0.281-0.147l-0.83-0.991q-0.107-0.134-0.094-0.315t0.147-0.288l9.629-8.022q0.429-0.348 1.018-0.348t1.018 0.348l3.268 2.732v-2.612q0-0.188 0.121-0.308t0.308-0.121h2.571q0.188 0 0.308 0.121t0.121 0.308v5.464l2.933 2.438q0.134 0.107 0.147 0.288t-0.094 0.315z"
+  },
+  "SIDEBAR": {
+    "section": "Desktop",
+    "title": "Sidebar",
+    "viewBox": "0 0 20 20",
+    "path": "M7 3h-5v14h5v-14zM9 3v14h9v-14h-9zM0 3c0-1.1 0.9-2 2-2h16c1.105 0 2 0.895 2 2v0 14c0 1.105-0.895 2-2 2v0h-16c-1.105 0-2-0.895-2-2v0-14zM3 4h3v2h-3v-2zM3 7h3v2h-3v-2zM3 10h3v2h-3v-2z"
+  },
+  "FILE": {
+    "section": "Desktop",
+    "title": "File",
+    "viewBox": "0 0 24 24",
+    "path": "M19.661 5.089q0.375 0.375 0.643 1.018t0.268 1.179v15.429q0 0.536-0.375 0.911t-0.911 0.375h-18q-0.536 0-0.911-0.375t-0.375-0.911v-21.429q0-0.536 0.375-0.911t0.911-0.375h12q0.536 0 1.179 0.268t1.018 0.643zM13.714 1.821v5.036h5.036q-0.134-0.388-0.295-0.549l-4.192-4.192q-0.161-0.161-0.549-0.295zM18.857 22.286v-13.714h-5.571q-0.536 0-0.911-0.375t-0.375-0.911v-5.571h-10.286v20.571h17.143z"
+  },
+  "CIRC.PLUS": {
+    "section": "Buttons",
+    "title": "Add",
+    "viewBox": "0 0 24 24",
+    "path": "M17.999 12.857v-1.714q0-0.348-0.254-0.603t-0.603-0.254h-3.429v-3.429q0-0.348-0.254-0.603t-0.603-0.254h-1.714q-0.348 0-0.603 0.254t-0.254 0.603v3.429h-3.429q-0.348 0-0.603 0.254t-0.254 0.603v1.714q0 0.348 0.254 0.603t0.603 0.254h3.429v3.429q0 0.348 0.254 0.603t0.603 0.254h1.714q0.348 0 0.603-0.254t0.254-0.603v-3.429h3.429q0.348 0 0.603-0.254t0.254-0.603zM22.285 12q0 2.799-1.379 5.163t-3.743 3.743-5.163 1.379-5.163-1.379-3.743-3.743-1.379-5.163 1.379-5.163 3.743-3.743 5.163-1.379 5.163 1.379 3.743 3.743 1.379 5.163z"
+  },
+  "CIRC.MINUS": {
+    "section": "Buttons",
+    "title": "Remove",
+    "viewBox": "0 0 24 24",
+    "path": "M17.999 12.857v-1.714q0-0.348-0.254-0.603t-0.603-0.254h-10.286q-0.348 0-0.603 0.254t-0.254 0.603v1.714q0 0.348 0.254 0.603t0.603 0.254h10.286q0.348 0 0.603-0.254t0.254-0.603zM22.285 12q0 2.799-1.379 5.163t-3.743 3.743-5.163 1.379-5.163-1.379-3.743-3.743-1.379-5.163 1.379-5.163 3.743-3.743 5.163-1.379 5.163 1.379 3.743 3.743 1.379 5.163z"
+  },
+  "CIRC.CLOSE": {
+    "section": "Buttons",
+    "title": "Close",
+    "viewBox": "0 0 24 24",
+    "path": "M8.973 17.102q0.348 0 0.603-0.254l2.424-2.424 2.424 2.424q0.254 0.254 0.603 0.254 0.362 0 0.616-0.254l1.205-1.205q0.254-0.254 0.254-0.616 0-0.348-0.254-0.603l-2.424-2.424 2.424-2.424q0.254-0.254 0.254-0.603 0-0.362-0.254-0.616l-1.205-1.205q-0.254-0.254-0.616-0.254-0.348 0-0.603 0.254l-2.424 2.424-2.424-2.424q-0.254-0.254-0.603-0.254-0.362 0-0.616 0.254l-1.205 1.205q-0.254 0.254-0.254 0.616 0 0.348 0.254 0.603l2.424 2.424-2.424 2.424q-0.254 0.254-0.254 0.603 0 0.362 0.254 0.616l1.205 1.205q0.254 0.254 0.616 0.254zM12 22.285q-2.799 0-5.163-1.379t-3.743-3.743-1.379-5.163 1.379-5.163 3.743-3.743 5.163-1.379 5.163 1.379 3.743 3.743 1.379 5.163-1.379 5.163-3.743 3.743-5.163 1.379z"
+  },
+  "CIRC.CHECK": {
+    "section": "Buttons",
+    "title": "Check",
+    "viewBox": "0 0 24 24",
+    "path": "M18.91 9.83q0-0.375-0.241-0.616l-1.219-1.205q-0.254-0.254-0.603-0.254t-0.603 0.254l-5.464 5.451-3.027-3.027q-0.254-0.254-0.603-0.254t-0.603 0.254l-1.219 1.205q-0.241 0.241-0.241 0.616 0 0.362 0.241 0.603l4.848 4.848q0.254 0.254 0.603 0.254 0.362 0 0.616-0.254l7.272-7.272q0.241-0.241 0.241-0.603zM22.285 12q0 2.799-1.379 5.163t-3.743 3.743-5.163 1.379-5.163-1.379-3.743-3.743-1.379-5.163 1.379-5.163 3.743-3.743 5.163-1.379 5.163 1.379 3.743 3.743 1.379 5.163z"
+  },
+  "CIRC.QUESTION": {
+    "section": "Buttons",
+    "title": "Check",
+    "viewBox": "0 0 24 24",
+    "path": "M13.714 18.429v-2.571q0-0.188-0.121-0.308t-0.308-0.121h-2.571q-0.188 0-0.308 0.121t-0.121 0.308v2.571q0 0.188 0.121 0.308t0.308 0.121h2.571q0.188 0 0.308-0.121t0.121-0.308zM17.142 9.429q0-1.179-0.743-2.183t-1.855-1.554-2.277-0.549q-3.254 0-4.969 2.853-0.201 0.321 0.107 0.563l1.768 1.339q0.094 0.080 0.254 0.080 0.214 0 0.335-0.161 0.71-0.911 1.152-1.232 0.455-0.321 1.152-0.321 0.643 0 1.145 0.348t0.502 0.79q0 0.509-0.268 0.817t-0.911 0.603q-0.844 0.375-1.547 1.158t-0.703 1.681v0.482q0 0.188 0.121 0.308t0.308 0.121h2.571q0.188 0 0.308-0.121t0.121-0.308q0-0.254 0.288-0.663t0.73-0.663q0.429-0.241 0.656-0.382t0.616-0.469 0.596-0.643 0.375-0.81 0.167-1.085zM22.285 12q0 2.799-1.379 5.163t-3.743 3.743-5.163 1.379-5.163-1.379-3.743-3.743-1.379-5.163 1.379-5.163 3.743-3.743 5.163-1.379 5.163 1.379 3.743 3.743 1.379 5.163z"
+  },
+  "CIRC.INFO": {
+    "section": "Buttons",
+    "title": "Info",
+    "viewBox": "0 0 24 24",
+    "path": "M15.428 18.429v-2.143q0-0.188-0.121-0.308t-0.308-0.121h-1.286v-6.857q0-0.188-0.121-0.308t-0.308-0.121h-4.286q-0.188 0-0.308 0.121t-0.121 0.308v2.143q0 0.188 0.121 0.308t0.308 0.121h1.286v4.286h-1.286q-0.188 0-0.308 0.121t-0.121 0.308v2.143q0 0.188 0.121 0.308t0.308 0.121h6q0.188 0 0.308-0.121t0.121-0.308zM13.714 6.429v-2.143q0-0.188-0.121-0.308t-0.308-0.121h-2.571q-0.188 0-0.308 0.121t-0.121 0.308v2.143q0 0.188 0.121 0.308t0.308 0.121h2.571q0.188 0 0.308-0.121t0.121-0.308zM22.285 12q0 2.799-1.379 5.163t-3.743 3.743-5.163 1.379-5.163-1.379-3.743-3.743-1.379-5.163 1.379-5.163 3.743-3.743 5.163-1.379 5.163 1.379 3.743 3.743 1.379 5.163z"
+  },
+  "SWITCH.OFF": {
+    "section": "Buttons",
+    "title": "Off",
+    "viewBox": "0 0 32 28",
+    "path": "M18 14c0-4.406-3.594-8-8-8s-8 3.594-8 8 3.594 8 8 8 8-3.594 8-8zM30 14c0-4.406-3.594-8-8-8h-6.031c2.438 1.828 4.031 4.734 4.031 8s-1.594 6.172-4.031 8h6.031c4.406 0 8-3.594 8-8zM32 14c0 5.516-4.484 10-10 10h-12c-5.516 0-10-4.484-10-10s4.484-10 10-10h12c5.516 0 10 4.484 10 10z"
+  },
+  "SWITCH.ON": {
+    "section": "Buttons",
+    "title": "On",
+    "viewBox": "0 0 32 28",
+    "path": "M0 14c0-5.516 4.484-10 10-10h12c5.516 0 10 4.484 10 10s-4.484 10-10 10h-12c-5.516 0-10-4.484-10-10zM22 22c4.406 0 8-3.594 8-8s-3.594-8-8-8-8 3.594-8 8 3.594 8 8 8z"
+  },
+  "TRIANGLE.LEFT": {
+    "section": "Arrows",
+    "title": "Left",
+    "viewBox": "0 0 16 16",
+    "path": "M13 2l-10 6 10 6z"
+  },
+  "TRIANGLE.UP": {
+    "section": "Arrows",
+    "title": "Up",
+    "viewBox": "0 0 16 16",
+    "path": "M2 13l6-10 6 10z"
+  },
+  "TRIANGLE.RIGHT": {
+    "section": "Arrows",
+    "title": "Right",
+    "viewBox": "0 0 16 16",
+    "path": "M3 2l10 6-10 6z"
+  },
+  "TRIANGLE.DOWN": {
+    "section": "Arrows",
+    "title": "Down",
+    "viewBox": "0 0 16 16",
+    "path": "M14 3l-6 10-6-10z"
+  },
+  "ANGLE.LEFT": {
+    "section": "Arrows",
+    "title": "Left",
+    "viewBox": "0 0 11 16",
+    "path": "M10.166 3.259l-4.741 4.741 4.741 4.741q0.17 0.17 0.17 0.402t-0.17 0.402l-1.482 1.482q-0.17 0.17-0.402 0.17t-0.402-0.17l-6.625-6.625q-0.17-0.17-0.17-0.402t0.17-0.402l6.625-6.625q0.17-0.17 0.402-0.17t0.402 0.17l1.482 1.482q0.17 0.17 0.17 0.402t-0.17 0.402z"
+  },
+  "ANGLE.RIGHT": {
+    "section": "Arrows",
+    "title": "Right",
+    "viewBox": "0 0 11 16",
+    "path": "M10.166 8.402l-6.625 6.625q-0.17 0.17-0.402 0.17t-0.402-0.17l-1.482-1.482q-0.17-0.17-0.17-0.402t0.17-0.402l4.741-4.741-4.741-4.741q-0.17-0.17-0.17-0.402t0.17-0.402l1.482-1.482q0.17-0.17 0.402-0.17t0.402 0.17l6.625 6.625q0.17 0.17 0.17 0.402t-0.17 0.402z"
+  },
+  "ANGLE.UP": {
+    "section": "Arrows",
+    "title": "Up",
+    "viewBox": "0 0 16 16",
+    "path": "M15.027 10.978l-1.482 1.473q-0.17 0.17-0.402 0.17t-0.402-0.17l-4.741-4.741-4.741 4.741q-0.17 0.17-0.402 0.17t-0.402-0.17l-1.482-1.473q-0.17-0.17-0.17-0.406t0.17-0.406l6.625-6.616q0.17-0.17 0.402-0.17t0.402 0.17l6.625 6.616q0.17 0.17 0.17 0.406t-0.17 0.406z"
+  },
+  "ANGLE.DOWN": {
+    "section": "Arrows",
+    "title": "Down",
+    "viewBox": "0 0 16 16",
+    "path": "M15.027 5.835l-6.625 6.616q-0.17 0.17-0.402 0.17t-0.402-0.17l-6.625-6.616q-0.17-0.17-0.17-0.406t0.17-0.406l1.482-1.473q0.17-0.17 0.402-0.17t0.402 0.17l4.741 4.741 4.741-4.741q0.17-0.17 0.402-0.17t0.402 0.17l1.482 1.473q0.17 0.17 0.17 0.406t-0.17 0.406z"
+  },
+  "DIR.VERTICAL": {
+    "section": "Arrows",
+    "title": "Vertical",
+    "viewBox": "0 0 7 16",
+    "path": "M6.287 2.857q0 0.232-0.17 0.402t-0.402 0.17h-1.143v9.143h1.143q0.232 0 0.402 0.17t0.17 0.402-0.17 0.402l-2.286 2.286q-0.17 0.17-0.402 0.17t-0.402-0.17l-2.286-2.286q-0.17-0.17-0.17-0.402t0.17-0.402 0.402-0.17h1.143v-9.143h-1.143q-0.232 0-0.402-0.17t-0.17-0.402 0.17-0.402l2.286-2.286q0.17-0.17 0.402-0.17t0.402 0.17l2.286 2.286q0.17 0.17 0.17 0.402z"
+  },
+  "DIR.HORIZONTAL": {
+    "section": "Arrows",
+    "title": "Horizontal",
+    "viewBox": "0 0 16 16",
+    "path": "M16 8q0 0.232-0.17 0.402l-2.286 2.286q-0.17 0.17-0.402 0.17t-0.402-0.17-0.17-0.402v-1.143h-9.143v1.143q0 0.232-0.17 0.402t-0.402 0.17-0.402-0.17l-2.286-2.286q-0.17-0.17-0.17-0.402t0.17-0.402l2.286-2.286q0.17-0.17 0.402-0.17t0.402 0.17 0.17 0.402v1.143h9.143v-1.143q0-0.232 0.17-0.402t0.402-0.17 0.402 0.17l2.286 2.286q0.17 0.17 0.17 0.402z"
+  },
+  "DIR.EXPAND": {
+    "section": "Arrows",
+    "title": "Expand",
+    "viewBox": "0 0 16 16",
+    "path": "M16 0h-6.5l2.5 2.5-3 3 1.5 1.5 3-3 2.5 2.5z M16 16v-6.5l-2.5 2.5-3-3-1.5 1.5 3 3-2.5 2.5z M0 16h6.5l-2.5-2.5 3-3-1.5-1.5-3 3-2.5-2.5z M0 0v6.5l2.5-2.5 3 3 1.5-1.5-3-3 2.5-2.5z"
+  },
+  "MEDIA.PREV": {
+    "section": "Media",
+    "title": "Previous",
+    "viewBox": "0 0 14 24",
+    "path": "M13.112 1.888q0.254-0.254 0.429-0.174t0.174 0.429v19.714q0 0.348-0.174 0.429t-0.429-0.174l-9.509-9.509q-0.121-0.121-0.174-0.254v9.080q0 0.348-0.254 0.603t-0.603 0.254h-1.714q-0.348 0-0.603-0.254t-0.254-0.603v-18.857q0-0.348 0.254-0.603t0.603-0.254h1.714q0.348 0 0.603 0.254t0.254 0.603v9.080q0.054-0.147 0.174-0.254z"
+  },
+  "MEDIA.NEXT": {
+    "section": "Media",
+    "title": "Next",
+    "viewBox": "0 0 14 24",
+    "path": "M0.603 22.112q-0.254 0.254-0.429 0.174t-0.174-0.429v-19.714q0-0.348 0.174-0.429t0.429 0.174l9.509 9.509q0.107 0.107 0.174 0.254v-9.080q0-0.348 0.254-0.603t0.603-0.254h1.714q0.348 0 0.603 0.254t0.254 0.603v18.857q0 0.348-0.254 0.603t-0.603 0.254h-1.714q-0.348 0-0.603-0.254t-0.254-0.603v-9.080q-0.067 0.134-0.174 0.254z"
+  },
+  "MEDIA.PLAY": {
+    "section": "Media",
+    "viewBox": "0 0 19 24",
+    "path": "M18.547 12.415l-17.786 9.884q-0.308 0.174-0.529 0.040t-0.221-0.482v-19.714q0-0.348 0.221-0.482t0.529 0.040l17.786 9.884q0.308 0.174 0.308 0.415t-0.308 0.415z"
+  },
+  "MEDIA.PAUSE": {
+    "section": "Media",
+    "viewBox": "0 0 24 24",
+    "path": "M20.571 2.571v18.857q0 0.348-0.254 0.603t-0.603 0.254h-6.857q-0.348 0-0.603-0.254t-0.254-0.603v-18.857q0-0.348 0.254-0.603t0.603-0.254h6.857q0.348 0 0.603 0.254t0.254 0.603zM8.571 2.571v18.857q0 0.348-0.254 0.603t-0.603 0.254h-6.857q-0.348 0-0.603-0.254t-0.254-0.603v-18.857q0-0.348 0.254-0.603t0.603-0.254h6.857q0.348 0 0.603 0.254t0.254 0.603z"
+  },
+  "MEDIA.STOP": {
+    "section": "Media",
+    "viewBox": "0 0 24 24",
+    "path": "M20.571 2.571v18.857q0 0.348-0.254 0.603t-0.603 0.254h-18.857q-0.348 0-0.603-0.254t-0.254-0.603v-18.857q0-0.348 0.254-0.603t0.603-0.254h18.857q0.348 0 0.603 0.254t0.254 0.603z"
+  },
+  "MEDIA.EJECT": {
+    "section": "Media",
+    "viewBox": "0 0 21 24",
+    "path": "M0.188 13.112l9.509-9.509q0.254-0.254 0.603-0.254t0.603 0.254l9.509 9.509q0.254 0.254 0.174 0.429t-0.429 0.174h-19.714q-0.348 0-0.429-0.174t0.174-0.429zM19.728 20.571h-18.857q-0.348 0-0.603-0.254t-0.254-0.603v-3.429q0-0.348 0.254-0.603t0.603-0.254h18.857q0.348 0 0.603 0.254t0.254 0.603v3.429q0 0.348-0.254 0.603t-0.603 0.254z"
+  },
+  "COMPONENT": {
+    "section": "Software",
+    "title": "Component",
+    "viewBox": "0 0 16 16",
+    "path": "M8 14.545l5.714-3.116v-5.679l-5.714 2.080v6.714zM7.429 6.821l6.232-2.268-6.232-2.268-6.232 2.268zM14.857 4.571v6.857q0 0.313-0.161 0.58t-0.438 0.42l-6.286 3.429q-0.25 0.143-0.545 0.143t-0.545-0.143l-6.286-3.429q-0.277-0.152-0.438-0.42t-0.161-0.58v-6.857q0-0.357 0.205-0.652t0.545-0.42l6.286-2.286q0.196-0.071 0.393-0.071t0.393 0.071l6.286 2.286q0.339 0.125 0.545 0.42t0.205 0.652z"
+  },
+  "PACKAGE": {
+    "section": "Software",
+    "title": "Package",
+    "viewBox": "0 0 21 16",
+    "path": "M5.714 14.571l3.429-1.714v-2.804l-3.429 1.464v3.054zM5.143 10.518l3.607-1.545-3.607-1.545-3.607 1.545zM14.857 14.571l3.429-1.714v-2.804l-3.429 1.464v3.054zM14.286 10.518l3.607-1.545-3.607-1.545-3.607 1.545zM10.286 7.902l3.429-1.473v-2.375l-3.429 1.464v2.384zM9.714 4.518l3.938-1.688-3.938-1.688-3.938 1.688zM19.429 9.143v3.714q0 0.321-0.17 0.598t-0.464 0.42l-4 2q-0.223 0.125-0.509 0.125t-0.509-0.125l-4-2q-0.045-0.018-0.063-0.036-0.018 0.018-0.063 0.036l-4 2q-0.223 0.125-0.509 0.125t-0.509-0.125l-4-2q-0.295-0.143-0.464-0.42t-0.17-0.598v-3.714q0-0.339 0.192-0.625t0.504-0.429l3.875-1.661v-3.571q0-0.339 0.192-0.625t0.504-0.429l4-1.714q0.205-0.089 0.446-0.089t0.446 0.089l4 1.714q0.313 0.143 0.504 0.429t0.192 0.625v3.571l3.875 1.661q0.321 0.143 0.509 0.429t0.188 0.625z"
+  }
+
+}
diff --git a/ivette/src/dome/src/renderer/controls/labels.css b/ivette/src/dome/src/renderer/controls/labels.css
new file mode 100644
index 0000000000000000000000000000000000000000..e7417b8bd757c78768630264189d11b2c217c203
--- /dev/null
+++ b/ivette/src/dome/src/renderer/controls/labels.css
@@ -0,0 +1,18 @@
+/* -------------------------------------------------------------------------- */
+/* --- Styling Labels                                                     --- */
+/* -------------------------------------------------------------------------- */
+
+.dome-xLabel {
+    padding: 2px ;
+    margin: 2px 4px ;
+}
+
+.dome-xLabel .dome-xIcon {
+    margin-right: 4px ;
+}
+
+.dome-xLabel.dome-text-descr {
+    overflow: auto ;
+}
+
+/* -------------------------------------------------------------------------- */
diff --git a/ivette/src/dome/src/renderer/controls/labels.js b/ivette/src/dome/src/renderer/controls/labels.js
new file mode 100644
index 0000000000000000000000000000000000000000..21c8f287c0184b89fb8d65d17f566a00d60d2d0b
--- /dev/null
+++ b/ivette/src/dome/src/renderer/controls/labels.js
@@ -0,0 +1,104 @@
+// --------------------------------------------------------------------------
+// --- Labels
+// --------------------------------------------------------------------------
+
+/** @module dome/controls/labels */
+
+import React from 'react' ;
+import labels from './labels.css' ;
+import { Icon } from './icons' ;
+
+// --------------------------------------------------------------------------
+// --- Generic Label
+// --------------------------------------------------------------------------
+
+const addClass = (a,b) => b ? a + ' ' + b : a ;
+
+const makeLabel = (className,props) => {
+  const { display=true } = props ;
+  const allClasses =
+        className +
+        (display ? ' ' : ' dome-control-erased ') +
+        (props.className || '');
+  return (
+    <label className={allClasses}
+           title={props.title}
+           style={props.style} >
+      {props.icon && <Icon title={props.title} id={props.icon}/>}
+      {props.label}
+      {props.text}
+      {props.children}
+    </label>
+  );
+};
+
+// --------------------------------------------------------------------------
+// --- CSS Classes
+// --------------------------------------------------------------------------
+
+const LABEL = "dome-xLabel dome-text-label" ;
+const TITLE = "dome-xLabel dome-text-title" ;
+const DESCR = "dome-xLabel dome-text-descr" ;
+const TDATA = "dome-xLabel dome-text-data" ;
+const TCODE = "dome-xLabel dome-text-code" ;
+
+// --------------------------------------------------------------------------
+// --- Components
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @summary Component labels.
+   @property {string} [label] - Textual content (prepend to children components, if any)
+   @property {string} [icon] - Label icon (optional, on left side)
+   @property {string} [title] - Label tooltip (optional)
+   @property {string} [className] - Additional class
+   @property {object} [style] - Additional CSS style
+*/
+export const Label = (props) => makeLabel(LABEL,props);
+
+/**
+   @class
+   @summary Title and headings.
+   @property {string} [label] - Textual content (prepend to children components, if any)
+   @property {string} [icon] - Label icon (optional, on left side)
+   @property {string} [title] - Label tooltip (optional)
+   @property {string} [className] - Additional class
+   @property {object} [style] - Additional CSS style
+*/
+export const Title = (props) => makeLabel(TITLE,props);
+
+/**
+   @class
+   @summary Description, textbook content.
+   @property {string} [label] - Textual content (prepend to children components, if any)
+   @property {string} [icon] - Label icon (optional, on left side)
+   @property {string} [title] - Label tooltip (optional)
+   @property {string} [className] - Additional class
+   @property {object} [style] - Additional CSS style
+*/
+export const Descr = (props) => makeLabel(DESCR,props);
+
+/**
+   @class
+   @summary Selectable textual information.
+   @property {string} [label] - Textual content (prepend to children components, if any)
+   @property {string} [icon] - Label icon (optional, on left side)
+   @property {string} [title] - Label tooltip (optional)
+   @property {string} [className] - Additional class
+   @property {object} [style] - Additional CSS style
+*/
+export const Data = (props) => makeLabel(TDATA,props);
+
+/**
+   @class
+   @summary Selectable inlined source-code content.
+   @property {string} [text] - Textual content (prepend to children components, if any)
+   @property {string} [icon] - Label icon (optional, on left side)
+   @property {string} [title] - Label tooltip (optional)
+   @property {string} [className] - Additional class
+   @property {object} [style] - Additional CSS style
+*/
+export const Code = (props) => makeLabel(TCODE,props);
+
+// --------------------------------------------------------------------------
diff --git a/ivette/src/dome/src/renderer/data.js b/ivette/src/dome/src/renderer/data.js
new file mode 100644
index 0000000000000000000000000000000000000000..e8b6b562cc4a8cc976a3bd77c928ceb7c0d7447a
--- /dev/null
+++ b/ivette/src/dome/src/renderer/data.js
@@ -0,0 +1,413 @@
+// --------------------------------------------------------------------------
+// --- Data Collector
+// --------------------------------------------------------------------------
+
+/** @module dome/data
+    @description
+
+This module allows to integrate data definitions within React elements.
+Typically, you can use it to define your own structure of logical elements
+and display them at different places in the GUI. For instance, you may
+want to define a custom list of elements, where each element
+will be rendered twice: locally with the currenly selected item, and in a side-bar
+or in the menu-bar, or for any other purpose than rendering. You want to declare
+your list like this:
+
+```{jsx}
+    <MyList>
+       <MyItem id={A} ... >...</MyItem>
+       <MyItem id={B} ... >...</MyItem>
+       <MyItem id={C} ... >...</MyItem>
+    </MyList>
+```
+
+Dome data libraries, as provided by this module, allows you to define such
+collection of data mixed with rendered elements.
+
+Data are collected through libraries.
+You create libraries with `createLibrary()` or `useLocalLibrary()` and
+then provide them to `<Data.Item>`, `<Data.Component>`, `<Data.Node>` or
+`<Data.Fragment>` elements.
+
+At any (other) point of the tree, you can use the collected items
+with the `useLibrary()` custom React hook.
+
+Libraries are pushed down the React virtual tree _via_ React context,
+so you don't need to re-specify it for children of `<Data.Node/>`
+and `<Data.Fragment/>`.
+
+Items must be identified with a unique `id` ; they are sorted according
+to their `order` property, although `<Data.Fragment/>` and `<Data.Node/>`
+preserve the order of data collected from each of their children. Items are
+collected when they are actually _mounted_ by React,
+like any other React element.
+
+Data elements are normal React element, that might be rendered by
+visible elements in the DOM. You may think of data elements as having a
+double rendering: mounted data items are collected into libraries, and normal visible
+elements are collected into the React virtual DOM. Each kind of data element is
+rendered differenly with this respect:
+
+- `<Data.Item>` renders its children within a React fragment;
+- `<Data.Node>` is like an item, with its data children stored in the registered item;
+- `<Data.Component>` always renders 'null' and capture its React children elements in the registered item;
+- `<Data.Fragment>` renders its children within a React fragment, with optional context library and specified order.
+
+See each component for more details.
+
+As an example of use, the introductory example can be implemented as follows:
+
+
+```{jsx}
+    const lib = createLibrary();
+
+    // To be used at any (possibly repeated) place in the hierarchy of react elements
+    // Children items are only added to the library when the list is mounted
+    const MyList = ({ children }) => {
+       // Actually renders nothing if children only contains items.
+       // The fragment makes items keep their declaration ordering.
+       return (
+          <Data.Fragment lib={lib}>{children}</Data.Fragment>
+       );
+    };
+
+    // To be used also at any (possibly repeated) place in the hierarchy
+    const MyItem = (props) => <Data.Item lib={lib} {...props} /> ;
+
+    // To be used for instance at top-level inside a side-bar
+    const MyListIndex = () => {
+       let items = Data.useLibrary(lib);
+       return (
+         <ul>
+           { items.map(({id,label}) => (<li key={id}>{label}</li>)) }
+         </ul>
+       );
+    };
+```
+
+*/
+
+import _ from 'lodash' ;
+import React from 'react';
+import Dome from 'dome' ;
+import EventEmitter from 'events' ;
+
+
+// --------------------------------------------------------------------------
+// --- Libraries
+// --------------------------------------------------------------------------
+
+const reOrder = (items) =>
+      _.sortBy( items , [ 'order','id' ] )
+      .map( (item,order) => Object.assign( item, { order } ));
+
+/**
+    @summary Data Collector.
+    @description
+    Libraries are used to collect data through the React virtual DOM.
+*/
+class Library extends EventEmitter
+{
+
+  constructor() {
+    super();
+    this.items = {};
+    this.lastItems = {};
+    this._trigger = _.debounce(this._trigger,10);
+  }
+
+  _trigger() {
+    if (!_.isEqual( this.items , this.lastItems )) {
+      this.lastItems = this.items ;
+      this.sorted = undefined ;
+      this.emit('trigger');
+    }
+  };
+
+  /**
+     @summary Register Item.
+     @param {object} item - must have an `'id'` property
+   */
+  add(item) {
+    const id = item.id ;
+    if (Dome.DEVEL && this.items[id])
+      console.warn(`[dome/data] duplicate item identifier (${id})`);
+    this.items[id] = item ;
+    this._trigger();
+  }
+
+  /**
+     @summary Remove Item.
+     @param {string} id - the item identifier to remove
+   */
+  remove(id) {
+    delete this.items[id];
+    this._trigger();
+  }
+
+  /**
+     @summary Bugger Contents.
+     @return {Array<object>} items array sorted by `'order'` and `'id'` properties.
+   */
+  contents() {
+    return this.sorted || (this.sorted = reOrder( this.items ));
+  }
+
+  /**
+     @summary Register callback.
+     @param {function} callback - invoked when library contents changes
+  **/
+  on(callback) { this.on('trigger',callback); }
+
+  /**
+     @summary Un-register callback.
+     @param {function} callback - callback to unregister
+  **/
+  off(callback) { this.off('trigger',callback); }
+
+}
+
+/**
+   @summary Creates a Data library.
+   @return {Library} a newly created, empty library.
+   @description
+   Same as `new Library()`.
+ */
+export function createLibrary() { return new Library(); }
+
+/**
+   @summary Collect living items from the library (Custom React Hook).
+   @param {Library} library - the desired library
+   @return {Array<items>} items currently mounted in the library
+   @description
+   This hook is automatically updated whenever an item is added or removed
+   from the library.
+ */
+export function useLibrary(library) {
+  const forceUpdate = Dome.useForceUpdate();
+  Dome.useEmitter( library , 'trigger' , forceUpdate );
+  return library.contents();
+}
+
+/**
+   @summary Use a locally created new library (Custom React Hook).
+   @return {object} `{ library, items }` the local library and its collected items
+   @description
+   This is a combination of a locally created library _and_ its collected items.
+   Same as:
+   ```
+   const library = React.useMemo( createLibrary , [] );
+   const items = useLibrary( library );
+   ```
+ */
+export function useLocalLibrary() {
+  const library = React.useMemo( createLibrary , [] );
+  const items = useLibrary( library );
+  return { library , items };
+}
+
+const CurrentLib = React.createContext();
+const CurrentPath = React.createContext([]);
+
+const makePath = (path,order) =>
+      order === undefined ? path : path.slice(0,-1).concat(order);
+
+/**
+   @summary Current library (Custom React Hook).
+   @return {Library} in local context
+ */
+export function useCurrentLibrary() {
+  return React.useContext( CurrentLib );
+}
+
+// --------------------------------------------------------------------------
+// --- Internals
+// --------------------------------------------------------------------------
+
+function useLocalItem( { lib:localLib, id, order, ...itemProps }, children )
+{
+  const currentLib = React.useContext(CurrentLib);
+  const currentPath = React.useContext(CurrentPath);
+  const path = makePath( currentPath, order );
+  React.useEffect( () => {
+    const library = localLib || currentLib ;
+    if (id && library) {
+      const item = { id, order:path, ...itemProps };
+      if (children) item.children = children ;
+      library.add(item) ;
+      return () => library.remove(id);
+    } else
+      return undefined;
+  });
+  return path;
+};
+
+function makeChildren( path, children)
+{
+  const n = React.Children.count( children );
+  if (n == 0) return null;
+  else {
+    const childContext = (elt,k) => {
+      if (elt) {
+        const newPath = path.concat(1+k);
+        return (
+          <CurrentPath.Provider value={newPath}>
+            {elt}
+          </CurrentPath.Provider>
+        );
+      } else
+        return elt ;
+    };
+    return React.Children.map( children, childContext );
+  }
+}
+
+// --------------------------------------------------------------------------
+// --- Item Definition
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @summary Data Item definition.
+   @property {Library} [lib] - data library collecting the item
+   @property {string} [id] - item identifier
+   @property {number} [order] - item local ordering (default: inherited)
+   @property {any} [...props] - other item properties
+   @property {React.Children} [children] - rendered elements
+   @description
+   Register a new item in the library:
+
+   ```
+   { id, order, props }
+   ```
+
+   If not specified, the current context library is used. If no identifier nor
+   library is actually available, the item definition is skipped.
+
+   An `<Item/>` element rendres its children in a nested, ordered fragment,
+   but with the same current library than the inherited one, if any.
+*/
+export const Item = ( { children, ...props} ) => {
+  let path = useLocalItem( props );
+  return (<React.Fragment>{makeChildren(path,children)}</React.Fragment>);
+};
+
+// --------------------------------------------------------------------------
+// --- Component Definition
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @summary Data Component definition.
+   @property {Library} [lib] - data library collecting the item (default: inherited)
+   @property {string} id - item identifier (default: skip item definition)
+   @property {number} [order] - item order (default: parent fragment ordering)
+   @property {any} [...props] - registered item properties
+   @property {React.Children} [children] - component _virtual_ elements
+   @description
+   Register a new item in the library. If enabled and not disabled,
+   the collected item data will be:
+
+   ```
+   { id, order, props, children: React.Children }
+   ```
+
+   The specified order property is used to sort this item among its immediate
+   neighbours in the React virtual DOM. The final item order is determined with
+   respect to all the other collected items.
+
+   Children elements of the `<Component/>` are _not_ mounted into the React
+   virtual DOM.  However, they are stored in the `children` property of the
+   item, and can be rendered in other parts of the DOM through
+   `useCurrentLibrary()` react hook. The local context will _not_ be propagated
+   to these children.
+
+   The component element itself is rendered as `null` when mounted in the virtual DOM by React.
+*/
+export const Component = ( { children, ...props} ) => {
+  useLocalItem( props, children );
+  return null;
+};
+
+// --------------------------------------------------------------------------
+// --- Node Definition
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @summary Recursive Data Item definition.
+   @property {Library} [lib] - data library collecting the item (default: inherited)
+   @property {string} id - item identifier (default: skip item definition)
+   @property {number} [order] - item order (default: parent fragment ordering)
+   @property {any} [...props] - registered item properties
+   @property {React.Children} [children] - sub-data and rendering of the item
+   @description
+   Register a new item in the library, as follows:
+
+   ```
+   { id, order, props, children: Array<item> }
+   ```
+
+   The specified order property is used to sort this item among its immediate
+   neighbours in the React virtual DOM. The final item order is determined with
+   respect to all the other collected items.
+
+   A _new_ local library is created and associated to this `<Node/>` element, and propagated
+   in children as the new context library. If necessary, you can obtain this local
+   library with the `useContext()` react hook.
+
+   Hence, children elements of the node item _are_ rendered in the virtual React DOM,
+   but their data elements are collected and stored in the `children` property of the
+   defined `<Node/>` item.
+*/
+export const Node = ({ children, ...props }) => {
+  let { library, items } = useLocalLibrary();
+  let path = useLocalItem( props , items );
+  return (
+    <CurrentLib.Provider value={library}>
+      {makeChildren( path, children )}
+    </CurrentLib.Provider>
+  );
+};
+
+// --------------------------------------------------------------------------
+// --- Fragment Definition
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @summary Ordered Data Collection.
+   @property {Library} [lib] - local library to use
+   @property {Sortable} [order] - local order to use (default: inherited)
+   @property {boolean} [enabled] - fragment shall be rendered (default: `true`)
+   @property {boolean} [disabled] - fragment shal not be rendered (default: `false`)
+   @property {React.Children} [children] - sub-data and rendering of the data collection
+ */
+export const Fragment = ({lib:localLib, order, enabled=true, disabled=false, children, ...localProps}) => {
+  const currentLib = React.useContext(CurrentLib);
+  const currentPath = React.useContext(CurrentPath);
+  const library = localLib || currentLib ;
+  if ( enabled && !disabled && React.Children.count(children) > 0 )
+  {
+    const path = makePath( currentPath, order );
+    return (
+      <CurrentLib.Provider value={library}>
+        {makeChildren( path, children )}
+      </CurrentLib.Provider>
+    );
+  } else
+    return null;
+};
+
+// --------------------------------------------------------------------------
+// --- Exports
+// --------------------------------------------------------------------------
+
+export default {
+  Library,
+  createLibrary, useLibrary,
+  useCurrentLibrary, useLocalLibrary,
+  Item, Component, Node, Fragment
+};
+
+// --------------------------------------------------------------------------
diff --git a/ivette/src/dome/src/renderer/dialogs.js b/ivette/src/dome/src/renderer/dialogs.js
new file mode 100644
index 0000000000000000000000000000000000000000..92f5416323cb2bc9d8c3ebd7219db61ca5519da4
--- /dev/null
+++ b/ivette/src/dome/src/renderer/dialogs.js
@@ -0,0 +1,318 @@
+/**
+   @module dome/dialogs
+   @description
+   Various kind of (modal) dialogs attached to the main application window.
+ */
+
+import { remote } from 'electron' ;
+import System from 'dome/system' ;
+
+// --------------------------------------------------------------------------
+// --- Message Box
+// --------------------------------------------------------------------------
+
+const defaultItems = [
+  { value:undefined },
+  { value:true, label:'Ok' }
+];
+
+const valueLabel = (v) => {
+  switch(v) {
+  case undefined: return 'Cancel' ;
+  case true: return 'Ok' ;
+  case false: return 'No' ;
+  default: return ''+v ;
+  }
+};
+
+const itemLabel = ({value,label}) => label || valueLabel(value) ;
+const isDefault = ({value,label}) => value===true || label==='Ok' || label==='Yes' ;
+const isCancel = ({value,label}) => !value || label==='Cancel' || label==='No' ;
+
+/**
+   @summary Show a configurable message box.
+   @parameter {object} options - configuration (see above)
+   @return {Promise} the selected option (see above)
+   @description
+The available fields and options for configuring the dialog are:
+
+| Options | Type | Description |
+|:--------|:----:|:------------|
+| `kind` | `'none','info','error','warning'` | Icon of the message box |
+| `title` | `string` (_opt._) | Heading of message box |
+| `message` | `string` | Message text |
+| `buttons` | `button[]` (_opt._) | Dialog buttons |
+| `defaultValue` | (any) (_opt._) | Value of the default button |
+| `cancelValue` | (any) (_opt._) | Value of the cancel key |
+
+The dialog buttons are specified by objects with the following fields:
+
+| Button Field | Type | Description |
+|:-------------|:----:|:------------|
+| `label` | `string` | Button label |
+| `value` | (any) | Button identifier (items only) |
+
+The returned promise object is never rejected, and is asynchronously
+resolved into:
+- the cancel value if the cancel key is pressed,
+- the default value if the enter key is pressed,
+- or the value of the clicked button otherwised.
+
+The default buttons are `"Ok"` and `"Cancel"` associated to values `true` and
+`undefined`, which are also associated to the enter and cancel keys.
+Unless specified, the default value is associated with the first button
+with 'true' value or 'Ok' or 'Yes' label,
+and the cancel value is the first button with a falsy value or 'Cancel'
+or 'No' label.
+*/
+export function showMessageBox( options )
+{
+  const {
+    kind,
+    title,
+    message,
+    defaultValue,
+    cancelValue,
+      buttons = defaultItems
+  } = options ;
+
+  const labels = buttons.map(itemLabel);
+  let defaultId =
+      defaultValue === undefined
+      ? buttons.findIndex(isDefault)
+      : buttons.findIndex((a) => a.value === defaultValue);
+  let cancelId =
+      cancelValue === undefined
+      ? buttons.findIndex(isCancel)
+      : buttons.findIndex((a) => a.value === cancelValue);
+
+  if (cancelId === defaultId) cancelId = -1;
+
+  return remote.dialog.showMessageBox(
+    remote.getCurrentWindow(),
+    {
+      type:kind,
+      message: message && title,
+      detail:  message || title,
+      defaultId, cancelId, buttons: labels
+    }
+  ).then((result) => {
+    let itemIndex = result ? result.response : -1 ;
+    return itemIndex ? buttons[itemIndex].value : cancelValue ;
+  });
+}
+
+// --------------------------------------------------------------------------
+// --- openFile dialog
+// --------------------------------------------------------------------------
+
+import filepath from 'path' ;
+const defaultPath = (path) => filepath.extname(path) ? filepath.dirname(path) : path ;
+
+/**
+   @summary Show a dialog for opening file.
+   @parameter {object} options - configuration (see above)
+   @return {Promise} the selected file (see above)
+   @description
+The available fields and options for configuring the dialog are:
+
+| Options | Type | Description |
+|:--------|:----:|:------------|
+| `message` | `string` (_opt._) | Prompt message |
+| `label` | `string` (_opt._) | Open button label |
+| `path` | `filepath` (_opt._) | Initially selected path |
+| `hidden` | `boolean` (_opt._) | Show hidden files (not by default) |
+| `filters` | `filter[]` (_opt._) | File filters (all files by default) |
+
+The file filters are object with the following fields:
+
+| Filter Field | Type | Description |
+|:-------------|:----:|:------------|
+| `name` | `string` | Filter name |
+| `extensions` | `string[]` | Allowed file extensions, _without_ dots («.») |
+
+A file filter with `extensions:["*"]` would accept any file extension.
+
+The returned promise object will be asynchronously:
+- either _resolved_ with `undefined` if no file has been selected,
+- or _resolved_ with the selected path
+
+The promise is never rejected.
+
+*/
+export function showOpenFile( options )
+{
+  const { message, label, path, hidden, filters } = options ;
+  const properties = [ 'openFile' ];
+  if (hidden) properties.push('showHiddenFiles');
+
+  return remote.dialog.showOpenDialog(
+    remote.getCurrentWindow(),
+    {
+      message, buttonLabel: label,
+      defaultPath: path && defaultPath(path),
+      properties, filters
+    }
+  ).then(result => {
+    if (!result.canceled && result.filePaths && result.filePaths.length > 0)
+      return result.filePaths[0] ;
+    else
+      return undefined ;
+  });
+}
+
+/**
+   @summary Show a dialog for opening file.
+   @parameter {object} options - configuration (see above)
+   @return {Promise} the selected file(s) (see above)
+   @description
+The available fields and options for configuring the dialog are:
+
+| Options | Type | Description |
+|:--------|:----:|:------------|
+| `message` | `string` (_opt._) | Prompt message |
+| `label` | `string` (_opt._) | Open button label |
+| `path` | `filepath` (_opt._) | Initially selected path |
+| `hidden` | `boolean` (_opt._) | Show hidden files (not by default) |
+| `filters` | `filter[]` (_opt._) | File filters (all files by default) |
+
+The file filters are object with the following fields:
+
+| Filter Field | Type | Description |
+|:-------------|:----:|:------------|
+| `name` | `string` | Filter name |
+| `extensions` | `string[]` | Allowed file extensions, _without_ dots («.») |
+
+A file filter with `extensions:["*"]` would accept any file extension.
+
+The returned promise object will be asynchronously:
+- either _resolved_ with `undefined` if no file has been selected,
+- or _resolved_ with the selected paths
+
+The promise is never rejected.
+
+*/
+export function showOpenFiles( options )
+{
+  const { message, label, path, hidden, filters } = options ;
+  const properties = [ 'openFile', 'multiSelections' ];
+  if (hidden) properties.push('showHiddenFiles');
+
+  return remote.dialog.showOpenDialog(
+    remote.getCurrentWindow(),
+    {
+      message, buttonLabel: label,
+      defaultPath: path && defaultPath(path),
+      properties, filters
+    }
+  ).then(result => {
+    if (!result.canceled && result.filePaths && result.filePaths.length > 0)
+      return result.filePaths ;
+    else
+      return undefined ;
+  });
+}
+
+
+// --------------------------------------------------------------------------
+// --- saveFile dialog
+// --------------------------------------------------------------------------
+
+/**
+   @summary Show a dialog for saving file.
+   @parameter {object} options - configuration (see above)
+   @return {Promise} the selected path (see above)
+   @description
+The available fields and options for configuring the dialog are:
+
+| Options | Type | Description |
+|:--------|:----:|:------------|
+| `message` | `string` (_opt._) | Prompt message |
+| `label` | `string` (_opt._) | Save button label |
+| `path` | `filepath` (_opt._) | Initially selected path |
+| `filters` | `filter[]` (_opt._) | Cf. `openFileDialog` |
+
+The returned promise object will be asynchronously:
+- either _resolved_ with `undefined` if no file has been selected,
+- or _resolved_ with the selected (single) path.
+
+The promise is never rejected.
+
+*/
+export function showSaveFile( options )
+{
+  const { message, label, path, filters } = options ;
+
+  return remote.dialog.showSaveDialog(
+    remote.getCurrentWindow(),
+    {
+      message, buttonLabel: label,
+      defaultPath: path,
+      filters
+    }
+  );
+}
+
+// --------------------------------------------------------------------------
+// --- openDir dialog
+// --------------------------------------------------------------------------
+
+/**
+   @summary Show a dialog for selecting directories.
+   @parameter {object} options - configuration (see above)
+   @return {Promise} the selected directories (see above)
+   @description
+The available fields and options for configuring the dialog are:
+
+| Options | Type | Description |
+|:--------|:----:|:------------|
+| `message` | `string` (_opt._) | Prompt message |
+| `label` | `string` (_opt._) | Open button label |
+| `path` | `filepath` (_opt._) | Initially selected path |
+| `hidden` | `boolean` (_opt._) | Show hidden files (not by default) |
+
+The returned promise object will be asynchronously:
+- either _resolved_ with `undefined` if no file has been selected,
+- or _resolved_ with the selected path
+
+*/
+export function showOpenDir( options )
+{
+  const { message, label, path, hidden } = options ;
+  const properties = [ 'openDirectory' ];
+  if (hidden) properties.push('showHiddenFiles');
+
+  switch(System.platform) {
+    case 'macos':   properties.push( 'createDirectory' ); break;
+  case 'windows': properties.push( 'promptToCreate' ); break;
+  }
+
+  return remote.dialog.showOpenDialog(
+    remote.getCurrentWindow(),
+    {
+      message,
+      buttonLabel: label,
+      defaultPath: path,
+      properties
+    }
+  ).then(result => {
+    if (!result.canceled && result.filePaths && result.filePaths.length > 0)
+      return result.filePaths[0] ;
+    else
+      return undefined ;
+  });
+}
+
+// --------------------------------------------------------------------------
+// --- Export
+// --------------------------------------------------------------------------
+
+export default {
+  showMessageBox,
+  showOpenFile,
+  showOpenFiles,
+  showSaveFile,
+  showOpenDir
+} ;
+
+// --------------------------------------------------------------------------
diff --git a/ivette/src/dome/src/renderer/dnd.js b/ivette/src/dome/src/renderer/dnd.js
new file mode 100644
index 0000000000000000000000000000000000000000..89829fbb5a3c86d6d251713c6ead917d2b96d503
--- /dev/null
+++ b/ivette/src/dome/src/renderer/dnd.js
@@ -0,0 +1,860 @@
+/**
+   @module dome/dnd
+   @summary Drag & Drop Features.
+   @description
+
+## D&D Sequences
+
+A Drag & Drop sequence is monitored by a D&D controller.
+Drag Sources initiates the a D&D sequence, and the D&D controller will then
+notify the Drop Target when they are dragged over.
+
+More precisely, the D&D controller is notified with all events of any D&D sequence.
+Drag sources are notified will all events of the D&D sequences they have initiated.
+Drop targets are only notified when they are actually participating into some D&D sequence.
+For instance, when the dragged item directly moves from one target into another one, both
+targets will be notified, but not the others.
+
+
+## Dragging Events
+
+Many dragging events are triggered during a D&D sequence.
+All event listeners will be notified with a single argument,
+which is a `Dragging` object with the following properties:
+ - `move`: the kind of move (see below);
+ - `meta`: a boolean indicating whether Ctrl, Alt or Meta key is pressed during the move;
+ - `held`: a boolean indicating whether the mouse is holding over position;
+ - `sourceClientRect`: the position of the drag source element;
+ - `targetClientRect`: the position of the drop target element;
+ - `deltaX`, `deltaY`: the current offset position of the dragged element,
+relative to its _initial_ position;
+ - `clientX`, `clientY`: the current client position of the _cursor_, as obtained from the dragging event;
+ - `position`: the current position from React-Draggable API (see DragSource documentation).
+
+All coordinates are provided in the window coordinate system,
+as returned by the `getBoundingClientRect` DOM method.
+
+
+## Dragging State
+
+During the entire sequence, the D&D controller is responsible for managing a shared
+state among source and targets.
+The drag source and each drop targets may update the shared state when receiving
+events, and will be re-rendered in turn when necessary.
+
+This design eases the separation of concern: each participant in the D&D
+sequence is only responsible with its own rendering, while synchronizing between the
+participants is ensured through the global state.
+
+
+## Kind of Moves
+
+During a D&D sequence, different kind of events can be triggered.
+They can be distinguished with the `move` property of the dragging events,
+as follows:
+ - `'START'`: D&D sequence initiated;
+ - `'STOP'`: D&D sequence finished without drop target;
+ - `'DROP'`: D&D sequence finished over some drop target;
+ - `'DRAG'`: the source is dragged over some drop target;
+ - `'HOLD'`: the source is holding the position for a while;
+ - `'MOVE'`: the source is moving again after holding for a while ;
+ - `'ENTER'`: the source is entering a target from _no_ other target;
+ - `'LEAVE'`: the source is leaving a target into _no_ other target;
+ - `'SWITCH'`: the source is leaving a target into another target.
+
+Notice that in kind of a `'SWITCH'` event, the leaved target will receive a `'LEAVE'` event
+and the entered target will receive an `'ENTER'` event.
+
+
+## Event Callbacks
+
+D&D controllers, drag sources and drop targets can listen to
+dragging events _via_ various callbacks.
+
+Each callback has signature `(Dragging) => void` and is sensible to
+specific kinds of events, as described below:
+ - `onDnd()`: all kind of events;
+ - `onStart()`: `START` events;
+ - `onEnter()`: `ENTER` and `SWITCH` events;
+ - `onLeave()`: `LEAVE` and `SWITCH` events;
+ - `onDrag()`: `DRAG` and `MOVE` events;
+ - `onHold()`: `HOLD` events;
+ - `onMove()`: `MOVE` events;
+ - `onDrop()`: `DROP` events;
+ - `onStop()`: `STOP` events;
+ - `onUpdate()`: only for D&D controllers, see below.
+
+In case of a `SWITCH` event, the `onLeave` listener is called first,
+then the `onEnter` listener. This is only relevant for the D&D controller
+and the drag source, since drop targets will receive either `ENTER` or `LEAVE` events.
+
+The same `Dragging` object is shared across all the event listeners, but
+not among different drag source and drop targets. For each source or target
+participating into the event, the `onDnd` listener is called first,
+then the more specific ones, when defined. Hence, you can modify the `Dragging`
+object during the process of `onDnd` and safely propagate those modifications accross
+all listeners.
+
+Inside a D&D sequence, the leaved targets are notified first,
+then the source and the target, and finally the D&D controller.
+This allow you to maintain the D&D state in sync with the different participants.
+
+Additionally, the D&D controller might be equiped with
+an `onUpdate()` handler, which is responsible for listening only to state update events.
+This event handler will receive the current D&D state in argument, with the cumulated
+updates from all participants merged in.
+
+*/
+
+import React from 'react' ;
+import Draggable, { DraggableCore } from 'react-draggable';
+import _ from 'lodash' ;
+
+const HOLD_TIME = 100 ; /* time in ms */
+const HOLD_FIRE = 6 ;   /* number of HOLD_TIME before « hold » */
+const HOLD_MOVE = 3 ;   /* maximum moved pixels for « holding » */
+
+// --------------------------------------------------------------------------
+// --- Events Dispatcher
+// --------------------------------------------------------------------------
+
+const notify = ( cb, evt ) => cb && cb(evt);
+
+/**
+   @method
+   @summary Dispatch a dragging event over listeners.
+   @param {Dragging} dragging - the Dragging event to dispatch over
+   @param {object} [handlers] - an object with (optional) listeners
+   @description
+
+Dispatch a `Dragging` event among listeners defined in the `handlers` object,
+when defined. For instance, `disptachEvents( evt , { onStop: myCallback } )`
+will trigger `myCallback(evt)` if and only if `evt` is a `STOP` event.
+
+See event listeners documentation above for more details on listeners.
+
+You normally don't have to call this function, unless when you need to add
+D&D features into your own component. Here is a typical pattern:
+
+    const MyComponent = (props) => (
+       <DragSource
+          dnd={props.dnd}
+          onDnd={(evt) => DnD.dispatchEvent(Object.assign(evt,…),props)} >
+          …
+       </DragSource>
+    );
+
+ */
+export const dispatchEvent = ( dragging , handlers ) => {
+  if (!handlers) return;
+  const dragged = Object.assign( {} , dragging );
+  notify( handlers.onDnd , dragged );
+  switch( dragged.move ) {
+  case 'START':
+    notify( handlers.onStart, dragged );
+    return;
+  case 'STOP':
+    notify( handlers.onStop,  dragged );
+    return;
+  case 'ENTER':
+    notify( handlers.onEnter, dragged );
+    return;
+  case 'SWITCH':
+    notify( handlers.onLeave, dragged );
+    notify( handlers.onEnter, dragged );
+    return;
+  case 'LEAVE':
+    notify( handlers.onLeave, dragged );
+    return;
+  case 'DRAG':
+    notify( handlers.onDrag,  dragged );
+    return;
+  case 'MOVE':
+    notify( handlers.onMove,  dragged );
+    notify( handlers.onDrag,  dragged );
+    return;
+  case 'HOLD':
+    notify( handlers.onHold,  dragged );
+    return;
+  case 'DROP':
+    notify( handlers.onDrop,  dragged );
+    return;
+  default:
+    return;
+  }
+};
+
+// --------------------------------------------------------------------------
+// --- Dnd Controllers
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @summary D&D Controller.
+   @param {object} [handlers] - D&D events callbacks
+   @property {object} state - Current D&D state
+   @property {Dragging} dragging - Last D&D event
+   @description
+   D&D controlers are responsible for managing various drop targets
+   and dispatching the drag source events among them.
+
+   The handler object shall define listeners to the D&D dragging events.
+   Each callback is sensible to specific kind of events, as specified
+   in the [event listeners](module-dome_dnd.html)
+   documentation.
+
+   You need a D&D controller whenever an element can be dragged
+   between different containers. D&D controllers also offers an
+   abstraction over dragging events, dispatching among several
+   drop targets and the management of « holding » the mouse over
+   one drop target.
+
+   Normally, you simply have to create D&D controllers in your global
+   (or local) state and pass them around into the hierarchy of your
+   components. For lower-level control, see the documentation of
+   the exposed methods.
+
+   Having several D&D controllers allows you to assign specific
+   sources to specific targets. You shall have one D&D controller
+   for each « kind » of data that you can drag over.
+
+   The `state` and `dragging` properties are shared across all partipants
+   of a D&D sequence, which are also re-rendered when the state is updated.
+   Outside of a D&D sequence, the `state` and `dragging` properties are undefined.
+
+*/
+
+export class DnD {
+
+  constructor(handlers) {
+    this.kid = 0 ;
+    this.targets = {} ;
+    this.tickHold = this.tickHold.bind(this);
+    this.triggerUpdate = this.triggerUpdate.bind(this);
+    this.triggerDnd = this.triggerDnd.bind(this);
+    this.handlers = handlers || {} ;
+  }
+
+  /** @summary Update the D&D event handlers.
+      @param {object} [handlers] - D&D events callbacks
+      @description
+      Replace the event handlers by those specified in the `handlers` object.
+      Not mentionned callbacks are left unchanged.
+      If the `null` object is given, all current handlers are removed.
+  */
+  setHandlers( handlers ) {
+    this.handlers =
+      handlers === null ? {} : Object.assign( this.handlers, handlers );
+  }
+
+  //--- Registering
+
+  /** @summary Registers a target into the D&D controller.
+      @param {React.ref} ref - a referrence on the drop target DOM element
+      @param {function} callback - generic D&D events callback
+      @return {ident} the target identifier
+      @description
+      You normally don't call this method by yoursef, it is automatically
+      called when `<DropTarget/>` are mounted in the DOM.
+  */
+  register( ref, callback )
+  {
+    if (!ref) return undefined;
+    const id = 'DOME$' + (++this.kid) ;
+    this.targets[id] = { id, ref , callback } ;
+    return id;
+  }
+
+  //--- Updating
+
+  /** @summary Update the event handlers associated to a drop target.
+      @param {ident} id - the drop target identifier
+      @param {React.ref} ref - a referrence on the drop target DOM element
+      @param {function} callback - generic D&D events callback
+      @description
+      You normally don't call this method by yoursef, it is automatically
+      called when `<DropTarget/>` are updated. If the target data is falsy, the target identifier
+      is used instead.
+  */
+  update( id, ref, callback ) {
+    if (this.targets[id]) {
+      this.targets[id] = { id, ref, callback };
+    }
+  }
+
+  //--- Un-Registering
+
+  /** @summary Un-register a target from the D&D controller.
+      @param {ident} id - the drop target identifier to remove
+      @description
+      You normally don't call this method by yoursef, it is automatically
+      called when `<DropTarget/>` are unmounted from the DOM.
+  */
+  remove( id ) {
+    delete this.targets[id] ;
+  }
+
+  //--- Generic Mouse Event
+
+  handleMouseEvent( evt, x, y ) {
+    const d = this.dragging ;
+    d.meta = evt.ctrKey || evt.altKey || evt.metaKey ;
+    d.deltaX = x - this.rootX ;
+    d.deltaY = y - this.rootY ;
+    d.position = {x,y} ;
+    const ex = d.clientX = evt.clientX ;
+    const ey = d.clientY = evt.clientY ;
+    d.targetClientRect = undefined ;
+    this.leaved = this.target ;
+    this.leavedHandler = this.targetHandler ;
+    this.target = undefined ;
+    this.targetHandler = undefined ;
+    const elts = document.elementsFromPoint(ex,ey);
+    elts.find((elt) => {
+      const target = _.find( this.targets, (tgt) => tgt.ref.current === elt );
+      if (target) {
+        const { id, callback } = target ;
+        this.target = id ;
+        this.targetHandler = target.callback ;
+        if (id === this.leaved) this.leavedHandler = undefined ;
+        d.targetClientRect = elt.getBoundingClientRect();
+        return true;
+      }
+      return false;
+    });
+  }
+
+  //--- Starting D&D sequence
+
+  /** @summary Initiates a new drag.
+      @param {Event} evt - the mouse event originating the drag
+      @param {number} x - initial horizontal coordinate
+      @param {number} y - initial vertical coordinate
+      @param {Element} node - the source DOM element
+      @param {function} callback - generic D&D events callback
+      @description
+You normally don't call this method by sourself, it is automatically
+called by `DragSource` objects.
+
+Initial `(x,y)` coordinates can be in any coordinate system, they
+are only used to compute relative offsets of further D&D events.
+They can be typically taken from React-Draggable events data.
+
+The generic callback shall handle two kind of events:
+ - `callback()` when the global D&D state has been updated;
+ - `callback(Dragging)` when some D&D event is triggered.
+
+Generally, you shall `forceUpdate()` your component in the first case,
+and `dispatchEvent()` the event to D&D listeners in the second case.
+
+  */
+  handleStart( evt, x, y, node, callback ) {
+    if (this.dragging) {
+      this.triggerDnd('STOP');
+      if (this.timer) clearInterval(this.timer);
+    }
+    this.state = {};
+    this.dragging = {
+      sourceClientRect: node.getBoundingClientRect()
+    };
+    this.sourceHandler = callback ;
+    this.targetHandler = undefined ;
+    this.leavedHandler = undefined ;
+    this.rootX = x;
+    this.rootY = y;
+    this.lastX = x;
+    this.lastY = y;
+    this.ticks = 0;
+    this.handleMouseEvent( evt, x, y );
+    this.triggerDnd( 'START' );
+    this.timer = setInterval( this.tickHold , HOLD_TIME );
+  }
+
+  //--- Dragging D&D sequence
+
+  /** @summary Dispatch a drag-event amoung registered drop targets.
+      @param {Event} evt - the dragging mouse event
+      @param {number} x - the horizontal coordinate
+      @param {number} y - the vertical coordinate
+      @description
+You normally don't call this method by sourself, it is automatically
+called by `DragSource` objects.
+
+The `(x,y)` coordinates shall be in the same coordinate system than
+when starting the D&D sequence. It is only used to compute relative
+the offsets of the associated D&D event.
+  */
+  handleDrag( evt, x, y ) {
+    let kind = 'DRAG' ;
+    let delta = Math.abs( x - this.lastX ) + Math.abs( y - this.lastY );
+    this.lastX = x ;
+    this.lastY = y ;
+    if (delta > HOLD_MOVE) {
+      this.ticks = 0 ;
+      const d = this.dragging ;
+      if (d.held) {
+        d.held = false ;
+        kind = 'MOVE' ;
+      }
+    }
+    this.handleMouseEvent( evt, x, y );
+    const target = this.target ;
+    const leaved = this.leaved ;
+    if (target !== leaved) {
+      if (target && !leaved) kind = 'ENTER' ; else
+        if (!target && leaved) kind = 'LEAVE' ; else
+          if (target && leaved) kind = 'SWITCH' ;
+    }
+    this.triggerDnd(kind);
+  }
+
+  //--- Stopping D&D sequence
+
+  /** @summary Dispatch a drop-event over registered drop targets.
+      @param {Event} evt - the dropping mouse event
+      @param {number} x - the horizontal coordinate
+      @param {number} y - the vertical coordinate
+      @description
+You normally don't call this method by sourself, it is automatically
+called by `DragSource` objects.
+
+The `(x,y)` coordinates shall be in the same coordinate system than
+when starting the D&D sequence. It is only used to compute relative
+the offsets of the associated D&D event.
+  */
+  handleStop( evt, x, y ) {
+    clearInterval( this.timer );
+    this.handleMouseEvent( evt, x, y );
+    this.triggerDnd(this.target ? 'DROP' : 'STOP');
+    this.dragging = undefined ;
+    this.state = undefined ;
+    this.timer = undefined;
+    this.ticks = undefined;
+    this.triggerUpdate();
+  }
+
+  //--- Hold Detection
+
+  tickHold() {
+    if (this.dragging && (this.ticks++) > HOLD_FIRE)
+    {
+      this.dragging.held = true ;
+      this.triggerDnd('HOLD');
+      this.ticks = 0 ;
+    }
+  }
+
+  //--- Update State
+
+  /**
+     @summary Update the controller state.
+     @parameter {object} update - the data to update the state with
+     @description
+     Update the state with the given updates, like React component states.
+     This will force the drag source target and drop target components to re-render.
+  */
+  setState(update) {
+    const state = this.state ;
+    if (state && update) {
+      this.state = Object.assign( state, update );
+      if (!this.dirty) {
+        this.dirty = true ;
+        setImmediate( this.triggerUpdate );
+      }
+    }
+  }
+
+  triggerUpdate() {
+    this.dirty = false ;
+    notify( this.leavedHandler ); // always different from target
+    notify( this.sourceHandler );
+    notify( this.targetHandler );
+    notify( this.handlers.onUpdate , this.state );
+  }
+
+  //--- Trigger Dragging Events
+
+  triggerDnd(move) {
+    const d = this.dragging ;
+    d.move = move ;
+    switch( move ) {
+    case 'ENTER':
+      notify( this.sourceHandler, d );
+      notify( this.targetHandler, d );
+      break;
+    case 'LEAVE':
+      notify( this.leavedHandler, d );
+      notify( this.sourceHandler, d );
+      break;
+    case 'SWITCH':
+      d.move = 'LEAVE'  ; notify( this.leavedHandler, d );
+      d.move = 'SWITCH' ; notify( this.sourceHandler, d );
+      d.move = 'ENTER'  ; notify( this.targetHandler, d );
+      d.move = 'SWITCH' ;
+      break;
+    default:
+      notify( this.leavedHandler, d ); // allways different from target
+      notify( this.sourceHandler, d );
+      notify( this.targetHandler, d );
+      break;
+    }
+    dispatchEvent( d, this.handlers );
+  }
+
+}
+
+// --------------------------------------------------------------------------
+// --- Drag Overlay
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @summary Display a Drag Source with an Overlay.
+   @property {Dragging} [dragging] - current dragging event
+   @property {string} [className] - source content class
+   @property {object} [style] - source content style
+   @property {object} [draggedStyle] - additional style for the source placeholder
+   @property {object} [draggingStyle] - additional style for the source being dragged
+   @property {number} [offsetX] - horizontal offset during drag (default 4)
+   @property {number} [offsetY] - vertical offset during drag (default 4)
+   @property {number} [zIndex] - fixed positioning during drag (default 1)
+   @property {any} [...divProps] - other properties to inject in the root element
+   @property {React.children|function} [children] - drag source content
+   @description
+   When dragged, a source drag is generally blit by its embedding container.
+   Using z-index CSS property might be a solution, but this is generally not
+   enough to escape from a positionned parent  `<div>` element.
+
+   The alternative is to separate the internal content of
+   a drag source from its outer `<div>`, and to use fixed positionning
+   during a D&D sequence.
+
+   The `<Overlay>` component implements this behaviour. The root element is
+   normally laid out with the `className`, `style` and `divProps` properties and
+   its internal content is wrapped inside an internal `<div>` container.
+
+   During dragging, this normal layout is slightly altered, without causing
+   extra React mounting or unmounting.  The outer `<div>` is styled with its
+   original class plus the `'dome-dragged'` class which provides by default a
+   light-blue background and invisible border style.  The inner class is styled
+   with its original class plus the `dome-dragging` class, canceled margins and
+   fixed positionning with respect to the dragging event.  You may use those
+   additinal classes to add conditional styling when dragging.
+
+   Additionnaly, when dragged around, the source content is offsets
+   with `offsetX` and `offsetY`, which defaults to 4 pixels each.
+
+   Finally, you may supply a custom rendering function for rendering the drag
+   source content, which is fed with the current dragging event.
+ */
+
+export const Overlay = ({
+  dragging, className='', style, styleDragged, styleDragging,
+  offsetX=4, offsetY=4, insetX=0, insetY=0,
+  zIndex=1,
+  children,
+  ...divProps
+}) => {
+  let outerClass,outerStyle,innerStyle,innerClass ;
+  if (dragging) {
+    const { left, top, width, height } = dragging.sourceClientRect ;
+    const position = {
+      position: 'fixed',
+      left:   left + offsetX + dragging.deltaX,
+      top:    top  + offsetY + dragging.deltaY,
+      width, height, zIndex, margin: 0
+    };
+    const holder = {
+      width, height
+    };
+    outerClass = 'dome-dragged ' + className ;
+    innerClass = 'dome-dragging ' + className ;
+    outerStyle = Object.assign( {}, style, styleDragged, holder );
+    innerStyle = Object.assign( {}, style, styleDragging, position );
+  } else {
+    outerClass = className ;
+    outerStyle = style ;
+    innerClass = undefined ;
+    innerStyle = undefined ;
+  }
+  return (
+    <div className={outerClass} style={outerStyle} {...divProps}>
+      <div className={innerClass} style={innerStyle}>
+        {typeof(children)==='function' ? children(dragging) : children}
+      </div>
+    </div>
+  );
+};
+
+// --------------------------------------------------------------------------
+// --- Drag Sources
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @summary Drag Source Component.
+   @property {DnD} [dnd] - the associated D&D controller
+   @property {string} [handle] - DOM selector initiating the drag (eg. `'.handle'`)
+   @property {boolean} [disabled] - cancel D&D events if non falsy value
+   @property {object} [draggable] - additional properties for the internal react-draggable wrapper
+   @property {function} [onDnd] - callback to D&D generic events
+   @property {function} [onXxx] - callback to other D&D specific events
+   @property {string} [className] - the class of the internal 'div' component (unless custom rendering)
+   @property {object} [style] - the style of the internal 'div' component (unless custom rendering)
+   @property {object} [position] - relative position of the internal 'div' component (unless custom rendering)
+   @property {boolean|object} [overlay] - use an `Overlay` to display the content
+   @property {React.children|function} children - element content or custom rendering function
+   @description
+
+Creates a drag source element and manage its dragging state. The component
+is rendered by an internal `<div>` element with the given class and style with specific
+handlers attached in. You can use the `'position'` property to modify its relative position
+_after_ a D&D sequence, the position being `{x:0,y:0}`. This position is set relative to the normal
+positioning of the internal `<div>` element, as specified by the provided class and style properties.
+Notice that during dragging, your component is attached an additional `'dome-dragging'` class name that
+you can use to alter its rendering.
+
+Alternatively, you may provide a custom rendering function as the unique children of the Drag Source element.
+The function will be given the current D&D dragging events, or `undefined` when outside a D&D sequence.
+The custom rendering _shall_ return a single root element supporting property injection in order for the Drag
+Source element to attach its event handlers into it. You are then fully responsible for rendering and positioning
+the element both _during_ and _outside_ of a D&D sequence. No internal `<div>` element is inserted for you,
+and the `className`, `style` and `position` properties are ignored.
+
+Finally, you may also use an [Overlay](module-dome_dnd.Overlay.html) component to render
+your content, by setting `overlay=true` or passing `overlay` the desired properties.
+
+When dragged, the [DnD](module-dome_dnd.DnD.html) controller is informed and dispatches
+the dragging events to the registered drop targets. The `handle` selector can be used
+to identify which parts of the content may initiate the drag. By default, any drag event
+may initiate the drag.
+
+The `onDnd` event listener receives all D&D events associated to this drop target.
+See [event callbacks](module-dome_dnd.html)
+documentation for more details on other specific event listeners.
+
+The `draggable` object, when provided, is used to inject additional properties
+into the internal `Draggable` or `DraggableCore` wrapper used to manage D&D events.
+By default, a full featured `Draggable` component is used.
+When a custom rendering function is specified, a `DraggableCore` is used instead.
+See [react-draggable](https://github.com/mzabriskie/react-draggable)
+documentation for more details.
+
+*/
+
+export class DragSource extends React.Component
+{
+  constructor(props)
+  {
+    super(props);
+    this.onStart = this.onStart.bind(this);
+    this.onDrag = this.onDrag.bind(this);
+    this.onStop = this.onStop.bind(this);
+    this.handleDnd = this.handleDnd.bind(this);
+    this.state = { };
+  }
+
+  componentDidMount() {
+    this.mounted = true;
+  }
+
+  componentWillUnmount() {
+    this.mounted = false;
+  }
+
+  onStart(evt,drag) {
+    const { dnd, disabled } = this.props;
+    if (dnd) dnd.handleStart(evt,drag.x,drag.y,drag.node,this.handleDnd);
+  }
+
+  onDrag(evt,drag) {
+    const { dnd, disabled } = this.props ;
+    if (dnd) dnd.handleDrag(evt,drag.x,drag.y);
+  }
+
+  onStop(evt,drag) {
+    const { dnd, disabled } = this.props ;
+    if (dnd) dnd.handleStop(evt,drag.x,drag.y);
+  }
+
+  handleDnd(dragging) {
+    if (!this.mounted) return;
+    if (!dragging)
+      this.forceUpdate();
+    else {
+      dispatchEvent( dragging, this.props );
+      switch(dragging.move) {
+      case 'STOP':
+      case 'DROP':
+        this.setState({dragging:undefined});
+        break;
+      default:
+        this.setState({dragging});
+        break;
+      }
+    }
+  }
+
+  render() {
+    const { dnd, disabled, className, style,
+            handle, draggable, overlay,
+            children } = this.props ;
+    if (overlay || typeof(children)==='function') {
+      const dragging = this.state.dragging ;
+      return (
+        <DraggableCore
+          handle={handle}
+          disabled={!dnd || disabled}
+          onStart={this.onStart}
+          onDrag={this.onDrag}
+          onStop={this.onStop}
+          {...draggable}
+          >
+          { overlay ?
+            (
+              <Overlay
+                dragging={dragging}
+                className={className} style={style}
+                {...overlay} >
+                {children}
+              </Overlay>
+            )
+            : children(dragging)
+          }
+        </DraggableCore>
+      );
+    } else {
+      const { className, style, position={x:0,y:0} } = this.props ;
+      return (
+        <Draggable
+          handle={handle}
+          disabled={!dnd || disabled}
+          position={position}
+          defaultClassName=''
+          defaultClassNameDragged=''
+          defaultClassNameDragging='dome-dragging'
+          onStart={this.onStart}
+          onDrag={this.onDrag}
+          onStop={this.onStop}
+          {...draggable}
+          >
+          <div className={className} style={style}>{children}</div>
+        </Draggable>
+      );
+    }
+  }
+
+}
+
+// --------------------------------------------------------------------------
+// --- Drop Target
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @summary Drop Target Container.
+   @property {DnD} [dnd] - the associated D&D Controller
+   @property {function} [onDnd] - callback to D&D generic events
+   @property {function} [onXxx] - callback to other D&D specific events
+   @property {string} [className] - class name(s) of the internal `<div>` element (unless custom rendering)
+   @property {object} [style] - style property for the internal `<div>` element (unless custom rendering)
+   @property {React.ref} [targetRef] - the React ref to use for identifying the target DOM element
+   @property {React.children|function} children - element content or custom rendering function
+   @description
+
+Crates a drop target container and register its callbacks into the specified
+[DnD](module-dome_dnd.DnD.html) controller. In case the DnD controller is undefined,
+the drag events are just disabled.
+
+The drop target container must inform the D&D controller of its DOM element that is responsible
+for reacting to dragging events. By default, the drop target element renders its children elements
+inside an internal `<div>` element with the specified class ans style properties. Notice that, during
+a D&D sequence, the `dome-dragging` class name is added to this `<div>` element and you can use this selector
+for your styling.
+
+Alternatively, you may provide a custom rendering function as the unique children of the Drag Source element.
+Your rendering function will receive the current D&D dragging of the D&D sequence, if any.
+The [targetRef] property shall be used to identify the target DOM element, otherwize, the drop target
+will not respond to dragging events.
+In case of custom rendering, the `className` and `style` properties are ignored.
+
+During a D&D sequence, the `onDnd` event listener receives all D&D events associated
+to this drop target. See [event callbacks](module-dome_dnd.html)
+documentation for more details on other specific event listeners.
+
+*/
+export class DropTarget extends React.Component
+{
+
+  constructor(props) {
+    super(props);
+    this.targetRef = this.props.targetRef || React.createRef() ;
+    this.handleDnd = this.handleDnd.bind(this);
+    this.state = {};
+  }
+
+  componentDidMount() {
+    const { dnd } = this.props ;
+    this.id = dnd && dnd.register( this.targetRef, this.handleDnd );
+    this.mounted = true ;
+  }
+
+  componentDidUpdate() {
+    const { dnd } = this.props ;
+    dnd && dnd.update(this.id, this.targetRef, this.handleDnd );
+  }
+
+  componentWillUnmount() {
+    const { dnd } = this.props ;
+    dnd && dnd.remove( this.id );
+    this.mounted = false ;
+  }
+
+  handleDnd(dragging) {
+    if (!this.mounted) return;
+    if (!dragging)
+      this.forceUpdate();
+    else {
+      dispatchEvent( dragging, this.props );
+      switch(dragging.move) {
+      case 'STOP':
+      case 'DROP':
+      case 'LEAVE':
+        this.setState({dragging:undefined});
+        break;
+      default:
+        this.setState({dragging});
+        break;
+      }
+    }
+  }
+
+  render() {
+    const targetRef = this.targetRef ;
+    const { children } = this.props ;
+    const { dragging } = this.state ;
+    if (typeof(children)==='function')
+      return children(dragging);
+    else {
+      let { className='', style } = this.props ;
+      return (
+        <div ref={targetRef}
+             className={ dragging ? className + ' dome-dragging' : className }
+             style={style} >
+          {children}
+        </div>
+      );
+    }
+  }
+
+}
+
+// --------------------------------------------------------------------------
+// --- Export
+// --------------------------------------------------------------------------
+
+export default {
+  DnD, DragSource, DropTarget, dispatchEvent
+} ;
+
+// --------------------------------------------------------------------------
diff --git a/ivette/src/dome/src/renderer/dome.css b/ivette/src/dome/src/renderer/dome.css
new file mode 100644
index 0000000000000000000000000000000000000000..99cb9265aaf25c94f992f721246303c6797b0af6
--- /dev/null
+++ b/ivette/src/dome/src/renderer/dome.css
@@ -0,0 +1,131 @@
+/* -------------------------------------------------------------------------- */
+/* --- Main Dome Styles                                                   --- */
+/* -------------------------------------------------------------------------- */
+
+* {
+    user-select: none;
+    box-sizing: border-box;
+    margin: 0 ;
+    padding: 0 ;
+  }
+
+body {
+    overflow: hidden ;
+    position: fixed ;
+    font-family: sans-serif ;
+    font-size: 13px ;
+    color: #333333 ;
+    background: #f0f0f0 ;
+    top: 0 ;
+    bottom: 0 ;
+    left: 0 ;
+    right: 0 ;
+}
+
+#app {
+    width: 100% ;
+    height: 100% ;
+}
+
+/* -------------------------------------------------------------------------- */
+/* --- Frame Colors                                                       --- */
+/* -------------------------------------------------------------------------- */
+
+.dome-window-active .dome-color-frame {
+    fill:         #606060 ;
+    color:        #606060 ;
+    border-color: #afafaf ;
+    background:   #d8d8d8 ;
+}
+
+.dome-window-inactive .dome-color-frame {
+    fill:         #b0b0b0 ;
+    color:        #b0b0b0 ;
+    border-color: #d6d6d6 ;
+    background:   #f6f6f6 ;
+}
+
+.dome-window-active .dome-color-selected {
+    border-color: #e8e8e8 ;
+    background:   #ff9504 ;
+    color:        #ffffff ;
+}
+
+.dome-window-inactive .dome-color-selected {
+    border-color: #e8e8e8 ;
+    background:   #d8d8d8 ;
+}
+
+.dome-color-dragzone {
+    opacity: 0.0 ;
+    background: transparent ;
+    transition: opacity .1s linear 0.1s , background .1s linear 0.1s ;
+}
+
+.dome-color-dragzone:hover {
+    background: #808080 ;
+    opacity: 0.2 ;
+    transition: opacity .1s linear 0.1s , background .1s linear 0.1s ;
+}
+
+.dome-color-dragging {
+    background: #64b4f0 ;
+    opacity: 0.5 ;
+    transition: opacity .1s linear 0.1s , background .1s linear 0.1s ;
+}
+
+div.dome-dragged {
+    background: lightblue ;
+    border: none ;
+}
+
+.dome-dragging * {
+    cursor: move ;
+}
+
+/* -------------------------------------------------------------------------- */
+/* --- Text Properties                                                    --- */
+/* -------------------------------------------------------------------------- */
+
+.dome-text-label {
+    font-family: sans-serif ;
+    user-select: none ;
+    white-space: nowrap ;
+    text-overflow: ellipsis ;
+}
+
+.dome-text-title {
+    font-family: sans-serif ;
+    font-size: larger ;
+    font-weight: bolder ;
+    user-select: none ;
+    white-space: nowrap ;
+    text-overflow: ellipsis ;
+}
+
+.dome-text-descr {
+    font-family: sans-serif ;
+    font-size: smaller ;
+    user-select: none ;
+    white-space: wrap ;
+    text-overflow: ellipsis ;
+}
+
+.dome-text-data {
+    cursor: text ;
+    user-select: text ;
+    font-family: sans-serif ;
+    white-space: nowrap ;
+    text-overflow: clip ;
+}
+
+.dome-text-code {
+    cursor: text ;
+    user-select: text ;
+    font-family: 'Andale mono', monospace ;
+    font-size: 9pt ;
+    white-space: nowrap ;
+    text-overflow: clip ;
+}
+
+/* -------------------------------------------------------------------------- */
diff --git a/ivette/src/dome/src/renderer/dome.js b/ivette/src/dome/src/renderer/dome.js
new file mode 100644
index 0000000000000000000000000000000000000000..d16ab7846991d5b78f6c061be4759d3fc35d8e5f
--- /dev/null
+++ b/ivette/src/dome/src/renderer/dome.js
@@ -0,0 +1,1062 @@
+/**
+   @module dome(renderer)
+   @description
+
+   ## Dome Application (Renderer Process)
+
+   This modules manages your main application window
+   and its interaction with the main process.
+
+   @example // File 'src/renderer/index.js':
+   import Dome from 'dome';
+   import Application from './Application.js' ;
+   Dome.setContent( Application );
+ */
+
+import _ from 'lodash' ;
+import React from 'react';
+import ReactDOM from 'react-dom';
+import { AppContainer } from 'react-hot-loader' ;
+import { remote , ipcRenderer } from 'electron';
+import System from 'dome/system' ;
+import './dome.css' ;
+
+// --------------------------------------------------------------------------
+// --- Context
+// --------------------------------------------------------------------------
+
+// main window focus
+var focus = true ;
+
+function setContextAppNode()
+{
+  const node = document.getElementById('app');
+  if (node) {
+    node.className =
+      'dome-container dome-platform-' + System.platform +
+      ( focus ? ' dome-window-active' : ' dome-window-inactive' ) ;
+  }
+  return node;
+}
+
+// --------------------------------------------------------------------------
+// --- Helpers
+// --------------------------------------------------------------------------
+
+/** @summary Development mode flag.
+    @description
+    Configured to be `'true'` when in development mode
+*/
+export const DEVEL = System.DEVEL ;
+
+/** @summary System platform.
+    @description
+    Same as [`platform`](module-dome_system.html#.platform) from `dome/system` */
+export const platform = System.platform ;
+
+// --------------------------------------------------------------------------
+// --- Application Emitter
+// --------------------------------------------------------------------------
+
+import { EventEmitter } from 'events' ;
+
+/** @summary Application Emitter.
+    @description
+    Can be used as a basic _Flux_ dispatcher. */
+export const emitter = new EventEmitter();
+
+/** Same as `emitter.on` */
+export function on(evt,job) { emitter.on(evt,job); }
+
+/** Same as `emitter.off` */
+export function off(evt,job) { emitter.off(evt,job); }
+
+/** Same as `emitter.emit` */
+export function emit(evt,...args) { emitter.emit(evt,...args); }
+
+// --------------------------------------------------------------------------
+// --- Application Events
+// --------------------------------------------------------------------------
+
+/** @event 'dome.update'
+    @description
+    Convenient pre-defined events for triggering a global re-render.
+    See also [Dome.onUpdate](#.onUpdate), [Dome.update](#.update) methods and
+    the [Dome.useUpdate](#.useUpdate) hook.
+*/
+
+/** @event 'dome.reload'
+    @description
+    Triggered when the application has been loaded or re-loaded
+    See also [Dome.onReload](#.onReload).
+*/
+
+/** @event 'dome.command'
+    @param {Array.<string>} argv - command line arguments
+    @param {string} wdir - working directory
+    @description
+    Triggered when the command line argument has been received, and when
+    the application is re-loaded (in development mode).
+
+    See also [Dome.onCommand](#.onCommand).
+*/
+
+/**
+   @summary Emits the `dome.update` event.
+*/
+export function update() { emitter.emit('dome.update'); }
+
+/**
+   @summary Update event handler.
+   @param {function} cb - invoked on update events.
+   @description
+   Register a callback on [dome.update](#~event:'dome.update') event.
+*/
+export function onUpdate(job) { emitter.on('dome.update',job); }
+
+/**
+   @summary Update event handler.
+   @param {function} cb - invoked on reload events.
+   @description
+   Register a callback on [dome.reload](#~event:'dome.reload') event.
+*/
+export function onReload(job) { emitter.on('dome.reload',job); }
+
+/** @summary Command-line event handler.
+    @param {function} cb - invoked with `cb(argv,wdir)`
+    @description
+Register a callback on [dome.command](#~event:'dome.reload') event,
+emitted by the `Main` process when the application instance is launched.
+
+See also:
+ - [Dome.useCommand](#.useCommand)
+ - [System.getArguments](module-dome_system.html#.getArguments)
+ - [System.getWorkingDir](module-dome_system.html#.getWorkingDir)
+*/
+export function onCommand(job) { emitter.on('dome.command',job); }
+
+ipcRenderer.on('dome.ipc.reload',() => emitter.emit('dome.reload'));
+ipcRenderer.on('dome.ipc.command', (_event,argv,wdir) => {
+  System.setCommand(argv,wdir);
+  emitter.emit('dome.command',argv,wdir);
+});
+
+// --------------------------------------------------------------------------
+// --- Main-Process Communication
+// --------------------------------------------------------------------------
+
+// --------------------------------------------------------------------------
+// --- Window Management
+// --------------------------------------------------------------------------
+
+export function isApplicationWindow()
+{
+  return process.argv.includes( System.WINDOW_APPLICATION_ARGV );
+}
+
+export function isPreferencesWindow()
+{
+  return process.argv.includes( System.WINDOW_PREFERENCES_ARGV );
+}
+
+// --------------------------------------------------------------------------
+// --- Window Title
+// --------------------------------------------------------------------------
+
+export function setModified( modified )
+{
+  ipcRenderer.send('dome.ipc.window.modified',modified);
+}
+
+export function setTitle( title )
+{
+  ipcRenderer.send('dome.ipc.window.title',title);
+}
+
+// --------------------------------------------------------------------------
+// --- Main Content
+// --------------------------------------------------------------------------
+
+/**
+   @summary Defines the user's main window content.
+   @param {React.Component} Component - to be rendered in the main window
+   @description
+   Binds the component to the main window.
+
+   <strong>Notes:</strong> a `<Component/>` instance is generated and rendered in the `#app`
+   window element. Its class name is set to `dome-platform-<platform>` with
+   the `<platform>` set to the `Dome.platform` value. This class name can be used
+   as a CSS selector for platform-dependent styling.
+*/
+export function setApplicationWindow( Component )
+{
+  if (isApplicationWindow()) {
+    syncSettings();
+    const appNode = setContextAppNode();
+    ReactDOM.render( <AppContainer><Component/></AppContainer> , appNode );
+  }
+}
+
+// --------------------------------------------------------------------------
+// --- Settings Window
+// --------------------------------------------------------------------------
+
+/**
+   @summary Defines the user's preferences window content.
+   @param {React.Component} Component - to be rendered in the settings window
+   @description
+   Binds the component to the settings window.
+
+   <strong>Notes:</strong> a `<Component/>` instance is generated and rendered in the `#app`
+   window element. Its class name is set to `dome-platform-<platform>` with
+   the `<platform>` set to the `Dome.platform` value. This class name can be used
+   as a CSS selector for platform-dependent styling.
+*/
+export function setPreferencesWindow( Component )
+{
+  if (isPreferencesWindow()) {
+    syncSettings();
+    const appNode = setContextAppNode();
+    ReactDOM.render( <AppContainer><Component/></AppContainer> , appNode );
+  }
+}
+
+// --------------------------------------------------------------------------
+// --- MenuBar Management
+// --------------------------------------------------------------------------
+
+const customItemCallbacks = {} ;
+
+/**
+   @summary Create a new custom menu in the menu bar.
+   @param {string} label - the menu title (shall be unique)
+   @description
+   This function can be triggered at any time, and will eventually trigger
+   an update of the whole application menubar.
+
+   It is also possible to call this function from the main process.
+*/
+export function addMenu( label ) { ipcRenderer.send( 'dome.ipc.menu.addmenu' , label ); }
+
+/**
+   @summary Insert a new custom item in a menu.
+   @param {object} spec - the menu-item specification
+   @description
+The menu-item shall be specified by using the following fields:
+ - `menu` (`string`, _required_) : the label of the menu to insert the item in;
+   can be a custom menu, or one of the predefined `'File'`, `'Edit'` or `'View'` menus.
+ - `id` (`string|number`, _required_) : the item identifier;
+   shall be unique among the entire menu-bar.
+ - `type` (`string`, _optional_) : one of `'normal'`, `'separator'`, `'checkbox'` or `'radio'`.
+ - `label` (`string`, _optional_) : the item label.
+ - `visible` (`boolean`, _optional_, default is `true`).
+ - `enabled` (`boolean`, _optional_, default is `true`).
+ - `checked` (`boolean`, _optional_, for `type:'checkbox'` and `type:'radio'` only, default is `false`).
+ - `key` (`string`, _optional_) : a keyboard shortcut for menu-item.
+ - `onClick` (`function`, _optional_) : an optional callback.
+
+These options (except `menu` and `id`) can be modified later on by using the [setMenuItem](#.setMenuItem) function.
+
+When clicked, the menu-item will also trigger a `'dome.menu.clicked'` event on the entire application (both process)
+with the corresponding `id`.
+
+Key short cuts shall be specified with the following codes:
+ - `"Cmd+<Key>"` for command (MacOS) or control (Linux) key
+ - `"Alt+<Key>"` for command+option (MacOS) or alt (Linux) key
+ - `"Meta+<Key>"` for command+shift (MacOS) or control+alt (Linux) key
+
+Alternatively, more precise keybord shortcuts can be specified with the `'accelerator'` option,
+which follows the same encoding that menu-item specifications from Electron.
+
+The `addMenu` function can be triggered at any time, and will eventually trigger
+an update of the whole application menubar.
+It is also possible to call this function from the main process.
+
+*/
+export function addMenuItem( spec )
+{
+  if (!spec.id && spec.type !== 'separator') {
+    console.error('[Dome] Missing menu-item identifier',spec);
+    return;
+  }
+  const { onClick , ...options } = spec ;
+  if ( onClick ) customItemCallbacks[ spec.id ] = onClick ;
+  ipcRenderer.send( 'dome.ipc.menu.addmenuitem' , options );
+}
+
+/**
+   @summary Update properties of an existing menu-item.
+   @param {object} options - the menu-item specification to update
+   @description
+   Options must follow the specification of the [addMenuItem](#.addMenuItem) function.
+   Option `id` must specify the identifier of the menu item to update.
+   The menu and item positions can _not_ be modified.
+   If an `onClick` callback is specified, it will _replace_ the previous one.
+   You shall specify `null` to remove the previously registered callback
+   (`undefined` callback is ignored).
+
+   This function can be triggered at any time, and will possibly trigger
+   an update of the whole application menubar if the properties
+   can not be changed dynamically in Electron.
+
+   It is also possible to call this function from the main process.
+   When specified, the item callback is only invoked in the process which
+   specify it. To register callbacks in other process,
+   you shall listen to the `'dome.menu.clicked'` event.
+ */
+export function setMenuItem( options ) {
+  if (!options.id) {
+    console.error('[Dome] Missing menu-item identifier',options);
+    return;
+  }
+  const { onClick , ...updates } = options ;
+  if (onClick !== undefined) {
+    if (onClick) customItemCallbacks[options.id] = onClick ;
+    else delete customItemCallbacks[options.id] ;
+  }
+  ipcRenderer.send( 'dome.ipc.menu.setmenuitem', updates );
+}
+
+/** @event 'dome.menu.clicked'
+    @description Emitted with the clicked menu-item identifier */
+
+ipcRenderer.on('dome.ipc.menu.clicked',(id) => {
+  const callback = customItemCallbacks[id] ;
+  callback && callback();
+});
+
+// --------------------------------------------------------------------------
+// --- Context Menus
+// --------------------------------------------------------------------------
+
+/**
+   @summary Popup a contextual menu.
+   @param {item[]} items - the array of menu items
+   @param {function} [callback] - an optional callback
+   @description
+Each menu item is specified by an object with the following fields:
+ - `id` (`string|number`, _optional_) : the item identifier.
+ - `label` (`string`, _optional_) : the item label.
+ - `enabled` (`boolean`, _optional_, default is `true`).
+ - `display` (`boolean`, _optional_, default is `true`).
+ - `checked` (`boolean`, _optional_, default is `undefined`).
+ - `onClick` (`function`, _optional_) : callback on item selection.
+
+Items can be separated by inserting a `'separator'` constant string
+in the array. Item identifier and label default to each others. Alternatively,
+an item can be specified by a single string that will be used for both
+its label and identifier. Undefined or null items are allowed (and skipped).
+
+The menu is displayed at the current mouse location.
+The callback is called with the selected item identifier or label.
+If the menu popup is canceled by the user, the callback is called with `undefined`.
+
+@example
+let myPopup = (_evt) => Dome.popupMenu([ …items… ],(id) => … );
+<div onRightClick={myPopup}>...</div>
+
+*/
+export function popupMenu( items, callback )
+{
+  const { Menu , MenuItem } = remote ;
+  const menu = new Menu();
+  var selected = undefined ;
+  var kid = 0 ;
+  items.forEach((item) => {
+    if (item === 'separator')
+      menu.append(new MenuItem({ type:'separator' }));
+    else if (item)
+    {
+      const { display=true, enabled, checked } = item ;
+      if (display) {
+        const label = item.label || '#'+(++kid) ;
+        const id = item.id || label ;
+        const click = () => {
+          selected = id ;
+          item.onClick && item.onClick();
+        };
+        const type = checked !== undefined ? 'checkbox' : 'normal' ;
+        menu.append(new MenuItem({ label, enabled, type, checked, click }));
+      }
+    }
+  });
+  const job = callback ? () => callback( selected ) : undefined ;
+  menu.popup({window: remote.getCurrentWindow(), callback:job });
+}
+
+// --------------------------------------------------------------------------
+// --- Settings
+// --------------------------------------------------------------------------
+
+var globals = {} ;
+var globalPatches = {} ;
+
+var settings = {} ;
+var settingsPatches = {} ;
+
+// initial values => synchronized event
+function syncSettings() {
+  const fullSettings = ipcRenderer.sendSync('dome.ipc.settings.sync');
+  globals = fullSettings.globals ;
+  settings = fullSettings.settings ;
+}
+
+const readSetting = ( local, key, defaultValue ) => {
+  const value = _.get( local ? settings : globals , key );
+  return value === undefined ? defaultValue : value ;
+};
+
+const writeSetting = ( local, key, value ) => {
+  if (key) {
+    const theValue = value===undefined ? null : value ;
+    const store = local ? settings : globals ;
+    const patches = local ? settingsPatches : globalPatches ;
+    _.set( store, key, theValue );
+    _.set( patches,  key, theValue );
+    emitter.emit('dome.settings');
+    if (local) {
+      if (DEVEL) fireSaveSettings();
+    } else {
+      fireSaveGlobals();
+    }
+  }
+};
+
+const fireSaveSettings = _.debounce(
+  () => {
+    if (!_.isEmpty(settingsPatches)) {
+      ipcRenderer.send( 'dome.ipc.settings.window', settingsPatches ) ;
+      settingsPatches = {} ;
+    }
+  }, 200
+);
+
+const fireSaveGlobals = _.debounce(
+  () => {
+    if (!_.isEmpty(globalPatches)) {
+      ipcRenderer.send( 'dome.ipc.settings.global', globalPatches ) ;
+      globalPatches = {} ;
+    }
+  }, 200
+);
+
+ipcRenderer.on('dome.ipc.closing', (_evt) => {
+  fireSaveSettings();
+  fireSaveSettings.flush();
+  fireSaveGlobals();
+  fireSaveGlobals.flush();
+  System.doExit();
+});
+
+/** @event 'dome.settings'
+    @description Emitted when the settings have been updated. */
+
+/** @event 'dome.defaults'
+    @description Emitted when the settings have been reset to default. */
+
+ipcRenderer.on('dome.ipc.settings.defaults',(sender) => {
+  fireSaveSettings.cancel();
+  fireSaveGlobals.cancel();
+  settingsPatches = {};
+  globalPatches = {};
+  settings = {};
+  globals = {};
+  emitter.emit('dome.defaults');
+  emitter.emit('dome.settings');
+});
+
+ipcRenderer.on('dome.ipc.settings.update',(sender,patches) => {
+  // Don't cancel local updates
+  _.merge( globals , patches , globalPatches );
+  emitter.emit('dome.settings');
+});
+
+/**
+    @summary Get value from local window (persistent) settings.
+    @param {string} key User's Setting Key (`'dome.*'` are reserved keys)
+    @param {any} [defaultValue] - default value if the key is not present
+    @return {any} associated value of object or `undefined`.
+    @tutorial application
+    @description
+    This settings are local to the current window, but persistently
+    saved in the user's home directory.<br/>
+    For global application settings, use `getGlobal()` instead.
+*/
+export function getWindowSetting( key, defaultValue ) {
+  return key ? readSetting( true, key , defaultValue ) : defaultValue ;
+}
+
+/** @summary Set value into local window (persistent) settings.
+    @param {string} key to store the data
+    @param {any} value associated value or object
+    @tutorial application
+    @description
+    This settings are local to the current window, but persistently
+    saved in the user's home directory.<br/>
+    For global application settings, use `setGlobal()` instead.
+*/
+export function setWindowSetting( key , value ) {
+  writeSetting( true, key, value );
+}
+
+/**
+    @summary Get value from application (persistent) settings.
+    @param {string} key User's Setting Key (`'dome.*'` are reserved keys)
+    @param {any} [defaultValue] - default value if the key is not present
+    @return {any} associated value of object or `undefined`.
+    @tutorial application
+    @description
+    These settings are global to the application and persistently
+    saved in the user's home directory.<br/>
+    For local window settings, use `get()` instead.
+*/
+export function getGlobalSetting( key, defaultValue ) {
+  return key ? readSetting( false, key , defaultValue ) : defaultValue ;
+}
+
+/** @summary Set value into application (persistent) settings.
+    @param {string} key to store the data
+    @param {any} value associated value or object
+    @tutorial application
+    @description
+    These settings are global to the current window, but persistently
+    saved in the user's home directory. Updated values are broadcasted
+    in batch to all other windows, which in turn receive a `'dome.settings'`
+    event for synchronizing.<br/>
+    For local window settings, use `set()` instead.
+*/
+export function setGlobalSetting( key , value ) {
+  writeSetting( false, key, value );
+}
+
+// --------------------------------------------------------------------------
+// --- Focus Management
+// --------------------------------------------------------------------------
+
+/** Current focus state of the main window. */
+export function isFocused() { return focus; }
+
+/**
+    @event 'dome.focus'
+    @param {boolean} state - updated focus state
+    @description Emitted when the application gain or loses focus.
+*/
+ipcRenderer.on('dome.ipc.focus',(sender,value) => {
+  focus = value;
+  setContextAppNode();
+  emitter.emit('dome.focus',value);
+});
+
+// --------------------------------------------------------------------------
+// --- Web Navigation
+// --------------------------------------------------------------------------
+
+/**
+    @event 'dome.href'
+    @param {string} href - internal `<a href=...>` target
+    @description
+    Emitted when the user clicks on a local `<a href=...>`.
+    URL with an `http://` protocole are opened externally
+    by the user's default browser.
+*/
+ipcRenderer.on('dome.ipc.href',(href) => emitter.emit('dome.href',href));
+
+// --------------------------------------------------------------------------
+// --- Function Component
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @summary Inlined Function React Component
+   @property {function} children - render function as children
+   @description
+   Allows to define an inlined functional component inside JSX.
+   The children function _can_ use hooks.
+
+@example
+<Render>
+   {() => {
+        let [ state, setState ] = React.useState();
+        …
+        return (<div>…</div>);
+   }}
+</Render>
+*/
+export const Render = ({children}) => {
+  return children();
+};
+
+// --------------------------------------------------------------------------
+// --- React Hooks
+// --------------------------------------------------------------------------
+
+/**
+   @summary Hook to re-render on demand (Custom React Hook).
+   @return {function} to trigger re-rendering
+   @description
+   Returns a callback to trigger a render on demand.
+*/
+export function useForceUpdate()
+{
+  const [tac,onTic] = React.useState();
+  return () => onTic(!tac);
+}
+
+/**
+   @summary Hook to re-render on Dome events (Custom React Hook).
+   @param {string} [event,...] - event names (default: `'dome.update'`)
+   @tutorial hooks
+   @description
+   Returns nothing.
+*/
+export function useUpdate(...evts)
+{
+  const update = useForceUpdate();
+  React.useEffect(() => {
+    const trigger = () => setImmediate(update);
+    if (evts.length == 0) evts.push('dome.update');
+    evts.forEach((evt) => emitter.on(evt,trigger));
+    return () => evts.forEach((evt) => emitter.off(evt,trigger));
+  });
+}
+
+/**
+   @summary Hook to register callbacks to Dome events (Custom React Hook).
+   @param {string} event - Event to register on
+   @param {function} callback - The callback to register
+   @tutorial hooks
+   @description
+   Register the callback on event until the component is unmount.
+   Do not force the component to re-render (unless the callback does).<br/>
+   Returns nothing.
+*/
+export function useEvent(evt,callback)
+{
+  React.useEffect(() => {
+    emitter.on(evt,callback);
+    return () => emitter.off(evt,callback);
+  });
+}
+
+/**
+   @summary Hook to register callbacks to events (Custom React Hook).
+   @param {EventEmitter} emitter - event emitter
+   @param {string} event - Event to register on
+   @param {function} callback - The callback to register
+   @tutorial hooks
+   @description
+   Register the callback on event until the component is unmount.
+   Do not force the component to re-render (unless the callback does).<br/>
+   Returns nothing.
+*/
+export function useEmitter(emitter,evt,callback)
+{
+  React.useEffect(() => {
+    emitter.on(evt,callback);
+    return () => emitter.off(evt,callback);
+  });
+}
+
+const NULL = {}; // Dummy initial value
+
+// --------------------------------------------------------------------------
+// --- Commands Hooks
+// --------------------------------------------------------------------------
+
+/**
+   @summary Hook for command-line interface (Custom React Hook).
+   @return {array} `[argv,wdir]` command-line arguments and working directory
+   @tutorial hooks
+   @description
+   Returns the command-line arguments and working directory for the application
+   instance running in the window. Automatically updated on `dome.command` events.
+
+   See also [Dome.onCommand](#.onCommand) event handler.
+*/
+export function useCommand() {
+  useUpdate('dome.command');
+  const wdir = System.getWorkingDir();
+  const argv = System.getArguments();
+  return [ argv , wdir ];
+}
+
+// --------------------------------------------------------------------------
+// --- Settings Hooks
+// --------------------------------------------------------------------------
+
+function useSettings( local, settings, defaultValue )
+{
+  const [ value, setValue ] = React.useState(() => readSetting( local, settings, defaultValue ));
+  React.useEffect(() => {
+    if (settings) {
+      let callback = () => {
+        let v = readSetting( local, settings , defaultValue );
+        setValue(v);
+      };
+      emitter.on('dome.settings',callback);
+      return () => emitter.off( 'dome.settings', callback );
+    } else {
+      let callback = () => setValue(defaultValue);
+      emitter.on('dome.defaults',callback);
+      return () => emitter.off( 'dome.defaults', callback );
+    }
+  });
+  const doUpdate = (upd) => {
+    const theValue = typeof(upd)==='function' ? upd(value) : upd ;
+    if (settings) writeSetting( local, settings, theValue );
+    else setValue(theValue);
+  };
+  return [ value, doUpdate ];
+}
+
+/**
+   @summary Local state with optional window settings (Custom React Hook).
+   @param {string} [settings] - optional window settings to backup the value
+   @param {any} [defaultValue] - the initial (and default) value
+   @return {array} `[value,setValue]` of the local state
+   @tutorial hooks
+   @description
+   Similar to `React.useState()` with persistent _window_ settings.
+   When the settings key is undefined, it simply uses a local React state.
+   Also responds to `'dome.settings'` to update the state and `'dome.defaults'`
+   to restore the default value.
+
+   The `setValue` callback accepts either a value, or a function to be applied
+   on current value.
+*/
+export function useState( settings, defaultValue )
+{
+  return useSettings( true, settings, defaultValue );
+}
+
+/**
+   @summary Local boolean state with optional window settings (Custom React Hook).
+   @param {string} [settings] - optional window settings to backup the value
+   @param {boolean} [defaultValue] - the initial value (default is `false`)
+   @return {array} `[value,flipValue]` for the local state
+   @tutorial hooks
+   @description
+   Same as [useState](#.useState) with a boolean value that can be set or flipped:
+    - `flipValue()` change the value to its opposite;
+    - `flipValue(v)` change the value to `v`.
+*/
+export function useSwitch( settings, defaultValue=false )
+{
+  const [ value, update ] = useSettings( true, settings, defaultValue );
+  return [ value, v => update(v===undefined ? !value : v) ];
+}
+
+/**
+   @summary Local state for managing history.
+   @param {string} [settings] - global window settings to store the history
+   @param {any} [defaultValue] - default current value
+   @return {object} for managing the history
+   @description
+The returned object contains the following properties:
+
+- `current` the current value in the history
+- `prev:()=>value` optional function for shifting to the next item (if any)
+- `next:()=>value` optional function for shifting to the previous item (if any)
+- `length:number` history size
+- `index:number` position in the history (starting from `0`)
+- `insert:(v)=>value` insert value `v` as the new current value
+- `fork:(v)=>value` insert value `v` but clear next items (if any)
+- `add:(v)=>value` insert value `v` in front of the history
+- `update:(v)=>value` only change the current value
+- `clear:()=>void` clear the history
+
+When inserting a value, previous and current values are kept in the history.
+When updating or insering a falsy value, the history is shifted to the next
+or previous element available in the history. You can drop the current value from the history
+with `update(undefined)`.
+All updating functions return the new current value.
+*/
+
+export function useHistory( settings, defaultValue )
+{
+  const [ { next,prev,current } , setHistory ] =
+        useSettings( true, settings, { next:[], prev:[], current:defaultValue } );
+  const setState = ({ next, prev, current }) => {
+    while( !current && next.length ) current = next.shift();
+    while( !current && prev.length ) current = prev.shift();
+    setHistory({next: next.slice(), prev: prev.slice(), current });
+    return current ;
+  };
+  const insert = (v) => {
+    if (current) prev.unshift(current);
+    return setState({ next, prev, current:v });
+  };
+  const fork = (v) => {
+    if (current) prev.unshift(current);
+    return setState({ next:[], prev, current:v });
+  };
+  const append = (v) => {
+    return setState({next:[], prev: next.reverse().append(current || [],next), current:v });
+  };
+  const move = ( src , tgt ) => src.length ? (() => {
+    let v = src.shift();
+    if (current) tgt.unshift(current);
+    return setState({ current:v, prev, next });
+  }) : undefined;
+  const update = (v) => setState({ next, prev, current:v });
+  const clear = (next.length || prev.length || current) && (() => setState({ next:[], prev:[] }));
+  return {
+    length:next.length + prev.length + (current ? 1 : 0),
+    index:next.length,
+    current,
+    insert,
+    fork,
+    append,
+    update,
+    next:move(next,prev),
+    prev:move(prev,next),
+    clear
+  };
+}
+
+/**
+   @summary Local state with global settings (Custom React Hook).
+   @param {string} settings - global settings for storing the value
+   @param {any} [defaultValue] - the initial and default value
+   @return {array} `[value,setValue]` of the local state
+   @tutorial hooks
+   @description
+   Similar to `React.useState()` with persistent _global_ settings.
+   When the settings key is undefined, it simply uses a local React state.
+   Also responds to `'dome.settings'` to update the state and `'dome.defaults'`
+   to restore the default value.
+
+   The `setValue` callback accepts either a value, or a function to be applied
+   on current value.
+*/
+export function useGlobalSetting( settings, defaultValue )
+{
+  return useSettings( false, settings, defaultValue );
+}
+
+// --------------------------------------------------------------------------
+// --- Global States
+// --------------------------------------------------------------------------
+
+/** @event 'dome.state.update'
+    @description
+    Notify updates within a State object.
+*/
+
+const STATE_UPDATE = 'dome.state.update' ;
+
+/**
+  @summary Global state object.
+  @property {object} state - the current state properties
+  @property {object} defaults - the default state properties
+  @description
+
+You may use this class as convenient way to implement global
+state for your Dome application. Simply create a state `s` with `new State(defaults)`
+and use `s.setState()`, `s.getState()` or `s.state` property, and `s.useState()`
+custom hooks.
+
+A state is also an event emitter that you can use to fire events, and you can use
+the React custom hooks `s.useUpdate()` and `s.useEvent()`.
+
+All above methods are bound to `this` by the constructor.
+
+*/
+export class State extends EventEmitter
+{
+
+  constructor(props) {
+    super();
+    // Makes this field private
+    this.defaults = props ;
+    this.state = Object.assign( {}, props );
+    this.update = this.update.bind(this);
+    this.getState = this.getState.bind(this);
+    this.setState = this.setState.bind(this);
+    this.clearState = this.clearState.bind(this);
+    this.replaceState = this.replaceState.bind(this);
+    this.useState = this.useState.bind(this);
+    this.useEvent = this.useEvent.bind(this);
+    this.useUpdate = this.useUpdate.bind(this);
+  }
+
+  /** Emits the `dome.state.update` event */
+  update() { this.emit('dome.state.update'); }
+
+  /** Returns the state property. */
+  getState() { return this.state; }
+
+  /** @summary Update the state with (some) properties.
+      @param {object} props - the properties to be updated
+      @description
+      Update the state with `Object.assign`, like `setState()` on React components.
+      Also fire the `'dome.update'` property on the object. */
+  setState(props) {
+    Object.assign( this.state, props );
+    this.update();
+  }
+
+  /** @summary Replace (all) state properties.
+      @description
+      Replace the entire store with the new properties.
+      Also fire the `'dome.update'` property on the object. */
+  replaceState(props) {
+    this.state = Object.assign( {}, props );
+    this.update();
+  }
+
+  /** @summary Reset (all) state properties.
+      @description
+      Restore the entire store with the default properties.
+      Also fire the `'dome.state.update'` property on the object. */
+  clearState() {
+    this.state = Object.assign( {}, this.defaults );
+    this.update();
+  }
+
+  /** @summary Hook to use the state (custom React Hook).
+      @return {array} `[state,setState]` with current object properties and
+      function to update them. */
+  useState() {
+    let forceUpdate = useForceUpdate();
+    useEmitter( this, 'dome.state.update', forceUpdate );
+    return [ this.state , this.setState ];
+  }
+
+  /** @summary Hook to re-render your component on State events.
+      @param {string} [event] - the event to listen to (defaults to `'dome.update'`)
+  */
+  useUpdate(evt = 'dome.state.update') {
+    let forceUpdate = useForceUpdate();
+    useEmitter( this, evt, forceUpdate );
+  }
+
+  /** @summary Hook to trigger callbacks on State events.
+      @param {string} event - the event to listen to
+      @param {function} callback - the callback triggered on event
+  */
+  useEvent(evt,callback) {
+    useEmitter( this, evt, callback );
+  }
+
+}
+
+// --------------------------------------------------------------------------
+// --- Timer Hooks
+// --------------------------------------------------------------------------
+
+// Collection of { pending, timer, period, time, event } indexed by period
+const clocks = {};
+
+const CLOCKEVENT = (period) => 'dome.clock.' + period ;
+
+const TIC_CLOCK = (clk) => () => {
+  if (0 < clk.pending) {
+    clk.time += clk.period ;
+    emitter.emit(clk.event,clk.time);
+  } else {
+    clearInterval(clk.timer);
+    delete clocks[clk.period];
+  }
+};
+
+const INC_CLOCK = (period) => {
+  let clk = clocks[period] ;
+  if (!clk) {
+    let event = CLOCKEVENT(period);
+    let time = (new Date()).getTime();
+    clk = { pending: 0, time, period, event };
+    clocks[period] = clk ;
+    let tic = TIC_CLOCK(clk);
+    clk.timer = setInterval(tic,period);
+  }
+  clk.pending++;
+};
+
+const DEC_CLOCK = (period) => {
+  let clk = clocks[period] ;
+  if (clk) {
+    clk.pending--;
+  }
+};
+
+/**
+   @summary Synchronized start & stop timer (Custom React Hook).
+   @param {number} period - timer interval, in milliseconds (ms)
+   @param {boolean} [initStart] - whether to initially start the timer (default is `false`)
+   @tutorial hooks
+   @return {timer} Timer object
+   @description
+   Create a local timer, synchronized on a global clock, that can be started
+   and stopped on demand during the life cycle of the component.
+
+   Each timer has its individual start & stop state. However,
+   all timers with the same period _are_ synchronized with each others.
+
+   The timer object has the following properties and methods:
+   - `timer.start()` starts the timer,
+   - `timer.stop()` starts the timer,
+   - `timer.time` is the time stamp of the last clock (see below)
+
+   It is safe to call `start()` and `stop()` whether the timer is running or not.
+   When `timer.time` is `-1`, it means the timer is stopped.
+   When `timer.time` is `0` it means the timer has just been started and no tic has
+   been received yet. The time stamp is in milliseconds; it is shared among all
+   timers synchronized on the same period and roughly equal to the `Date.getTime()`
+   of the associated clock.
+
+ */
+
+export function useClock(period,initStart)
+{
+  const [time,setTime] = React.useState(initStart ? 0 : -1);
+  const running = 0 <= time ;
+  React.useEffect(() => {
+    if (running) {
+      INC_CLOCK(period);
+      const event = CLOCKEVENT(period);
+      emitter.on(event,setTime);
+      return () => {
+        DEC_CLOCK(period);
+        emitter.off(event,setTime);
+      };
+    } else
+      return undefined ;
+  });
+  return {
+    time,
+    start: () => { if (!running) setTime(0); },
+    stop: () => { if (running) setTime(-1); }
+  };
+}
+
+// --------------------------------------------------------------------------
+// --- Export
+// --------------------------------------------------------------------------
+
+export default {
+  platform,
+  DEVEL,
+  setTitle,
+  setModified,
+  isApplicationWindow, setApplicationWindow,
+  isPreferencesWindow, setPreferencesWindow,
+  isFocused,
+  emitter, emit, on, off,
+  addMenu,
+  addMenuItem,
+  setMenuItem,
+  popupMenu,
+  setWindowSetting, getWindowSetting,
+  setGlobalSetting, getGlobalSetting,
+  update,  onUpdate, onReload, onCommand,
+  State, Render,
+  useForceUpdate,
+  useUpdate, useEvent, useEmitter,
+  useCommand, useClock,
+  useState, useSwitch,
+  useHistory,
+  useGlobalSetting
+} ;
+
+// --------------------------------------------------------------------------
diff --git a/ivette/src/dome/src/renderer/errors.js b/ivette/src/dome/src/renderer/errors.js
new file mode 100644
index 0000000000000000000000000000000000000000..bb3ea064aae87841fe90421142efec17808be573
--- /dev/null
+++ b/ivette/src/dome/src/renderer/errors.js
@@ -0,0 +1,75 @@
+// --------------------------------------------------------------------------
+// --- Managing Errors
+// --------------------------------------------------------------------------
+
+import React from 'react' ;
+import { Label } from 'dome/controls/labels' ;
+import { Button } from 'dome/controls/buttons' ;
+
+/** @module dome/errors */
+
+// --------------------------------------------------------------------------
+// --- Error Boundaries
+// --------------------------------------------------------------------------
+
+/**
+   @summary React Error Boundaries.
+   @property {string} [label] - Default error box label
+   @property {function} [onError] - Alternative renderer
+   @description
+   Install an error boundary. In case of error, the default
+   rendering is a warning button that output on console the
+   catched error.
+
+   An alternative rendering can be supplied
+   with `onError:(error,info) => React.Element`.
+
+ */
+export class Catch extends React.Component
+{
+
+  constructor(props) {
+    super(props);
+    this.state = { };
+    this.logerr = this.logerr.bind(this);
+    this.reload = this.reload.bind(this);
+  }
+
+  dumpError(error,info) {
+  }
+
+  componentDidCatch(error, info) {
+    this.setState({ error, info });
+  }
+
+  logerr() {
+    const { error, info } = this.state ;
+    console.error('[dome] Catched error:',error,info);
+  }
+
+  reload() {
+    this.setState({ error: undefined, info: undefined });
+  }
+
+  render() {
+    const { error, info } = this.state ;
+    if (error) {
+      const { onError, label='Error' } = this.props ;
+      if (typeof(onError)==='function')
+        return onError(error,info,this.reload);
+      else
+        return (
+          <div>
+            <Button icon='WARNING' kind='warning'
+                    title={error}
+                    onClick={this.logerr} />
+            <Button icon='RELOAD' onClick={this.reload} />
+            <Label>{label}</Label>
+          </div>
+        );
+    }
+    return this.props.children || null ;
+  }
+}
+
+// --------------------------------------------------------------------------
diff --git a/ivette/src/dome/src/renderer/layout/boxes.css b/ivette/src/dome/src/renderer/layout/boxes.css
new file mode 100644
index 0000000000000000000000000000000000000000..a53382360de5caed92ac8239926234f8e0fd5bf9
--- /dev/null
+++ b/ivette/src/dome/src/renderer/layout/boxes.css
@@ -0,0 +1,86 @@
+/* -------------------------------------------------------------------------- */
+/* --- Box Containers                                                     --- */
+/* -------------------------------------------------------------------------- */
+
+.dome-xBoxes-hbox
+{
+    display: flex ;
+    overflow: hidden ;
+    flex-flow: row nowrap ;
+    align-items: stretch ;
+}
+
+.dome-xBoxes-vbox
+{
+    display: flex ;
+    overflow: hidden ;
+    flex-flow: column nowrap ;
+    align-items: stretch ;
+}
+
+.dome-xBoxes-grid
+{
+    flex: 1 1 auto ;
+    overflow: auto ;
+    display: grid ;
+    display: grid;
+    grid-auto-rows: auto;
+    grid-auto-flow: row;
+    align-items: stretch;
+    align-content: start;
+    justify-content: start;
+}
+
+/* -------------------------------------------------------------------------- */
+/* --- Extensibility                                                      --- */
+/* -------------------------------------------------------------------------- */
+
+.dome-xBoxes-pack
+{
+    flex: 0 0 auto ;
+    align-self: flex-start ;
+}
+
+.dome-xBoxes-box
+{
+    flex: 0 0 auto ;
+}
+
+.dome-xBoxes-fill
+{
+    flex: 1 1 auto ;
+}
+
+.dome-xBoxes-scroll
+{
+    flex: 1 1 auto ;
+    overflow: auto ;
+}
+
+.dome-container > .dome-xBoxes-fill
+{
+    width:  100% ;
+    height: 100% ;
+}
+
+.dome-container > .dome-xBoxes-scroll
+{
+    width:  100% ;
+    height: 100% ;
+}
+
+/* -------------------------------------------------------------------------- */
+/* --- Spaces                                                             --- */
+/* -------------------------------------------------------------------------- */
+
+.dome-xBoxes-space
+{
+    flex: 0 1 24px ;
+}
+
+.dome-xBoxes-filler
+{
+    flex: 1 1 24px ;
+}
+
+/* -------------------------------------------------------------------------- */
diff --git a/ivette/src/dome/src/renderer/layout/boxes.js b/ivette/src/dome/src/renderer/layout/boxes.js
new file mode 100644
index 0000000000000000000000000000000000000000..b648f612cfb75d2af04252b78b3bad8ad4299f9a
--- /dev/null
+++ b/ivette/src/dome/src/renderer/layout/boxes.js
@@ -0,0 +1,201 @@
+// --------------------------------------------------------------------------
+// --- Box Layout
+// --------------------------------------------------------------------------
+
+/** @module dome/layout/boxes
+    @description
+
+This modules offers several `<div>` containers with various
+predefined layout.
+
+Boxes are the very elementary way to layout components horizontally
+or vertically. The different kinds of proposed boxes differ in how they
+extends in both directions: normal boxes extends
+along their layout direction, _pack_ boxes don't extends and _fill_ boxes
+extends along both directions.
+
+Grids layout their component from left-to-right inside predefined _columns_,
+then vertically by wrapping cells in rows.
+
+The various containers are summarized on the table below:
+
+| **Box** | Layout | Extends |
+|:-------------:|:-----------:|:----------:|:----------:|
+| [Hbox](module-dome_layout_boxes.Hpack.html) | horiz. | horiz. |
+| [Vbox](module-dome_layout_boxes.Vpack.html) | vert. | vert. |
+| [Hpack](module-dome_layout_boxes.Hpack.html) | horiz. | none |
+| [Vpack](module-dome_layout_boxes.Vpack.html) | vert. | none |
+| [Hfill](module-dome_layout_boxes.Hpack.html) | horiz. | both |
+| [Vfill](module-dome_layout_boxes.Vpack.html) | vert. | both |
+| [Grid](module-dome_layout_boxes.Hpack.html) | columns | both |
+| [Scroll](module-dome_layout_boxes.Scroll.html) | n/a | both |
+
+Inside a box, you may add `<Space/>` and `<Filler/>` to separate items.
+Inside a grid, you may also use `<Space/>` or an empty `<div/>` for empty cells.
+
+<strong>Warning:</strong> large elements will be clipped if they overflow.
+If you want to add scrolling capabilities to some item that does not manage overflow
+natively, place it inside a `<Scroll/>` sub-container.
+
+*/
+
+import React from 'react';
+import Dome from 'dome';
+import { Title } from 'dome/controls/labels' ;
+import './boxes.css' ;
+
+// --------------------------------------------------------------------------
+// --- Generic Box
+// --------------------------------------------------------------------------
+
+const makeBox = ( boxClasses, props, morestyle ) => {
+  const { children, className, style, ...others } = props ;
+  const allClasses = className ? boxClasses + ' ' + className : boxClasses ;
+  const allStyles = morestyle ? (style ? Object.assign( {}, style, morestyle ) : morestyle) : style ;
+  return (
+    <div className={allClasses} style={allStyles} {...others} >
+      {children}
+    </div>
+  );
+};
+
+// --------------------------------------------------------------------------
+// --- Predefined Defaults
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @summary Horizontal box (extends horizontally, no overflow)
+   @property {object} [...props] - Extra properties passed to the `<div>` container
+   @description
+   <strong>Warning:</strong> large elements will be clipped if they overflow.
+*/
+export const Hbox = (props) => makeBox( 'dome-xBoxes-hbox dome-xBoxes-box' , props );
+
+/**
+   @class
+   @summary Vertical box (extends vertically, no overflow)
+   @property {object} [...props] - Extra properties passed to the `<div>` container
+   @description
+   <strong>Warning:</strong> large elements will be clipped if they overflow.
+*/
+export const Vbox = (props) => makeBox( 'dome-xBoxes-vbox dome-xBoxes-box' , props );
+
+/**
+   @class
+   @summary Compact Horizontal box (fixed dimensions, no overflow)
+   @property {object} [...props] - Extra properties passed to the `<div>` container
+   @description
+   <strong>Warning:</strong> large elements would be clipped if they overflow.
+*/
+export const Hpack = (props) => makeBox( 'dome-xBoxes-hbox dome-xBoxes-pack' , props );
+
+/**
+   @class
+   @summary Compact Vertical box (fixed dimensions, no overflow)
+   @property {object} [...props] - Extra properties passed to the `<div>` container
+   @description
+   <strong>Warning:</strong> large elements will be clipped if they overflow.
+*/
+export const Vpack = (props) => makeBox( 'dome-xBoxes-vbox dome-xBoxes-pack' , props );
+
+/**
+   @class
+   @summary Horizontally filled box (fixed height, maximal width, no overflow)
+   @property {object} [...props] - Extra properties passed to the `<div>` container
+   @description
+   <strong>Warning:</strong> large elements will be clipped if they overflow.
+*/
+export const Hfill = (props) => makeBox( 'dome-xBoxes-hbox dome-xBoxes-fill' , props );
+
+/**
+   @class
+   @summary Vertically filled box (fixed width, maximal height, no overflow)
+   @property {object} [...props] - Extra properties passed to the `<div>` container
+   @description
+   <strong>Warning:</strong> large elements will be clipped if they overflow.
+*/
+export const Vfill = (props) => makeBox( 'dome-xBoxes-vbox dome-xBoxes-fill' , props );
+
+// --------------------------------------------------------------------------
+// --- Scrolling & Spacing
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @summary Scrolling container
+   @property {object} [...props] - Extra properties passed to the `<div>` container
+*/
+export const Scroll = (props) => makeBox( 'dome-xBoxes-scroll dome-container' , props );
+
+/**
+   @class
+   @summary rigid space between items in a box
+   @property {object} [...props] - Extra properties passed to the `<div>` separator
+*/
+export const Space = (props) => makeBox( 'dome-xBoxes-space' , props );
+
+/**
+   @class
+   @summary extensible space between items in a box
+   @property {object} [...props] - Extra properties passed to the `<div>` separator
+*/
+export const Filler = (props) => makeBox( 'dome-xBoxes-filler' , props );
+
+// --------------------------------------------------------------------------
+// --- Grids
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @summary Grid box container
+   @property {string} [columns] - Grid column specifications
+   @property {object} [...props] - Extra properties passed to the `<div>` container
+   @description
+   Layout its children in a multi-column grid. Each column is specified by its
+   width, following the CSS Grid Layout `grid-template-columns` property.
+
+   The rows are populated with children from left-to-right, using one column at a time.
+   Items layout can be modified by adding CSS Grid Layout item properties.
+
+   Example: `<Grid columns="25% auto auto"> ... </Grid>`
+*/
+export const Grid = ({columns='auto',...props}) =>
+  makeBox( 'dome-xBoxes-grid', props , { gridTemplateColumns: columns });
+
+// --------------------------------------------------------------------------
+// --- Folders
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @summary Foldable Vpack box
+   @property {string} label - box label
+   @property {string} [title] - box label tooltip
+   @property {string} [settings] - window setting to store the fold/unfold state
+   @property {boolean} [defaultUnfold] - initial state (default is `false`)
+   @property {React.Children} [children] - content of the vertical box
+   @description
+   A vertical `Vpack` box with a clickable head label to fold/unfold its content.
+*/
+export function Folder( { settings, defaultUnfold=false, indent=18, label, title, children } )
+{
+  const [ unfold , setUnfold ] = Dome.useState( settings, defaultUnfold );
+  const icon = unfold ? 'TRIANGLE.DOWN' : 'TRIANGLE.RIGHT' ;
+  const onClick = () => setUnfold( !unfold );
+  return (
+    <Vpack>
+      <Hpack onClick={onClick}><Title icon={icon} label={label} title={title} /></Hpack>
+      <Vpack style={{ marginLeft:indent }}>
+        { unfold && children }
+      </Vpack>
+    </Vpack>
+  );
+}
+
+// --------------------------------------------------------------------------
+
+export default { Hpack, Vpack, Hbox, Vbox, Hfill, Vfill,
+                 Space, Filler, Scroll, Grid, Folder };
+
+// --------------------------------------------------------------------------
diff --git a/ivette/src/dome/src/renderer/layout/dispatch.js b/ivette/src/dome/src/renderer/layout/dispatch.js
new file mode 100644
index 0000000000000000000000000000000000000000..fd40b802d3f3c5ad04dd574fe521b2533dbd6dbc
--- /dev/null
+++ b/ivette/src/dome/src/renderer/layout/dispatch.js
@@ -0,0 +1,118 @@
+// --------------------------------------------------------------------------
+// --- Dispatch Layout
+// --------------------------------------------------------------------------
+
+/** @module dome/layout/dispatch
+    @description
+
+This module allow to declare components anywhere in a component hierarchy
+and to render them a totally different place.
+
+You shall wrap dispatched components inside a `<Dispatch.Item>` container,
+and render them wherever you want with `<Dispatch.Render>`. Each target
+place can display only one uniquely identified item.
+
+This can be also used to display some item among many in one unique place.
+*/
+
+import _ from 'lodash' ;
+import React from 'react';
+import Dome from 'dome' ;
+
+// --------------------------------------------------------------------------
+// --- Global Dispatcher
+// --------------------------------------------------------------------------
+
+const EVENT = (id) => 'dome.dispatch.' + id ;
+const ITEMS = {};
+
+const getItem = (id) => {
+  let item = ITEMS[id];
+  if (!item) item = ITEMS[id] = {
+    id, evt: EVENT(id)
+  };
+  return item;
+};
+
+const trigger = (item) => {
+  if (item.rendered) {
+    item.rendered = false ;
+    setImmediate(() => Dome.emit(item.evt));
+  }
+};
+
+const setItem = (id,children) => {
+  let item = getItem(id);
+  item.content = children ;
+  trigger(item);
+};
+
+const removeItem = (id) => {
+  let item = getItem(id);
+  item.content = null ;
+  trigger(item);
+};
+
+// --------------------------------------------------------------------------
+// --- Dispatched Items
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @summary Define dispatched item
+   @property {string} id - the item _global_ unique identifier
+   @property {React.Children} {children} - item contents
+   @description
+   Declare the content of some dispatched item.
+   Each item identifier shall be assigned once from the mounted
+   hierarchy of components. Otherwize, the content that would be
+   displayed is totally unpredictable.
+*/
+export function Item({ id, children })
+{
+  React.useEffect(() => {
+    setItem(id,children);
+    return () => removeItem(id);
+  });
+  return null;
+}
+
+// --------------------------------------------------------------------------
+// --- Render Targets
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @summary Render dispatched item
+   @property {string} id - the item _global_ unique identifier to render
+   @property {function|React.Children} {children} - conditional or alternative content (default: `null`)
+   @description
+   Render the content of some dispatched item.
+   In case multiple of rendering, the children elements would be shared
+   among several places, with unpredicatable behavior.
+
+   If the render element has a function as children, it is passed the content of the
+   item, or undefined. This allows for conditional rendering, depending on whether the item
+   has been specified somewhere in the hierarchy or not.
+
+   Otherwized, when the item is not specified, the render element display
+   its own content, if any.
+*/
+export function Render({ id, children=null })
+{
+  Dome.useUpdate(EVENT(id));
+  let item = getItem(id);
+  item.rendered = true ;
+  if (typeof(children)==='function')
+    return children(item.content);
+  else {
+    let content = item.content || children ;
+    return content && <React.Fragment>{content}</React.Fragment>;
+  }
+}
+
+// --------------------------------------------------------------------------
+
+export default { Item, Render };
+
+// --------------------------------------------------------------------------
diff --git a/ivette/src/dome/src/renderer/layout/forms.css b/ivette/src/dome/src/renderer/layout/forms.css
new file mode 100644
index 0000000000000000000000000000000000000000..23a1712dc346d0bf66214dcf09bae04ded0a4c44
--- /dev/null
+++ b/ivette/src/dome/src/renderer/layout/forms.css
@@ -0,0 +1,254 @@
+/* -------------------------------------------------------------------------- */
+/* --- Form Containers                                                    --- */
+/* -------------------------------------------------------------------------- */
+
+/* Main Form */
+.dome-xForm-grid
+{
+    flex: 1 1 auto;
+    overflow: auto;
+    display: grid;
+    padding: 12px 32px;
+    grid-template-columns: [left] auto [field] auto [right] ;
+    grid-gap: 4px 12px;
+    grid-auto-rows: auto;
+    grid-auto-flow: row;
+    align-items: start;
+    align-content: start;
+    justify-content: start;
+}
+
+/* Field List */
+.dome-xForm-list
+{
+    display: grid;
+    grid-template-columns: [field] auto [right] ;
+    grid-gap: 4px ;
+    grid-auto-rows: auto;
+    grid-auto-flow: row;
+    align-items: start;
+}
+
+/* Block Container */
+.dome-xForm-block
+{
+    margin-bottom: 6px ;
+    margin-top: 6px ;
+    position: relative ;
+    grid-column-start: left ;
+    grid-column-end: right ;
+    justify-self: start ;
+}
+
+/* -------------------------------------------------------------------------- */
+/* --- Fields                                                             --- */
+/* -------------------------------------------------------------------------- */
+
+/* Label */
+.dome-xForm-label
+{
+    position: relative ;
+    grid-column-start: left ;
+    grid-column-end: field ;
+    justify-self: end ;
+}
+
+.dome-xForm-section
+{
+    margin-bottom: 6px ;
+    position: relative ;
+    left: -6px ;
+    grid-column-start: left ;
+    grid-column-end: right ;
+    justify-self: start ;
+}
+
+.dome-xForm-section .dome-disabled {
+    color: #aaa ;
+}
+
+.dome-xForm-hsep
+{
+    grid-column-start: left ;
+    grid-column-end: right ;
+    height: 6px ;
+}
+
+.dome-xForm-fold
+{
+    position: absolute ;
+    top: 2px ;
+    fill: #888 ;
+    left: -14px ;
+}
+
+.dome-xForm-block .dome-xForm-label
+{
+    display: none ;
+}
+
+/* -------------------------------------------------------------------------- */
+/* --- Field                                                              --- */
+/* -------------------------------------------------------------------------- */
+
+.dome-xForm-field
+{
+    position: relative ;
+    grid-column-start: field ;
+    grid-column-end: right ;
+    justify-self: start ;
+}
+
+.dome-xForm-label.dome-disabled, .dome-xForm-field.dome-disabled
+{
+    color: #aaa ;
+    opacity: 0.9 ;
+}
+
+/* -------------------------------------------------------------------------- */
+/* --- Errors                                                             --- */
+/* -------------------------------------------------------------------------- */
+
+/* Error Icon */
+.dome-xForm-error
+{
+    position: absolute ;
+    top: -1px ;
+    fill: darkorange ;
+    margin-left: 6px ;
+}
+
+/* Error Warning */
+.dome-xForm-warning
+{
+    visibility: hidden ;
+    position: absolute ;
+    top: 3px ;
+    color: darkorange ;
+    margin-left: 4px ;
+    font-size: smaller ;
+}
+
+/* Hovered Warning */
+.dome-xForm-error:hover .dome-xForm-warning
+{
+    visibility: visible ;
+}
+
+/* -------------------------------------------------------------------------- */
+/* --- Text Fields                                                        --- */
+/* -------------------------------------------------------------------------- */
+
+.dome-xForm-text-field
+{
+    font-size: 10pt ;
+    width: 180px ;
+    padding-top:    1px ;
+    padding-bottom: 1px ;
+    padding-right: 4px ;
+    padding-left:  4px ;
+}
+
+.dome-xForm-textarea-field
+{
+    border-color: #d6d6d6 ;
+    font-size: 10pt ;
+    padding-top:    2px ;
+    padding-bottom: 2px ;
+    padding-right: 4px ;
+    padding-left:  4px ;
+}
+
+.dome-xForm-field.dome-disabled .dome-xForm-textarea-field
+{
+    opacity: 0.6 ;
+}
+
+/* -------------------------------------------------------------------------- */
+/* --- Number Fields                                                      --- */
+/* -------------------------------------------------------------------------- */
+
+.dome-xForm-number-field
+{
+    text-align: right ;
+    padding-right: 12px ;
+    padding-left:  12px ;
+    width: 80px ;
+}
+
+.dome-xForm-spinner-field
+{
+    text-align: right ;
+    padding-left:  12px ;
+    width: 80px ;
+}
+
+.dome-xForm-units
+{
+    color: #888 ;
+    font-size: smaller ;
+    margin-left: 4px ;
+}
+
+/* -------------------------------------------------------------------------- */
+/* --- Sliders                                                            --- */
+/* -------------------------------------------------------------------------- */
+
+.dome-xForm-slider-field
+{
+    padding-right: 12px ;
+    padding-left:  12px ;
+    width: 120px ;
+}
+
+.dome-xForm-slider-value
+{
+    position: relative ;
+    top: -4px ;
+}
+
+.dome-xForm-slider-hide
+{
+    opacity: 0.0 ;
+    transition: opacity 0.5s ease-in 0.1s ;
+}
+
+.dome-xForm-slider-show
+{
+    transition: none ;
+}
+
+/* -------------------------------------------------------------------------- */
+/* --- Date & Time                                                        --- */
+/* -------------------------------------------------------------------------- */
+
+.dome-xForm-date-field
+{
+    text-align: right ;
+    width: 137px ;
+}
+
+.dome-xForm-time-field
+{
+    text-align: right ;
+    width: 109px ;
+}
+
+/* -------------------------------------------------------------------------- */
+/* --- Color                                                              --- */
+/* -------------------------------------------------------------------------- */
+
+.dome-xForm-color-field
+{
+    position: relative ;
+    top: -1px ;
+    height: 21px ;
+    width: 32px ;
+}
+
+.dome-disabled .dome-xForm-color-field
+{
+    opacity: 0.2 ;
+}
+
+/* -------------------------------------------------------------------------- */
diff --git a/ivette/src/dome/src/renderer/layout/forms.js b/ivette/src/dome/src/renderer/layout/forms.js
new file mode 100644
index 0000000000000000000000000000000000000000..2753c40bff6ebda733cfc0454f37162d3568a7da
--- /dev/null
+++ b/ivette/src/dome/src/renderer/layout/forms.js
@@ -0,0 +1,1147 @@
+// --------------------------------------------------------------------------
+// --- Form Layout
+// --------------------------------------------------------------------------
+
+/** @module dome/layout/forms */
+
+import React from 'react' ;
+import Dome from 'dome' ;
+import { SVG } from 'dome/controls/icons' ;
+import { Checkbox, Radio, Select as Selector } from 'dome/controls/buttons' ;
+import _ from 'lodash' ;
+import './forms.css' ;
+
+// --------------------------------------------------------------------------
+// --- Utilities
+// --------------------------------------------------------------------------
+
+const Context = React.createContext();
+
+const SELECT = (props,context) => props === undefined ? context : props ;
+const ACCESS = (props,context,path) => props === undefined ? _.get(context,path) : props ;
+const CONDITION = (context,cond,undef) => {
+  switch(typeof(cond)) {
+  case 'undefined':
+    return undef;
+  case 'string':
+  case 'array':
+    return _.get(context,cond);
+  default:
+    return cond;
+  }
+};
+
+const COMBINE = (a,b) => {
+  if (a === undefined) return b;
+  if (b === undefined) return a;
+  return (...args) => { a(...args); b(...args); };
+};
+
+const UPDATE = (value,error,path,callback) => callback &&
+      ((v,e) => {
+        let update = value || {} ;
+        _.set( update, path, v );
+        let errors ;
+        if (error || e) {
+          errors = error || {} ;
+          _.set( errors, path, e );
+          if (!_.find(errors))
+            errors = undefined;
+        }
+        callback(update,errors);
+      });
+
+const CHECK = (validate,v) => {
+  if (typeof(validate) === 'function')
+    return validate(v);
+  if (_.isRegExp(validate))
+    return validate.test(v);
+  return validate;
+};
+
+const ERROR = (validate,warning,v) => {
+  let ok = CHECK(validate,v);
+  switch(ok) {
+  case undefined:
+  case true:
+    return undefined ;
+  default:
+    return ok || warning ;
+  }
+};
+
+const VALIDATE = (validate,warning,callback) => {
+  if (!validate) return callback;
+  return ((v,e) => {
+    if (!callback) return;
+    else if (v===undefined || e) callback(v,e);
+    else {
+      let e = ERROR(validate,warning,v);
+      callback(v,e);
+    }
+  });
+};
+
+const RENDER = (children,context) => (
+  children ?
+    ( typeof(children)==='function'
+      ? children(context)
+      : children )
+    : null
+);
+
+const ONCHANGE = (onChange) =>
+      typeof(onChange)==='function'
+      ? (evt) => onChange(evt.target.value)
+      : undefined ;
+
+const CLASSES = (...args) => _.filter(args).join(' ');
+
+// --------------------------------------------------------------------------
+// --- Filtering DataFlow
+// --------------------------------------------------------------------------
+
+const FILTER_ERROR = (err) =>
+      err ? _.toString(err) : 'Invalid input, enter a new value to fix'
+;
+
+class FILTER extends React.Component
+{
+
+  constructor(props) {
+    super(props);
+    this.state = this.forward();
+    this.onChange = this.onChange.bind(this);
+    if (props.period)
+      this.backward = _.debounce(this.backward,props.period);
+  }
+
+  componentDidUpdate(prevProps) {
+    const props = this.props ;
+    if (props.value !== prevProps.value ||
+        props.error !== prevProps.error)
+      this.setState(this.forward());
+  }
+
+  onChange(value,error) {
+    this.setState({value,error});
+    this.backward(value,error);
+  }
+
+  forward() {
+    try {
+      const { input , value , error } = this.props ;
+      return { value: input ? input(value) : value, error };
+    } catch(err) {
+      return { value: undefined, error: FILTER_ERROR(err) };
+    }
+  };
+
+  backward(v,e)
+  {
+    const { output, onChange } = this.props ;
+    try {
+      if (output) v = output(v);
+    } catch(err) {
+      this.setState({error: FILTER_ERROR(err)});
+      return;
+    }
+    if (onChange) onChange(v,e);
+  }
+
+  render() {
+    const { disabled, children } = this.props ;
+    const { value, error } = this.state ;
+    const context = { onChange: this.onChange, disabled, value, error };
+    return (
+      <Context.Provider value={context}>
+        {RENDER(children,context)}
+      </Context.Provider>
+    );
+  }
+
+}
+
+// --------------------------------------------------------------------------
+// --- Generic Field
+// --------------------------------------------------------------------------
+
+const PERIOD = (latency) => {
+  switch(typeof(latency)) {
+  case 'undefined': return 0;
+  case 'number': return latency;
+  default:
+    return latency ? 600 : 0 ;
+  }
+};
+
+/**
+   @class
+   @summary Generic context wrapper for field values
+   @property {string} [path] - select a property in the context value (and error)
+   @property {any} [value] - set the inherited or edited value
+   @property {any} [error] - set the inherited or edited error
+   @property {function} [input] - pre-processing of input values (after `path` selection)
+   @property {function} [output] - post-processing of output values (before `path` update)
+   @property {function} [onChange] - callback for edited values and errors
+   @property {boolean|string|function} [disabled] - disabled field (default is `false`)
+   @property {boolean|string|function} [enabled] - conditional enabling (default is `true`)
+   @property {boolean|string|function} [visible] - conditional rendering (default is `true`)
+   @property {boolean|string|function} [hidden] - conditional hidding (default is `false`)
+   @property {boolean|regexp|function} [validate] - validation of the updated value
+   @property {string} [warning] - error message in case `validate` returns `false`
+   @property {boolean|number} [latency] - delay validation and propagation callbacks
+   @property {boolean} [state] - maintain a locally edited state (default is `false`)
+   @property {React.children|function} [children] - elements to render in the new context
+   @description
+
+Forms are based on a local context that can be modified by using this multi-purposed
+component. The local context consists of:
+- `value`: the currently edited value;
+- `error`: the last emitted error for this value;
+- `onChange(value[,error])`: the callback to be used on updates;
+- `disabled`: whether this value can be edited or not.
+
+Undefined properties are inherited from the context. If the `path` property is defined,
+the `<Select>` component extract the associated value and error from the inherited context.
+When both `path` and `value` properties are defined, the specified value takes the precedence.
+Access and updates of `path` are performed with the lodash `_.get()` and `_.set()` functions.
+Although, the `<Select>` might perform in-place modifications of previous object or array value.
+Use `_.cloneDeep()` to build a (recursive) copy of some value.
+
+Before being processed downward, the inherited or selected value can be transformed.
+The `input` function is applied on incomming value, and `output` function is applied
+on updated values.
+
+The specified callback `onChange` is invoked with the updated value and error. The callback
+inherited from the context is _also_ notified. When the `path` property is set,
+the contextual callback will be notified with the full context values and errors,
+updated with local changes to the selected path.
+
+The validation callback is invoked on updates. It shall return `undefined`
+or a string explaining why the value is invalid. Boolean are also accepted,
+leading to an error string defined by the `warning` property, which defaults to `"Invalid field"`.
+Alternatively, `validate` can be a regexp that is tested against the value.
+For instance, to accept only numbers, you can use `validate={/^[0-9]+$/}`
+and `warning='Must be a number'`.
+
+To avoid too many callback and validation checks, typically for text-based fields, it is possible to
+set a latency period before validating the input and emitting the callback. In such a case, a transient
+local state for value and error is maintained until the edited value is stabilized, before performing
+validation and upward propagation. The default lantency period is 600ms.
+
+It is possible to conditionally enabling, disabling or hiding the content by using
+the `disabled`, `enabled`, `visible` and `hidden` properties. They all can be boolean values,
+or path in or function of the inherited value. For instance, `<Select path='f.g' enabled='f.ok'/>`
+would allow to edit the value `value.f.g`, but disable edition when `value.f.ok` is falsy.
+Remark that if the inherited context is disabled, the selected content is disabled too.
+
+When latency, or input/output transformation are required, a local state is maintained.
+Otherwized, context and callbacks are propagated upward and backward. However, you can
+force this local state to be maintained by specifying the property `state` to `true`.
+This shall be used only at top-level for implementing a non-controlled component.
+
+*/
+export function Select({
+  path, latency,
+  defaultValue:def,
+  children, state,
+  ...props
+}) {
+  let context = React.useContext( Context );
+  if (!CONDITION(context.value,props.visible,true) ||
+      CONDITION(context.value,props.hidden,false))
+    return null;
+  let disabled = context.disabled
+      || CONDITION(context.value,props.disabled,false)
+      || !CONDITION(context.value,props.enabled,true);
+  let value,error,callback ;
+  if (path) {
+    value = ACCESS( props.value , context.value , path );
+    error = ACCESS( props.error , context.error , path );
+    if (!disabled)
+      callback = UPDATE(context.value,context.error,path,context.onChange);
+  } else {
+    value = SELECT( props.value , context.value );
+    error = SELECT( props.error , context.error );
+    if (!disabled)
+      callback = context.onChange;
+  };
+
+  let onChange ;
+  if (!disabled) {
+    onChange = VALIDATE( props.validate, props.warning,
+                         COMBINE(props.onChange,callback) );
+  }
+
+  if (props.validate && value && !error) {
+    error = ERROR( props.validate, props.warning, value );
+  }
+
+  let newContext = { value,error, disabled, onChange };
+  if (state || latency || props.input || props.output) {
+    return (
+      <FILTER period={PERIOD(latency)}
+              input={props.input}
+              output={props.output}
+              {...newContext}>
+        {children}
+      </FILTER>
+    );
+  } else
+    return (
+      <Context.Provider value={newContext}>
+        {RENDER(children,newContext)}
+      </Context.Provider>
+    );
+}
+
+// --------------------------------------------------------------------------
+// --- Form Container
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @summary Form Container
+   @property {string} [className] - Container additional class
+   @property {object} [style] - Container additional style
+   @property {object} [value] - Set the form context value
+   @property {object} [error] - Set the form context error
+   @property {boolean} [disabled] - Disables the form (default is `false`)
+   @property {onChange} [function] - Callback to updated `(value,error)`
+   @property {any} [...props] - [Select](module-dome_layout_forms.Select.html) properties
+   @property {React.children|function} [children] - Fields to populate the form
+   @description
+
+   Setup a form context with the provided values.
+   A local state is maintained unless you fully control the `value` and `error`
+   properties. You may also specify any other properties of the
+   [Select](module-dome_layout_forms.Select.html) filter component.
+
+*/
+
+export const Form = ({className,style,...props}) =>
+  (
+    <div className={CLASSES('dome-xForm-grid',className)}>
+      <Context.Provider value={{}}>
+        <Select state {...props}/>
+      </Context.Provider>
+    </div>
+  );
+
+// --------------------------------------------------------------------------
+// --- Generic Error Badge
+// --------------------------------------------------------------------------
+
+const ERROR_MSG = (error) => {
+  switch(typeof(error)) {
+  case 'undefined':
+    return undefined ;
+  case 'string':
+    return error;
+  case 'object':
+  case 'array':
+    var n = 0;
+    _.forEach(error,(err) => { if (err) n++; });
+    if (n==1) return 'Invalid field' ;
+    if (n>1) return n + ' Invalid fields' ;
+    return undefined ;
+  default:
+    return _.toString(error);
+  }
+};
+
+/**
+   @class
+   @summary Warning badge with description
+   @property {string} [warn] - the short message (hovered)
+   @property {number} [offset] - the label offset (Cf. field)
+   @property {any} [error] - the error description (in tooltip)
+   @property {full} [boolean] - full error message on error (no tooltip, default `false`)
+   @description
+   Display a warning badge with a tooltip when the `error` is not
+   undefined. Otherwize, renders nothing.
+*/
+export const Warning = ({full,warn,offset,error,width}) => {
+  let msg = ERROR_MSG(error);
+  return msg ? (
+    <div className='dome-xIcon dome-xForm-error'
+         style={{top: (offset-2)}} >
+      <SVG id='WARNING' size={11} title={full ? undefined : msg}/>
+      {(full
+        ? <span className='dome-xForm-warning' style={{width: 'max-content'}}>{msg}</span>
+        : <span className='dome-xForm-warning'>{warn}</span>
+       )}
+    </div>
+  ) : null ;
+};
+
+// --------------------------------------------------------------------------
+// --- Section Container
+// --------------------------------------------------------------------------
+
+const TITLE_ENABLED = 'dome-text-title' ;
+const TITLE_DISABLED = 'dome-text-title dome-disabled' ;
+
+/**
+   @class
+   @summary Expandable Section sub-form
+   @property {string} label - Section title
+   @property {string} [title] - Tooltip text
+   @property {string} [path] - Fields selection
+   @property {string} [settings] - User's settings for making fold/unfold state persistent
+   @property {boolean} [unfold] - Default fold/unfold state (default is `false`)
+   @property {any} [...props] - [Select](module-dome_layout_forms.Select.html) properties
+   @property {React.children|function} [children] - Section field content
+   @description
+   Wraps some fields inside a foldable section.
+   When unfolded, the section fields are not visible but still rendered.
+*/
+
+export function Section(props)
+{
+
+  let [ unfold, setUnfold ] = Dome.useState(props.settings,props.unfold);
+
+  const onSwitch = () => setUnfold(!unfold);
+
+  const { label, title, path, children, ...otherProps } = props ;
+
+  return (
+    <Select path={path} {...otherProps}>
+      {(context) => (
+        <React.Fragment>
+          <div className='dome-xForm-section'>
+            <div className='dome-xForm-fold' onClick={onSwitch}>
+              <SVG id={unfold?'TRIANGLE.DOWN':'TRIANGLE.RIGHT'} size={11}/>
+            </div>
+            <label className={ (path && context.disabled) ? TITLE_DISABLED : TITLE_ENABLED }
+                   title={title}>
+              {label}
+            </label>
+            { unfold && path && <Warning full error={context.error}/> }
+          </div>
+          { unfold && RENDER(children,context) }
+          { unfold && <div className='dome-xForm-hsep'/> }
+        </React.Fragment>
+      )}
+    </Select>
+  );
+}
+
+// --------------------------------------------------------------------------
+// --- Block Container
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @summary Full width form block
+   @property {any} [...props] - [Select](module-dome_layout_forms.Select.html) properties
+   @property {React.children|function} [children] - Block content
+   @description
+   Wraps its children inside the entire width of the form.
+*/
+
+export const Block = ({ children, ...props }) => (
+  <Select {...props}>
+    {(context) => (
+      <div className='dome-xForm-block'>
+        {RENDER(children,context)}
+      </div>)}
+  </Select>
+);
+
+// --------------------------------------------------------------------------
+// --- Generic Fields
+// --------------------------------------------------------------------------
+
+let fid = 0 ;
+
+const LABEL_ENABLED  = 'dome-xForm-label dome-text-label';
+const LABEL_DISABLED = 'dome-xForm-label dome-text-label dome-disabled';
+const FIELD_ENABLED  = 'dome-xForm-field dome-text-label';
+const FIELD_DISABLED = 'dome-xForm-field dome-text-label dome-disabled';
+
+/**
+   @class
+   @summary Generic Custom Field
+   @property {string} [label] - Field label
+   @property {string} [title] - Field tooltip text
+   @property {number} [offset] - Vertical label offset (for baseline alignment)
+   @property {boolean|string} [warn] - Display errors (default: `true`)
+   @property {any} [...props] - [Select](module-dome_layout_forms.Select.html) properties
+   @property {React.children|function} [children] - Custom field content
+   @description
+
+Field layout inside a Form container.
+The custom field component is positionned on the left of the label, right-aligned.
+The label itself is left-aligned with the other fields.
+
+The Field component setup a [Select](module-dome_layout_forms.Select.html)
+filter for your custom component. Additionnaly, if you use a custom function rendering,
+the context is enriched with the `id` of the `<label/>` DOM element of the label, that
+you can use with `<input htmlFor={id}/>` as custom field component.
+
+A warning badge is displayed on the right of your custom component, unless `warn:false`
+is specified. When hovered, the badge displays `Error` or the specified `string`. When
+hovered for a while, a full description of the error is displayed in a tooltip.
+*/
+
+export class Field extends React.Component
+{
+
+  constructor(props) {
+    super(props);
+    this.id = props.id || 'DOME$' + (fid++) ;
+  }
+
+  render() {
+    const id = this.id ;
+    const { label, title, offset=1, children, warn='Error', ...props } = this.props ;
+    return (
+      <Select {...props}>
+        {(context) => (
+          <React.Fragment>
+            <label className={context.disabled ? LABEL_DISABLED : LABEL_ENABLED}
+                   style={{top: offset}}
+                   htmlFor={id}
+                   title={title}>
+              {label}
+            </label>
+            <div className={context.disabled ? FIELD_DISABLED : FIELD_ENABLED}>
+              {RENDER(children,Object.assign(context,{id}))}
+              {warn && <Warning offset={offset} warn={warn} error={context.error}/>}
+            </div>
+          </React.Fragment>
+        )}
+      </Select>
+    );
+  }
+
+}
+
+// --------------------------------------------------------------------------
+// --- Field List Container
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @summary Field List Container
+   @property {string} [label] - Field label
+   @property {string} [title] - Field tooltip text
+   @property {boolean} [warn] - Display errors (default: `false`)
+   @property {any} [...props] - [Select](module-dome_layout_forms.Select.html) properties
+   @property {React.children|function} [children] - List field content
+   @description
+
+Render its field children in a right-aligned list on the left of the label.
+Typically designed for a list of checkboxes and radio buttons.
+
+Inside the field list, the field-label column no more exists. Hence, text field labels are
+typically never displayed when placed in a field list.
+
+*/
+export const FieldList = ({ label, title, warn=false, children, ...props }) => (
+  <Field label={label} title={title} warn={warn} {...props}>
+    <div className='dome-xForm-list'>
+      {children}
+    </div>
+  </Field>
+);
+
+// --------------------------------------------------------------------------
+// --- Text Field
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @summary Text Field
+   @property {string} [label] - Field label
+   @property {string} [title] - Field tooltip text
+   @property {string} [placeholder] - Input field place holder
+   @property {string} [className] - Input field additional class
+   @property {object} [style] - Input field additional style
+   @property {any} [...props] - [Select](module-dome_layout_forms.Select.html) properties
+   @description
+   Field with a Text Input element. The default latency is set to 600ms.
+*/
+export const FieldText = ({ className, style, latency=true, placeholder, ...props }) => (
+  <Field offset={4} latency={latency} {...props}>
+    {({id,value,disabled,onChange}) => (
+      <input id={id}
+             type='text'
+             value={value || ''}
+             className={CLASSES('dome-xForm-text-field',className)}
+             style={style}
+             disabled={disabled}
+             placeholder={placeholder}
+             onChange={ONCHANGE(onChange)}
+             />
+    )}
+  </Field>
+);
+
+// --------------------------------------------------------------------------
+// --- Code Field
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @summary Monospaced Text Field
+   @property {string} [label] - Field label
+   @property {string} [title] - Field tooltip text
+   @property {string} [placeholder] - Input field place holder
+   @property {string} [className] - Input field additional class
+   @property {object} [style] - Input field additional style
+   @property {any} [...props] - [Select](module-dome_layout_forms.Select.html) properties
+   @description
+   Field with a Text Input element. The default latency is set to 600ms.
+*/
+export const FieldCode = ({ className, style, latency=true, placeholder, ...props }) => (
+  <Field offset={4} latency={latency} {...props}>
+    {({id,value,disabled,onChange}) => (
+      <input id={id}
+             type='text'
+             value={value || ''}
+             className={CLASSES('dome-xForm-text-field dome-text-code',className)}
+             style={style}
+             disabled={disabled}
+             placeholder={placeholder}
+             onChange={ONCHANGE(onChange)}
+             />
+    )}
+  </Field>
+);
+
+// --------------------------------------------------------------------------
+// --- TextArea Field
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @summary Text Area Field
+   @property {string} [label] - Field label
+   @property {string} [title] - Field tooltip text
+   @property {string} [className] - Input field additional class
+   @property {string} [placeholder] - Input field place holder
+   @property {number} [cols] - Number of columns (default 35, min 5)
+   @property {number} [rows] - Number of lines (default 5, min 2)
+   @property {object} [style] - Input field additional style
+   @property {any} [...props] - [Select](module-dome_layout_forms.Select.html) properties
+   @description
+   Field with a Text Input element. The default latency is set to 900ms.
+*/
+export const FieldTextArea = ({ className, style, cols=35, rows=5,
+                                placeholder, latency=900, ...props }) => (
+  <Field offset={4} latency={latency} {...props}>
+    {({id, value, disabled, onChange }) => (
+      <textarea id={id}
+                type='text'
+                className={CLASSES('dome-xForm-textarea-field',className)} style={style}
+                disabled={disabled}
+                wrap='off' spellchecker='true'
+                value={value || ''}
+                cols={Math.max(5,cols)}
+                rows={Math.max(1,rows-1)}
+                placeholder={placeholder}
+                onChange={ONCHANGE(onChange)}
+                />
+    )}
+  </Field>
+);
+
+// --------------------------------------------------------------------------
+// --- CodeArea Field
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @summary Text Area Field
+   @property {string} [label] - Field label
+   @property {string} [title] - Field tooltip text
+   @property {string} [className] - Input field additional class
+   @property {string} [placeholder] - Input field place holder
+   @property {number} [cols] - Number of columns (default 35, min 5)
+   @property {number} [rows] - Number of lines (default 5, min 2)
+   @property {object} [style] - Input field additional style
+   @property {any} [...props] - [Select](module-dome_layout_forms.Select.html) properties
+   @description
+   Field with a Text Input element. The default latency is set to 900ms.
+*/
+export const FieldCodeArea = ({ className, style, cols=35, rows=5,
+                                placeholder, latency=900, ...props }) => (
+  <Field offset={4} latency={latency} {...props}>
+    {({id, value, disabled, onChange }) => (
+      <textarea id={id}
+                ref={this.area}
+                type='text'
+                className={CLASSES('dome-xForm-textarea-field dome-text-code',className)} style={style}
+                disabled={disabled}
+                wrap='off' spellchecker='false'
+                value={value || ''}
+                cols={Math.max(5,cols)}
+                rows={Math.max(1,rows-1)}
+                placeholder={placeholder}
+                onChange={ONCHANGE(onChange)}
+                />
+    )}
+  </Field>
+);
+
+// --------------------------------------------------------------------------
+// --- Number Field
+// --------------------------------------------------------------------------
+
+const PARSE_NUMBER = (v,debug) => {
+  let n = parseFloat(v);
+  if (Number.isNaN(n)) {
+    let msg = "Invalid number format";
+    if (debug) {
+      let txt = _.toString(v);
+      if (txt.length > 20) txt = txt.substring(0,19)+'…' ;
+      msg += ": «" + txt + "»" ;
+    }
+    throw msg ;
+  }
+  return n;
+};
+const TEXT_OF_NUMBER = (v) => v===undefined ? '' : PARSE_NUMBER(v,true).toLocaleString('en');
+const NUMBER_OF_TEXT = (s) => s==='' ? undefined : PARSE_NUMBER(s.replace(/[ ,]/g,''));
+
+/**
+   @class
+   @summary Number Field
+   @property {string} [label] - Field label
+   @property {string} [title] - Field tooltip text
+   @property {string} [placeholder] - Input field place holder
+   @property {string} [units] - Number units or currency
+   @property {string} [className] - Input field additional class
+   @property {object} [style] - Input field additional style
+   @property {any} [...props] - [Select](module-dome_layout_forms.Select.html) properties
+   @description
+   Field to edit number values with a Text Input element.
+   The default latency is set to 600ms. Numbers are rendered in the english locale, grouping thousands
+   with «,». The currency or units is displayed on the right of the field. The edited text
+   is converted back and forth to number values with `parseFloat`.
+*/
+export const FieldNumber =
+  ({ className, style, latency=true, units, placeholder, ...props }) => (
+    <Field latency={latency}
+           input={TEXT_OF_NUMBER}
+           output={NUMBER_OF_TEXT}
+           {...props}>
+      {({id,value,disabled,onChange}) => (
+        <React.Fragment>
+          <input id={id}
+                 type='text'
+                 value={value || ''}
+                 className={CLASSES('dome-xForm-number-field',className)}
+                 style={style}
+                 disabled={disabled}
+                 placeholder={placeholder}
+                 onChange={ONCHANGE(onChange)}
+                 />
+        {units && <label className='dome-text-label dome-xForm-units'>{units}</label>}
+        </React.Fragment>
+      )}
+    </Field>
+  );
+
+// --------------------------------------------------------------------------
+// --- Spinner field
+// --------------------------------------------------------------------------
+
+const PARSE_INT = (v,debug) => {
+  let n = parseInt(v);
+  if (Number.isNaN(n)) {
+    let msg = "Invalid number format";
+    if (debug) {
+      let txt = _.toString(v);
+      if (txt.length > 20) txt = txt.substring(0,19)+'…' ;
+      msg += ": «" + txt + "»" ;
+    }
+    throw msg ;
+  }
+  return n;
+};
+const TEXT_OF_INT = (v) => v===undefined ? '' : PARSE_INT(v,true);
+const INT_OF_TEXT = (s) => s==='' ? undefined : PARSE_INT(s);
+
+const INT_RANGE = (min,max,warning) => (v) =>
+      (min <= v && v <= max) ? undefined :
+      warning || 'Range ' + min + '…' + max ;
+
+/**
+   @class
+   @summary Spinner Field
+   @property {string} [label] - Field label
+   @property {string} [title] - Field tooltip text
+   @property {string} [placeholder] - Input field place holder
+   @property {string} [units] - Number units or currency
+   @property {number} [min] - Minimum value (default: 0)
+   @property {number} [max] - Maximum value (default: 1000)
+   @property {number} [step] - Stepper increment
+   @property {string} [warning] - Out of bound message (default is explaining the range)
+   @property {string} [className] - Input field additional class
+   @property {object} [style] - Input field additional style
+   @property {any} [...props] - [Select](module-dome_layout_forms.Select.html) properties
+   @description
+   Field to edit integer numbers with a spinner element.
+   The default latency is set to 600ms.
+*/
+export const FieldSpinner =
+  ({ className, style, latency=true, units, placeholder, min=0, max=1000, step, ...props }) => (
+    <Field latency={latency}
+           input={TEXT_OF_INT}
+           output={INT_OF_TEXT}
+           validate={INT_RANGE(min,max)}
+           {...props}>
+      {({id,value,disabled,onChange}) => (
+        <React.Fragment>
+          <input id={id}
+                 type='number'
+                 value={ value === undefined ? '' : value }
+                 className={CLASSES('dome-xForm-spinner-field',className)}
+                 style={style}
+                 min={min}
+                 max={max}
+                 step={step}
+                 disabled={disabled}
+                 placeholder={placeholder}
+                 onChange={ONCHANGE(onChange)}
+                 />
+        {units && <label className='dome-text-label dome-xForm-units'>{units}</label>}
+        </React.Fragment>
+      )}
+    </Field>
+  );
+
+// --------------------------------------------------------------------------
+// --- Slider field
+// --------------------------------------------------------------------------
+
+const RESET_RANGE = (onChange,min,max) =>
+      onChange && (() => onChange(Math.round( min + (max - min) / 2 )));
+
+const SHOW_VALUE = (show,v) => {
+  if (typeof(show)==='function')
+    return show(v);
+  if (v>0) return '+' + v ;
+  if (v<0) return v ;
+  return undefined ;
+};
+
+const SLIDER_VALUE = 'dome-text-label dome-xForm-units dome-xForm-slider-value ' ;
+const SHOW_SLIDER = SLIDER_VALUE + 'dome-xForm-slider-show' ;
+const HIDE_SLIDER = SLIDER_VALUE + 'dome-xForm-slider-hide' ;
+
+class REVEAL extends React.Component {
+
+  constructor(props) {
+    super(props);
+    this.state = { shown: false };
+    this.fadeOut = _.debounce(this.fadeOut,PERIOD(props.latency));
+  }
+
+  componentDidUpdate(prevProps) {
+    if (prevProps.value !== this.props.value) {
+      this.fadeIn();
+      this.fadeOut();
+    }
+  }
+
+  fadeIn() {
+    if (!this.state.shown) this.setState({ shown: true });
+  }
+
+  fadeOut() {
+    if (this.state.shown) this.setState({ shown: false });
+  }
+
+  render() {
+    const { show, value } = this.props ;
+    const { shown } = this.state ;
+    return (
+      <label className={shown ? SHOW_SLIDER : HIDE_SLIDER}>{SHOW_VALUE(show,value)}</label>
+    );
+  }
+
+}
+
+
+/**
+   @class
+   @summary Slider Field
+   @property {string} [label] - Field label
+   @property {string} [title] - Field tooltip text
+   @property {number} [min] - Minimum value (default: -24)
+   @property {number} [max] - Maximum value (default: 24)
+   @property {number} [step] - Stepper increment (default: 1)
+   @property {boolean} [reset] - Reset on double click (default is `true`)
+   @property {boolean|function} [show] - Display the selected value on the right (default is `true`)
+   @property {string} [className] - Input field additional class
+   @property {object} [style] - Input field additional style
+   @property {any} [...props] - [Select](module-dome_layout_forms.Select.html) properties
+   @description
+   Field to edit integer numbers with a spinner element.
+   The default latency is set to 600ms. Double click on the slider reset it
+   to its median value, unless `toreset` is specified. The `show` flag can be set to display
+   the actual value when the slider is dragged. Alternatively, a function can be provided
+   for computing the text to display for the dragged value.
+*/
+export const FieldSlider =
+  ({ className, style, latency=true, min=-24, max=24, step=1, reset=true, show=true, ...props }) => (
+    <Field latency={latency}
+           input={TEXT_OF_INT}
+           output={INT_OF_TEXT}
+           {...props}>
+      {({id,value,disabled,onChange}) => (
+        <React.Fragment>
+          <input id={id}
+                 type='range'
+                 value={ value === undefined ? '' : value }
+                 className={CLASSES('dome-xForm-slider-field',className)}
+                 style={style}
+                 min={min}
+                 max={max}
+                 step={step}
+                 disabled={disabled}
+                 onDoubleClick={reset ? RESET_RANGE(onChange,min,max) : undefined}
+                 onChange={ONCHANGE(onChange)}
+                 />
+          {show && <REVEAL latency={latency} show={show} value={value}/>}
+        </React.Fragment>
+      )}
+    </Field>
+  );
+
+// --------------------------------------------------------------------------
+// --- Date Field
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @summary Date Field
+   @property {string} [label] - Field label
+   @property {string} [title] - Field tooltip text
+   @property {string} [min] - Minimum date (default to `undefined`)
+   @property {string} [max] - Maximum date (default to `undefined`)
+   @property {string} [className] - Input field additional class
+   @property {object} [style] - Input field additional style
+   @property {any} [...props] - [Select](module-dome_layout_forms.Select.html) properties
+   @description
+   Field with a Date Input element. The default latency is set to 600ms.
+   The date is presented in english locale, with format `mm/dd/yyyy`, but the internal value
+   is a string compatible with javascript `Date('yyyy-dd-mm')` format.
+*/
+export const FieldDate = ({ className, style, latency=true, min, max, ...props }) => (
+  <Field latency={latency} {...props}>
+    {({id,value,disabled,onChange}) => (
+      <input id={id}
+             type='date'
+             value={value || ''} min={min} max={max}
+             className={CLASSES('dome-xForm-date-field',className)}
+             style={style}
+             disabled={disabled}
+             onChange={ONCHANGE(onChange)}
+             />
+    )}
+  </Field>
+);
+
+// --------------------------------------------------------------------------
+// --- Time Field
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @summary Time Field
+   @property {string} [label] - Field label
+   @property {string} [title] - Field tooltip text
+   @property {string} [min] - Minimum time (default to `undefined`)
+   @property {string} [max] - Maximum time (default to `undefined`)
+   @property {string} [className] - Input field additional class
+   @property {object} [style] - Input field additional style
+   @property {any} [...props] - [Select](module-dome_layout_forms.Select.html) properties
+   @description
+   Field with a Time Input element. The default latency is set to 600ms.
+   The time is presented in english locale, but its internal value is a string 'hh:mm'
+   on 24h per day basis. This internal format can be used to form a valid javascript
+   `Date('yyyy-mm-ddThh:mm')` object.
+*/
+export const FieldTime = ({ className, style, latency=true, min, max, ...props }) => (
+  <Field latency={latency} {...props}>
+    {({id,value,disabled,onChange}) => (
+      <input id={id}
+             type='time'
+             value={value || ''} min={min} max={max}
+             className={CLASSES('dome-xForm-time-field',className)}
+             style={style}
+             disabled={disabled}
+             onChange={ONCHANGE(onChange)}
+             />
+    )}
+  </Field>
+);
+
+// --------------------------------------------------------------------------
+// --- Date Field
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @summary Color Field
+   @property {string} [label] - Field label
+   @property {string} [title] - Field tooltip text
+   @property {string} [className] - Input field additional class
+   @property {object} [style] - Input field additional style
+   @property {any} [...props] - [Select](module-dome_layout_forms.Select.html) properties
+   @description
+   Field with a Text Input element. The default latency is set to 600ms.
+*/
+export const FieldColor = ({ className, style, latency=true, ...props }) => (
+  <Field latency={latency} {...props}>
+    {({id,value,disabled,onChange}) => (
+      <input id={id}
+             type='color'
+             value={value || '#ffffff'}
+             className={CLASSES('dome-xForm-color-field',className)}
+             style={style}
+             disabled={disabled}
+             onChange={ONCHANGE(onChange)}
+             />
+    )}
+  </Field>
+);
+
+// --------------------------------------------------------------------------
+// --- Select Field
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @summary Select Field
+   @property {string} [label] - Field label
+   @property {string} [title] - Field tooltip text
+   @property {string} [placeholder] - Selector placeholder
+   @property {string} [className] - Input field additional class
+   @property {object} [style] - Input field additional style
+   @property {any} [...props] - [Select](module-dome_layout_forms.Select.html) properties
+   @property {option|optgroup} [children] - HTML options of the `<select>` element
+   @description
+   Field with a Select element. Children must be standard `<option>` and `<optgroup>` elements.
+   Typically:
+
+   ```
+   <FieldSelect placeholder='Choose a value'>
+      <option value='A'>Item A</option>
+      <option value='B'>Item B</option>
+      <option value='C'>Item C</option>
+   </FieldSelect>
+   ```
+
+*/
+export const FieldSelect = ({ className, style, children, placeholder, ...props }) => (
+  <Field {...props}>
+    {({id,value,disabled,onChange}) => (
+      <Selector id={id}
+                className={className}
+                style={style}
+                placeholder={placeholder}
+                value={value}
+                onChange={onChange} >
+        {children}
+      </Selector>
+    )}
+  </Field>
+);
+
+// --------------------------------------------------------------------------
+// --- CheckBox Field
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @summary Checkbox Field
+   @property {string} [label] - Field label
+   @property {string} [title] - Field tooltip text
+   @property {boolean} [inverted] - Inverted value (incompatible with `input` and `output` properties)
+   @property {any} [...props] - [Select](module-dome_layout_forms.Select.html) properties
+   @description
+   A check box field.
+*/
+
+export const FieldCheckbox = ({ label, title, inverted, ...props }) => {
+  let transform = inverted ? (v) => !v : (v) => !!v ;
+  return (
+    <Select input={transform} output={transform} {...props}>
+      {({value,disabled,onChange}) => (
+        <Checkbox className={disabled ? FIELD_DISABLED : FIELD_ENABLED}
+                  label={label} title={title}
+                  disabled={disabled}
+                  value={value} onChange={onChange}/>
+      )}
+    </Select>
+  );
+};
+
+// --------------------------------------------------------------------------
+// --- Radio Field
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @summary Radio Button Field
+   @property {string} [label] - Field label
+   @property {string} [title] - Field tooltip text
+   @property {any} [value] - Value associated with the radio button
+   @property {any} [...props] - [Select](module-dome_layout_forms.Select.html) properties
+   @description
+   A radio button field.
+
+   <strong>Note:</strong> there is no need for using a radio group here,
+   since the selected value is taken from the context.
+*/
+
+export const FieldRadio = ({ label, title, value, ...props }) => {
+  return (
+    <Select {...props}>
+      {({value:selection,disabled,onChange}) => (
+        <Radio className={disabled ? FIELD_DISABLED : FIELD_ENABLED}
+               label={label} title={title}
+               disabled={disabled}
+               value={value}
+               selection={selection}
+               onSelection={onChange}/>
+      )}
+    </Select>
+  );
+};
+
+// --------------------------------------------------------------------------
+// --- Exports
+// --------------------------------------------------------------------------
+
+export default {
+  Select,
+  Form,
+  Block,
+  Field,
+  Section,
+  FieldText,
+  FieldCode,
+  FieldTextArea,
+  FieldCodeArea,
+  FieldNumber,
+  FieldSpinner,
+  FieldSlider,
+  FieldDate,
+  FieldTime,
+  FieldColor,
+  FieldSelect,
+  FieldList,
+  FieldCheckbox,
+  FieldRadio
+};
+
+// --------------------------------------------------------------------------
diff --git a/ivette/src/dome/src/renderer/layout/frames.js b/ivette/src/dome/src/renderer/layout/frames.js
new file mode 100644
index 0000000000000000000000000000000000000000..d16aa614cf787a5b170f2a9e9ee4b4bf258ae7bb
--- /dev/null
+++ b/ivette/src/dome/src/renderer/layout/frames.js
@@ -0,0 +1,211 @@
+// --------------------------------------------------------------------------
+// --- Frames
+// --------------------------------------------------------------------------
+
+/** @module dome/layout/frames */
+
+import React from 'react' ;
+import Dome from 'dome' ;
+import { dispatch } from 'dome/misc/register' ;
+import { Vfill } from 'dome/layout/boxes' ;
+import { SideBar } from 'dome/layout/sidebars' ;
+import { ToolBar } from 'dome/layout/toolbars' ;
+import { TabsBar , TabContent } from 'dome/layout/tabs' ;
+import { Splitter } from 'dome/layout/splitters' ;
+
+const callbacks = (f,g) => (f ? (g ? (id) => { f(id); g(id); } : f ) : g );
+
+// --------------------------------------------------------------------------
+// --- Frame Component
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @summary Main view with optional tabs, toolbar and sidebar.
+   @property {string} [settings] - User-setting base name for
+   persistent selection and customization
+   @property {string} [selectedTab] - Currently selected tab
+   @property {string} [selectedItem] - Currently selected sidebar item
+   @property {string} [defaultTab] - Default selected tab (see also default tab's property)
+   @property {string} [defaultItem] - Default selected item
+   @property {function} [onTabClose] - tab closing callback
+   @property {function} [onTabSelection] - tab selection callback
+   @property {function} [onItemSelection] - sidebar item selection callback
+   @property {boolean} [hideToolbar] - hide tool bar (default is `false`)
+   @property {boolean} [hideTabsbar] - hide tabs bar (default is `false`)
+   @description
+
+   ##### Content
+   The Frame layout and its decorations are specified according to
+   the type of its children elements:
+   - [Tab](module-dome_layout_tabs.Tab.html), if any, will be rendered into an optional
+   [TabsBar](module-dome_layout_tabs.TabsBar.html) and the selected tab content
+   will be rendered into the main content of the frame.
+   - [Sidebar items](module-dome_layout_sidebars.html), if any, will be rendered
+   inside a sidebar on the left of the frame.
+   - [Toolbar items](module-dome_layout_toolbars.html), if any,
+   will be rendered inside a toolbar on top of the frame.
+   - The remaining elements will be rendered (inside a vertical box) into the
+   main content the frame, provided there is no defined tab.
+
+
+   ##### Callbacks
+   Both general callbacks and tab ones are invoked
+   when the user select or close a tab. The tab callbacks are invoked _before_ the
+   frame ones.
+
+
+   ##### Settings
+   When provided, user selection will be saved in user's settings
+   under the following keys (when relevant):
+   - `'<settings>.dome.tab'` tab selection
+   - `'<settings>.dome.item'` item selection
+   - `'<settings>.dome.sidebar'` position of side-bar splitter
+   - `'<settings>.dome.section'` visibility of side-bar sections
+*/
+
+export class Frame extends React.Component
+{
+
+  constructor(props)
+  {
+    super(props);
+    const settings = props.settings ;
+    const derived = ( selected , user , def ) =>
+          (selected || ( settings && Dome.getWindowSetting( settings + user ) ) || def) ;
+    var selectedTab = derived( props.selectedTab , '.dome.tab' , props.defaultTab );
+    var selectedItem = derived( props.selectedItem , '.dome.item' , props.defaultItem );
+    this.state = { selectedTab , selectedItem };
+    this.handleSelectTab = this.handleSelectTab.bind(this);
+    this.handleSelectItem = this.handleSelectItem.bind(this);
+  }
+
+  // --------------------------------------------------------------------------
+  // --- Life Cycle
+  // --------------------------------------------------------------------------
+
+  componentWillMount() {
+    const onTabSelection = this.props.onTabSelection ;
+    if (onTabSelection) {
+      const stateId = this.state.selectedTab ;
+      const propsId = this.props.selectedTab ;
+      if (stateId !== propsId) setImmediate(() => onTabSelection( stateId ));
+    }
+    const onItemSelection = this.props.onItemSelection ;
+    if (onItemSelection) {
+      const stateId = this.state.selectedItem ;
+      const propsId = this.props.selectedItem ;
+      if (stateId !== propsId) setImmediate(() => onItemSelection( stateId ));
+    }
+  }
+
+  componentDidUpdate() {
+    const tabid = this.props.selectedTab ;
+    if (tabid && this.state.selectedTab !== tabid)
+      this.setState( { selectedTab: tabid } );
+    const itemid = this.props.selectedItem ;
+    if (itemid && this.state.selectedItem !== itemid)
+      this.setState( { selectedItem: itemid } );
+  }
+
+  // --------------------------------------------------------------------------
+  // --- Update
+  // --------------------------------------------------------------------------
+
+  handleSelectTab(id) {
+    this.setState( { selectedTab: id } );
+    const settings = this.props.settings ;
+    const onSelect = this.props.onTabSelection ;
+    settings && Dome.setWindowSetting( settings + '.dome.tab' , id );
+    onSelect && onSelect( id );
+  }
+
+  handleSelectItem(id) {
+    this.setState( { selectedItem: id } );
+    const settings = this.props.settings ;
+    const onSelect = this.props.onItemSelection ;
+    settings && Dome.setWindowSetting( settings + '.dome.item' , id );
+    onSelect && onSelect( id );
+  }
+
+  // --------------------------------------------------------------------------
+  // --- Rendering
+  // --------------------------------------------------------------------------
+
+  render() {
+
+    const content = dispatch( this.props.children , {
+      tabsItems: 'DOME_TABSBAR_ITEM',
+      sideItems: 'DOME_SIDEBAR_ITEM',
+      toolItems: 'DOME_TOOLBAR_ITEM',
+      others: undefined
+    });
+
+    var selection = this.state.selectedTab ;
+    if (selection === undefined)
+      React.Children.forEach( content.tabsItems , (tab) => {
+        if (!selection && tab && tab.props.default)
+          selection = tab.props.ident ;
+      });
+
+    const makeTab = (tab) => {
+      const onSelection = callbacks( tab.props.onSelection , this.handleSelectTab );
+      const onClose  = callbacks( tab.props.onClose , this.props.onTabClose );
+      const props = { selection , onSelection , onClose , content: false } ;
+      return React.cloneElement( tab , props , null );
+    };
+
+    const makeContent = (tab) => {
+      const props = {
+        selection ,
+        onSelection : undefined,
+        onClose : undefined,
+        content: true
+      };
+      return React.cloneElement( tab , props );
+    };
+
+    var tabs , main ;
+    const settings = this.props.settings ;
+    const tools = content.toolItems ;
+
+    if (content.tabsItems) {
+      tabs = React.Children.map( content.tabsItems , (tab) => tab && makeTab(tab) );
+      main = React.Children.map( content.tabsItems , (tab) => tab && makeContent(tab) );
+      if (content.others) console.warn('Unexpected main content for Frame with Tabs');
+    } else {
+      tabs = undefined ;
+      main = content.others ;
+    }
+
+    if (content.sideItems) {
+      const sidebarOffset = settings && (settings + '.dome.sidebar');
+      const sidebarSections = settings && (settings + '.dome.section');
+      main = (
+        <Splitter dir='LEFT' settings={sidebarOffset} >
+          { <SideBar
+                settings={sidebarSections}
+                selection={this.state.selectedItem}
+                onSelection={this.handleSelectItem}
+              >{content.sideItems}</SideBar> }
+          { main }
+        </Splitter>
+      );
+    }
+
+    return (
+      <Vfill>
+        { !this.props.hideToolbar && tools && <ToolBar>{tools}</ToolBar> }
+        { !this.props.hideTabsbar && tabs && <TabsBar>{tabs}</TabsBar> }
+        { main }
+      </Vfill>
+    );
+  }
+
+}
+
+// --------------------------------------------------------------------------
+
+export default { Frame };
+
+// --------------------------------------------------------------------------
diff --git a/ivette/src/dome/src/renderer/layout/grids.css b/ivette/src/dome/src/renderer/layout/grids.css
new file mode 100644
index 0000000000000000000000000000000000000000..f7f8ce35b9f83a6f47acb0cb81f7b2bf6b9d6913
--- /dev/null
+++ b/ivette/src/dome/src/renderer/layout/grids.css
@@ -0,0 +1,51 @@
+/* -------------------------------------------------------------------------- */
+/* --- GridLayout Styles                                                  --- */
+/* -------------------------------------------------------------------------- */
+
+.dome-xGridLayout {
+    flex: 1 1 auto ;
+    width: 100% ;
+    height: 100% ;
+    position: relative ;
+    overflow: auto ;
+}
+
+.dome-xGridLayout-splitter {
+    z-index: 1 ;
+    position: absolute ;
+}
+
+.dome-xGridLayout-hsplit { cursor: col-resize }
+.dome-xGridLayout-hdrag  { cursor: ew-resize  }
+.dome-xGridLayout-vsplit { cursor: row-resize }
+.dome-xGridLayout-vdrag  { cursor: ns-resize  }
+
+.dome-xGridLayout-target {
+    z-index: 2 ;
+    position: absolute ;
+    border-radius: 12px ;
+    background: #64b4f0 ;
+    opacity: 0.5 ;
+}
+
+.dome-xGridLayout-holder {
+    z-index: -1 ;
+    position: absolute ;
+    overflow: hidden ;
+    background: lightblue ;
+    opacity: 0.5 ;
+}
+
+.dome-xGridLayout-item {
+    z-index: 0 ;
+    position: absolute ;
+    overflow: hidden ;
+}
+
+.dome-xGridLayout-item.dome-dragging
+{
+    z-index: +1 ;
+    transition: all 0.1s ease-out ;
+}
+
+/* -------------------------------------------------------------------------- */
diff --git a/ivette/src/dome/src/renderer/layout/grids.js b/ivette/src/dome/src/renderer/layout/grids.js
new file mode 100644
index 0000000000000000000000000000000000000000..2361960b0d4ec208fc0c486b34be1e3b87cc2dc2
--- /dev/null
+++ b/ivette/src/dome/src/renderer/layout/grids.js
@@ -0,0 +1,1348 @@
+// --------------------------------------------------------------------------
+// --- Grid Layout
+// --------------------------------------------------------------------------
+
+/** @module dome/layout/grids */
+
+import React from 'react' ;
+import Dome from 'dome' ;
+import { dispatchEvent, DnD, DragSource, DropTarget } from 'dome/dnd' ;
+import { AutoSizer } from 'react-virtualized' ;
+import { DraggableCore } from 'react-draggable' ;
+import _ from 'lodash' ;
+import './grids.css' ;
+
+// --------------------------------------------------------------------------
+// --- Grid Utilities
+// --------------------------------------------------------------------------
+
+function flatten( dir, gs )
+{
+  const content = [] ;
+  gs && gs.forEach((g) => {
+    let kd = g && g.kind ;
+    if (kd)
+      if ( kd === dir ) {
+        if (g.content.length > 0) content.push( ...g.content );
+      } else
+        content.push( g );
+  });
+  return content ;
+}
+
+function iter( grid , fn )
+{
+  function visit(g) {
+    if (!g) return;
+    if (g.kind === 'ITEM') fn(g);
+    else g.content.forEach(visit);
+  }
+  visit(grid);
+}
+
+function get( g, id )
+{
+  if (g) {
+    if (g.id === id) return g;
+    switch( g.kind ) {
+    case 'HBOX':
+    case 'VBOX':
+      const gs = g.content ;
+      var k = 0;
+      while (k < gs.length) {
+        let r = get(gs[k],id);
+        if (r) return r;
+        k++;
+      }
+    }
+  }
+  return undefined;
+}
+
+/**
+   @summary Pretty-print shape.
+ */
+
+export function stringOfShape( s )
+{
+  if (!s) return "<>" ;
+  switch(s.kind) {
+  case 'ITEM':
+    if (s.switch || s.height)
+      return s.id + "(" + s.width + "x" + s.height + ")" ;
+    else
+      return s.id ;
+  case 'HBOX': return "H<" + s.content.map(stringOfShape).join(',') + ">" ;
+  case 'VBOX': return "V<" + s.content.map(stringOfShape).join(',') + ">" ;
+  default: return "<?" + s + "?>";
+  }
+}
+
+// --------------------------------------------------------------------------
+// --- Shape Utilities
+// --------------------------------------------------------------------------
+
+/**
+   @summary Iterates over items in a shape.
+   @parameter {Shape} shape - the shape to iter over
+   @parameter {function} job - the function to apply on each
+   @description
+   The iteree receives `job(id,width,height)` for each `ITEM` in the shape.
+ */
+export function iterShape( shape, job )
+{
+  iter( shape , (item) => job(item.id,item.width,item.height) );
+}
+
+/**
+   @summary Returns the shape element of the given item identifier.
+   @parameter {Shape} shape - the shape to look into
+   @parameter {string} id - the item identifier
+   @return {object} the associated item object, or `undefined`
+ */
+export function getShapeItem( shape, id )
+{
+  return get(shape,id);
+}
+
+/**
+   @summary Remove an item or sub-grid from the shape.
+   @parameter {Shape} shape - the shape to filter
+   @parameter {string} id - the item or grid identifier to remove
+   @return {Shape} the filtered shape
+ */
+export function removeShapeItem( shape, id )
+{
+  const visit = (s) => {
+    if (s && s.id !== id) {
+      switch(s.kind) {
+      case 'ITEM':
+        return s;
+      case 'HBOX':
+      case 'VBOX':
+        return makeShapeBox( s.kind , s.id, s.content.map(visit) );
+        //otherwize continue
+      }
+    }
+    return null;
+  };
+  return visit(shape);
+}
+
+
+// --------------------------------------------------------------------------
+// --- Dimensions
+// --------------------------------------------------------------------------
+
+function shareDim( d , ds )
+{
+  d.min = 0;
+  d.size = 0;
+  d.next = 0;
+  d.ext = 0;
+  d.fill = false;
+  d.resize = false;
+  ds.forEach( (di) => {
+    d.min  += di.min ;
+    d.size += di.size ;
+    if (di.resize) d.resize = true ;
+    if (di.fill) {
+      d.fill = true ;
+      d.ext  += di.min ;
+      d.next ++ ;
+    } else
+      d.ext  += di.size ;
+  });
+}
+
+function stackDim( d , ds )
+{
+  d.min = 0;
+  d.size = 0;
+  d.fill = 0;
+  d.resize = 0;
+  ds.forEach( (di) => {
+    d.min   = Math.max( d.min,  di.min  );
+    d.size  = Math.max( d.size, di.size );
+    if (di.fill) d.fill = true ;
+    if (di.resize) d.resize = true ;
+  });
+}
+
+
+// --------------------------------------------------------------------------
+// --- Sequence Layout
+// --------------------------------------------------------------------------
+
+/* Distributes extra available space w.r.t respective ratio. */
+function layoutShare( d0 , ds )
+{
+  const d = d0.dim ;
+  const s = d0.size ;
+  const e = d0.ext ;
+  const m = d0.min ;
+
+  let flayout ;
+
+  if ( s <= d ) {
+    const K = d0.next ;
+    flayout = (di) =>
+      di.fill ? di.size + ( d - s ) / K : di.size ;
+  } else if ( e <= d ) {
+    // Have e <= d < s, Hence e < s
+    const alpha = (d - e) / (s - e) ;
+    flayout = (di) =>
+      di.fill ? di.min + alpha * (di.size - di.min) : di.size ;
+  } else if ( m <= d ) {
+    // Have m <= d < {e,s}, Hence m < e
+    const alpha = (d - m) / (e - m) ;
+    flayout = (di) =>
+      !di.fill ? di.min + alpha * (di.size - di.min) : di.min ;
+  } else if ( m > 0 ) {
+    const alpha = d / m ;
+    flayout = (di) => alpha * di.min ;
+  } else {
+    flayout = (di) => 0 ;
+  }
+
+  var p0 = d0.pos ;
+  var p1 = p0 ;
+  var ps = 0 ;
+  ds.forEach((di) => {
+    ps += di.drag ? di.drag : flayout(di) ; // Avoids cumulative rounding
+    let p2 = p0 + Math.round(ps) ;
+    di.pos = p1 ;
+    di.dim = p2-p1 ;
+    p1 = p2 ;
+  });
+}
+
+// --------------------------------------------------------------------------
+// --- Parallel Layout
+// --------------------------------------------------------------------------
+
+/* Apply available space to all elements */
+
+function layoutStack( d0 , ds )
+{
+  const p = d0.pos ;
+  const d = d0.dim ;
+  ds.forEach((di) => {
+    di.pos = p ;
+    di.dim = di.drag ? di.drag : (di.fill ? d : Math.min( d , di.size )) ;
+    di.room = d - di.dim ;
+  });
+}
+
+// --------------------------------------------------------------------------
+// --- Layout Grid
+// --------------------------------------------------------------------------
+
+function layoutGrid(grid,width,height)
+{
+  function visit(g) {
+    if (!g) return ;
+    switch( g.kind ) {
+    case 'ITEM':
+      break;
+    case 'HBOX':
+      layoutShare( g.dx , g.dxs );
+      layoutStack( g.dy , g.dys );
+      g.content.forEach(visit);
+      break;
+    case 'VBOX':
+      layoutStack( g.dx , g.dxs );
+      layoutShare( g.dy , g.dys );
+      g.content.forEach(visit);
+      break;
+    }
+  }
+  grid.dx.pos = 0; grid.dy.pos = 0;
+  grid.dx.dim = width; grid.dy.dim = height;
+  visit(grid);
+}
+
+// --------------------------------------------------------------------------
+// --- Freezing
+// --------------------------------------------------------------------------
+
+function freezeGrid( g )
+{
+  if (!g) return;
+  switch(g.kind) {
+  case 'ITEM':
+    g.dx.size = g.dx.dim ;
+    g.dy.size = g.dy.dim ;
+    break;
+  case 'HBOX':
+    g.content.forEach( freezeGrid );
+    shareDim(g.dx,g.dxs);
+    stackDim(g.dy,g.dys);
+    break;
+  case 'VBOX':
+    g.content.forEach( freezeGrid );
+    stackDim(g.dx,g.dxs);
+    shareDim(g.dy,g.dys);
+    break;
+  }
+}
+
+function resizeGrid(g,dgx,dgy)
+{
+  if (!g) return;
+  switch(g.kind) {
+  case 'ITEM':
+    if (dgx && !g.dx.room) g.dx.drag = dgx ;
+    if (dgy && !g.dy.room) g.dy.drag = dgy ;
+    break;
+  case 'HBOX':
+    g.content.forEach( (g) => resizeGrid(g,g.dx.drag,dgy) );
+    break;
+  case 'VBOX':
+    g.content.forEach( (g) => resizeGrid(g,dgx,g.dy.drag) );
+    break;
+  }
+}
+
+// --------------------------------------------------------------------------
+// --- Grid Constructors
+// --------------------------------------------------------------------------
+
+const BOXID = 'DOME$' ;
+const MINDIM = 12 ;
+const INSETS = 6 ; // For insertion borders
+
+function makeGridItem( content, width, height )
+{
+  if (content.display !== undefined && !content.display) return undefined ;
+  const dimension = (d) => Math.max( d || 0 , MINDIM );
+  const w  = dimension(content.width) ;
+  const h  = dimension(content.height);
+  const mw = dimension(content.minWidth);
+  const mh = dimension(content.minHeight);
+  const dx = { size: Math.max( mw , w ), min: Math.min( mw , w ), resize:false, fill:false };
+  const dy = { size: Math.max( mh , h ), min: Math.min( mh , h ), resize:false, fill:false };
+  switch( content && content.fill ) {
+  case 'both':       dx.fill = true ; dy.fill = true ; break;
+  case 'horizontal': dx.fill = true ; break;
+  case 'vertical':   dy.fill = true ; break;
+  }
+  switch( content && content.resize ) {
+  case undefined:    dx.resize = dx.fill ; dy.resize = dy.fill; break;
+  case 'both':       dx.resize = true ; dy.resize = true ; break;
+  case 'horizontal': dx.resize = true ; break;
+  case 'vertical':   dy.resize = true ; break;
+  }
+  if (width && dx.resize && dx.min <= width)
+    dx.size = width ;
+  if (height && dy.resize && dy.min <= height)
+    dy.size = height ;
+  const id = content.id ;
+  return { kind: 'ITEM', dx, dy, id, content };
+}
+
+function makeGridHbox( id, gs )
+{
+  const content = flatten( 'HBOX' , gs );
+  switch(content.length) {
+  case 0: return null ;
+  case 1: return content[0];
+  default:
+    const dxs = content.map((g) => g.dx);
+    const dys = content.map((g) => g.dy);
+    const dx = {} ;
+    const dy = {} ;
+    shareDim(dx,dxs);
+    stackDim(dy,dys);
+    return { kind: 'HBOX', id , dx, dxs, dy, dys, content };
+  }
+}
+
+function makeGridVbox( id, gs )
+{
+  const content = flatten( 'VBOX' , gs );
+  switch(content.length) {
+  case 0: return null ;
+  case 1: return content[0];
+  default:
+    const dxs = content.map((g) => g.dx);
+    const dys = content.map((g) => g.dy);
+    const dx = {} ;
+    const dy = {} ;
+    stackDim(dx,dxs);
+    shareDim(dy,dys);
+    return { kind: 'VBOX', id, dx, dxs, dy, dys, content };
+  }
+}
+
+function makeGridDir( dir, id, gs )
+{
+  switch( dir || 'vertical') {
+  case 'horizontal':
+    return makeGridHbox(id,gs);
+  case 'vertical':
+    return makeGridVbox(id,gs);
+  default:
+    return null;
+  }
+}
+
+function makeShapeDir( dir, id, content )
+{
+  switch( dir || 'vertical') {
+  case 'horizontal':
+    return makeShapeBox('HBOX',id,content);
+  case 'vertical':
+    return makeShapeBox('VBOX',id,content);
+  default:
+    return null;
+  }
+}
+
+// --------------------------------------------------------------------------
+// --- Shapes
+// --------------------------------------------------------------------------
+
+function makeShapeBox( kind, id, gs )
+{
+  const content = flatten( kind, gs );
+  switch(content.length) {
+  case 0: return null ;
+  case 1: return content[0];
+  default: return { kind, id, content };
+  }
+}
+
+function makeShapeOfGrid( g )
+{
+  let kind = g && g.kind ;
+  switch( kind ) {
+  case 'HBOX':
+  case 'VBOX':
+    return { kind, id: g.id, content: g.content.map(makeShapeOfGrid) };
+  case 'ITEM':
+    return { kind:'ITEM', id: g.id, width: g.dx.dim, height: g.dy.dim };
+  default:
+    return null;
+  }
+}
+
+const BOXINSERT = 'DOME$INSERT' ;
+let insertId = 0 ;
+
+function makeShapeInsert( shape,at,dir,item )
+{
+  const visit = (s) => {
+    if (!s || s.id === item.id) return null;
+    switch( s.kind ) {
+    case 'HBOX':
+    case 'VBOX':
+      s = makeShapeBox( s.kind , s.id, s.content.map(visit) );
+      // then falls through
+    }
+    if (s.id === at) {
+      const id = BOXINSERT + (insertId++) ;
+      switch( dir ) {
+      case 'NORTH': return makeShapeBox('VBOX', id, [ item , s ]);
+      case 'SOUTH': return makeShapeBox('VBOX', id, [ s , item ]);
+      case 'EAST':  return makeShapeBox('HBOX', id, [ s , item ]);
+      case 'WEST':  return makeShapeBox('HBOX', id, [ item , s ]);
+        // otherwize falls through
+      }
+    }
+    return s ;
+  };
+  return at === '*' ? item : at === item.id ? shape : visit(shape);
+}
+
+// --------------------------------------------------------------------------
+// --- Insertions
+// --------------------------------------------------------------------------
+
+const quadrant = ( dx , dy , x , y ) => {
+  let dir, best = 10000 ;
+  const qdir = ( d , w ) => { if (0 <= w && w < best) { best = w ; dir = d; } };
+  qdir( 'NORTH', y - dy.pos );
+  qdir( 'SOUTH', dy.pos + dy.dim - y );
+  qdir( 'EAST',  dx.pos + dx.dim - x );
+  qdir( 'WEST',  x - dx.pos );
+  return dir;
+};
+
+const isHead = (d,p) => ( d.pos <= p && p <= d.pos + INSETS );
+const isTrail = (d,p) => ( d.pos + d.dim - INSETS <= p && p <= d.pos + d.dim ) ;
+const inside = (x,y) => (g) => {
+  const dx = g.dx ;
+  const dy = g.dy ;
+  return ( dx.pos < x && x < dx.pos + dx.dim &&
+           dy.pos < y && y < dy.pos + dy.dim );
+} ;
+
+const targetAt = ( grid , x , y ) => {
+  if (!grid) return undefined;
+  const dx = grid.dx ;
+  const dy = grid.dy ;
+  let dir ;
+  switch( grid.kind ) {
+  case 'ITEM':
+    dir = quadrant(dx,dy,x,y);
+    break;
+  case 'HBOX':
+    if (isHead(dy,y)) { dir = 'NORTH'; break; }
+    if (isTrail(dy,y)) { dir = 'SOUTH'; break; }
+    return targetAt( grid.content.find(inside(x,y)), x , y );
+  case 'VBOX':
+    if (isHead(dx,x)) { dir = 'WEST'; break; }
+    if (isTrail(dx,x)) { dir = 'EAST'; break; }
+    return targetAt( grid.content.find(inside(x,y)), x , y );
+  }
+  return dir && Object.assign( { at:grid.id, dir } , makePosition(grid) ) ;
+};
+
+const TARGET_PLAIN = { at:'*', dir:'*', left:0, top:0 };
+
+// --------------------------------------------------------------------------
+// --- Resizing Events
+// --------------------------------------------------------------------------
+
+const onResizeStart = ({ id,resizer,dp,dq }) => {
+  dp.root = dp.dim ;
+  dq.root = dq.dim ;
+  freezeGrid( resizer.grid );
+  resizer.id = id ;
+  resizer.splitters = [resizer.splitters[id]] ;
+  resizer.element.setState({ resizer });
+};
+
+const onResizeDrag = ({ id,resizer,dp,dq },offset) => {
+  offset = Math.max( dp.min - dp.root , offset );
+  offset = Math.min( dq.root - dq.min , offset );
+  resizer.offset = offset ;
+  dp.drag = dp.root + offset ;
+  dq.drag = dq.root - offset ;
+  resizeGrid( resizer.grid );
+  resizer.element.forceUpdate();
+};
+
+const onResizeStop = ({ resizer }) => {
+  let shape = makeShapeOfGrid( resizer.grid );
+  resizer.element.setShape( shape );
+};
+
+const onResizeReset = ({ resizer }) => {
+  resizer.element.setShape( undefined );
+};
+
+// --------------------------------------------------------------------------
+// --- Splitters & Targets
+// --------------------------------------------------------------------------
+
+const ITEMCLASS = 'dome-xGridLayout-item dome-container' ;
+const HDRAGZONE = 'dome-xGridLayout-splitter dome-xGridLayout-hsplit dome-color-dragzone' ;
+const HDRAGGING = 'dome-xGridLayout-splitter dome-xGridLayout-hdrag  dome-color-dragging' ;
+const VDRAGZONE = 'dome-xGridLayout-splitter dome-xGridLayout-vsplit dome-color-dragzone' ;
+const VDRAGGING = 'dome-xGridLayout-splitter dome-xGridLayout-vdrag  dome-color-dragging' ;
+
+class HSPLIT extends React.Component {
+  render() {
+    let {x,y,width,height,...handler} = this.props ;
+    let id = handler.id ;
+    let { id:sid , offset=0 } = handler.resizer ;
+    return (
+      <DraggableCore
+        onStart={() => {
+          onResizeStart(handler);
+          this.forceUpdate();
+        }}
+        onDrag={(_evt,data) => {
+          onResizeDrag(handler,data.x - x + 2);
+          this.forceUpdate();
+        }}
+        onStop={() => onResizeStop(handler)}
+        >
+        <div className={ id === sid ? HDRAGGING : HDRAGZONE }
+             onClick={(evt) => evt.altKey && onResizeReset(handler)}
+             style={{ top:y, height, left:x+offset-2, width:4 }} />
+      </DraggableCore>
+    );
+  }
+}
+
+class VSPLIT extends React.Component {
+  render() {
+    let {x,y,width,height,...handler} = this.props ;
+    let id = handler.id ;
+    let { id:sid , offset=0 } = handler.resizer ;
+    return (
+      <DraggableCore
+        onStart={() => {
+          onResizeStart(handler);
+          this.forceUpdate();
+        }}
+        onDrag={(_evt,data) => {
+          onResizeDrag(handler,data.y - y + 2);
+          this.forceUpdate();
+        }}
+        onStop={() => onResizeStop(handler)}
+        >
+        <div className={ id === sid ? VDRAGGING : VDRAGZONE }
+             onDoubleClick={(evt) => evt.altKey && onResizeReset(handler)}
+             style={{ top:y+offset-2, height:4, left:x, width }} />
+      </DraggableCore>
+    );
+  }
+}
+
+const TARGET = ({target:{ dir,left,top,width,height }}) => {
+  switch(dir) {
+  case 'NORTH':
+    top = top - 3 ; height = 6 ;
+    break;
+  case 'SOUTH':
+    top = top + height - 3 ; height = 6 ;
+    break;
+  case 'WEST':
+    left = left -3 ; width = 6 ;
+    break;
+  case 'EAST':
+    left = left + width -3 ; width = 6 ;
+    break;
+  }
+  let style = { left, top, width, height };
+  return <div className='dome-xGridLayout-target' style={style} />;
+};
+
+const HOLDER = ({position}) => (
+  <div className='dome-xGridLayout-holder' style={position} />
+);
+
+const ELEMENT = ({id,className,style,children}) => (
+  <div key={id} className={className} style={style}>
+    { children }
+  </div>
+);
+
+// --------------------------------------------------------------------------
+// --- Grid Splitters
+// --------------------------------------------------------------------------
+
+function splitSequence( d,ds,fn )
+{
+  const n = ds.length || 0 ;
+  if (n<=0) return;
+  const dt = ds[n-1] ;
+  const dn = { resize:false, dim:0, min:0 };
+  const trail = d.room ;
+  if (trail > 0) {
+    dn.resize = true; dn.dim = trail;
+    let i,p,di ;
+    for (i = 0, p = -1; i < n; i++) {
+      di = ds[i];
+      p = di.resize ? i : p ;
+      if (p>=0) {
+        fn(di.pos+di.dim,ds[p],dn);
+      }
+    }
+  } else {
+    const dr = ds.slice(1).concat(dn);
+    const ps = Array(n);
+    const qs = Array(n);
+    let i,k ;
+    for (i = 0, k = -1; i < n; i++)
+      k = ps[i] = ds[i].resize ? i : k ;
+    for (i = n-1, k = -1; 0 <= i; i--)
+      k = qs[i] = dr[i].resize ? i : k ;
+    ds.forEach((di,i) => {
+      var p = ps[i];
+      var q = qs[i];
+      if (p>=0 && q>=0)
+        fn(di.pos+di.dim,ds[p],dr[q]);
+    });
+  }
+}
+
+function extendItem( d , fn ) {
+  if (d.resize && d.room > 0) {
+    const df = { resize:true, dim:d.room, min:0 };
+    fn(d.pos + d.dim,d,df);
+  }
+};
+
+function splitGrid( resizer,grid )
+{
+  var SID = 0 ;
+  const splitters = resizer.splitters ;
+  const makeHsplit = (dy) => (x,dp,dq) => {
+    var id = SID++;
+    var s = (
+      <HSPLIT id={id} key={id} resizer={resizer}
+              x={x} y={dy.pos} height={dy.dim}
+              dp={dp} dq={dq}
+              />) ;
+    splitters.push(s);
+  };
+  const makeVsplit = (dx) => (y,dp,dq) => {
+    var id = SID++;
+    var s = (
+      <VSPLIT id={id} key={id} resizer={resizer}
+              x={dx.pos} y={y} width={dx.dim}
+              dp={dp} dq={dq}
+              />) ;
+    splitters.push(s);
+  };
+  function visit(g) {
+    if (!g) return;
+    switch(g.kind) {
+    case 'ITEM':
+      extendItem( g.dx , makeHsplit(g.dy) );
+      extendItem( g.dy , makeVsplit(g.dx) );
+      return;
+    case 'HBOX':
+      splitSequence( g.dx , g.dxs , makeHsplit(g.dy) );
+      g.content.forEach(visit);
+      return;
+    case 'VBOX':
+      splitSequence( g.dy , g.dys , makeVsplit(g.dx) );
+      g.content.forEach(visit);
+      return;
+    }
+  }
+  visit(grid);
+}
+
+// --------------------------------------------------------------------------
+// --- Grid Elements
+// --------------------------------------------------------------------------
+
+const makePosition = ( {dx,dy}, padding=0 ) => ({
+  left:    dx.pos + padding,
+  top:     dy.pos + padding,
+  width:   dx.dim - padding,
+  height:  dy.dim - padding
+});
+
+function makeElement( render = ELEMENT, item, padding )
+{
+  if (!item) return null;
+  const { id, content:{className:itemClass,style:itemStyle,children,...itemProps} } = item;
+  const position = makePosition( item, padding );
+  const className = itemClass ? ITEMCLASS + " " + itemClass : ITEMCLASS ;
+  const style = Object.assign( {}, itemStyle , position );
+  return render({id,className,style,children,...itemProps});
+}
+
+function orderElements( e1 , e2 )
+{
+  let a = e1.key ;
+  let b = e2.key ;
+  if (!a || a < b) return -1;
+  if (!b || a > b) return 1;
+  return 0;
+}
+
+// --------------------------------------------------------------------------
+// --- GridItem
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @summary Elementary GridLayout Component
+   @property {string} id - Component identifier
+   @property {boolean} display - Whether to mount the component
+   @property {number} width - Desired component width
+   @property {number} height - Desired component height
+   @property {number} minWidth - Minimum component width
+   @property {number} minHeight - Minium component height
+   @property {directions} [fill] - Direction(s) the component fills in
+   @property {directions} [resize] - Direction(s) the component can be resized in
+   @property {directions} [shrink] - Direction(s) the component can be shrinked in
+   @property {string} [className] - Additional class of the component
+   @property {string} [handle] - class name of the handler for dragging the component
+   @property {object} [style] - Additional style of the component
+   @property {React.children} children - grid item content
+   @description
+
+   The `id` property is mandatory for the GridLayout container
+   to property manage its different components. All items in the same GridLayout shall
+   have distinct identifiers.
+
+   Direction properties (with type `directions`) can take the following values:
+   `'none'`, `'horizontal'`, `'vertical'` or `'both'`.
+ */
+export const GridItem = () => null;
+
+// --------------------------------------------------------------------------
+// --- Grid H/V Boxes
+// --------------------------------------------------------------------------
+
+/**
+    @class
+    @property {ident} [id] - the box identifier
+    @property {direction} dir - either `'horizontal'` or `'vertical'`
+    @property {GridContent} [children] - internal grid contents
+    @summary Layout its content in an horizontal or vertical box.
+    @description
+
+This container is a _fake_ component thats simply groups several
+`GridItem`s or `GridBox`es horizontally or vertically.
+
+It can be used only as direct children of `GridLayout`
+and other `GridBox` containers.
+
+See also:
+  - [GridLayout](module-dome_layout_grids.GridLayout.html) top-level container
+  - [GridItem](module-dome_layout_grids.GridItem.html) elementary component
+  - [GridHbox](module-dome_layout_grids.GridHbox.html) horizontal box
+  - [GridVbox](module-dome_layout_grids.GridVbox.html) vertical box
+*/
+export const GridBox = () => null ;
+
+/**
+    @class
+    @summary Layout its content in an horizontal box.
+    @property {ident} [id] - the box identifier
+    @description
+    See [GridBox](module-dome_layout_grids.GridBox.html)
+*/
+export const GridHbox = () => null ;
+
+/**
+    @class
+    @summary Layout its content in a vertical box.
+    @property {ident} [id] - the box identifier
+    @description
+    See [GridBox](module-dome_layout_grids.GridBox.html)
+*/
+export const GridVbox = () => null ;
+
+// --------------------------------------------------------------------------
+// --- User Grid & Components Extraction
+// --------------------------------------------------------------------------
+
+import { register, classOf } from 'dome/misc/register' ;
+
+register(GridBox,  'DOME_GRIDLAYOUT_BOX');
+register(GridHbox, 'DOME_GRIDLAYOUT_HBOX');
+register(GridVbox, 'DOME_GRIDLAYOUT_VBOX');
+register(GridItem, 'DOME_GRIDLAYOUT_ITEM');
+
+/**
+   @summary Extract shape from GridChildren.
+   @parameter {GridChildren} children - Grid items elements arranged in grids
+   @parameter {direction} [dir] - default direction
+   @return {Shape} the associated shape
+   @description
+   Converts a React JSX-based grid of items into a Shape object.
+ */
+export function makeChildrenShape( children, dir )
+{
+  const makeEltShape = (elt) => {
+    let { id, width, height } = elt.props ;
+    return { kind: 'ITEM', id, width, height };
+  };
+
+  const makeBoxShape = (dir,elt) => {
+    let { id, children } = elt.props ;
+    let gs = makeShapes(children);
+    return makeShapeDir(dir,id,gs);
+  };
+
+  const makeShapes = (children) => {
+    return React.Children.map(children,(elt) => {
+      if (!elt) return null;
+      switch( classOf(elt) ) {
+      case 'DOME_GRIDLAYOUT_ITEM':
+        return makeEltShape(elt);
+      case 'DOME_GRIDLAYOUT_HBOX':
+        return makeBoxShape('horizontal',elt);
+      case 'DOME_GRIDLAYOUT_VBOX':
+        return makeBoxShape('vertical',elt);
+      case 'DOME_GRIDLAYOUT_BOX':
+        return makeBoxShape(elt.props.dir,elt);
+      default:
+        console.warn('[Dome.GridLayout] Unknown grid box', elt);
+        return null;
+      }
+    });
+  };
+
+  return makeShapeDir( dir, undefined, makeShapes(children) );
+}
+
+function makeChildrenGrid( dir, items, children, shape )
+{
+  let kid = 0 ;
+  let index = {} ;
+
+  const indexElt = (elt) => {
+    let { id } = elt.props ;
+    let props = index[id];
+    if (!props) props = index[id] = {};
+    Object.assign( props , elt.props );
+    return props;
+  };
+
+  const makeGridElt = (elt) => makeGridItem(indexElt(elt));
+
+  const makeGridBox = (dir,elt) => {
+    let { id, children } = elt.props ;
+    if (!id) id = BOXID + (++kid) ;
+    return makeGridDir(dir,id,makeGrids(children));
+  };
+
+  const makeGrids = (children) => {
+    return React.Children.map(children,(elt) => {
+      if (!elt) return null;
+      switch( classOf(elt) ) {
+      case 'DOME_GRIDLAYOUT_ITEM':
+        return makeGridElt(elt);
+      case 'DOME_GRIDLAYOUT_HBOX':
+        return makeGridBox('horizontal',elt);
+      case 'DOME_GRIDLAYOUT_VBOX':
+        return makeGridBox('vertical',elt);
+      case 'DOME_GRIDLAYOUT_BOX':
+        return makeGridBox(elt.props.dir,elt);
+      default:
+        console.warn('[Dome.GridLayout] Unknown grid box', elt);
+        return null;
+      }
+    });
+  };
+
+  const makeShape = (s) => {
+    switch( s && s.kind ) {
+    case 'ITEM':
+      let props = index[ s.id ] ;
+      if (!props) return null;
+      index[ s.id ] = undefined ;
+      return makeGridItem( props, s.width, s.height );
+    case 'HBOX':
+      return makeGridHbox( s.id, s.content.map(makeShape) );
+    case 'VBOX':
+      return makeGridVbox( s.id, s.content.map(makeShape) );
+    default:
+      return null;
+    }
+  };
+
+  items && items.forEach && items.forEach( indexElt );
+
+  if (shape)
+    return makeShape(shape);
+  else
+    return makeGridDir(dir,BOXID,makeGrids(children));
+}
+
+// --------------------------------------------------------------------------
+// --- GridLayout Core
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @summary Grid-based Flexible Container
+   @property {direction} [dir] - Default children layout (`'horizontal'` or `'vertical'`)
+   @property {number}    [padding] - Padding between items
+   @property {Shape}     [shape] - The desired shape (defaults to children grid)
+   @property {GridItem[]} [items] - A collection of items (defaults to children items)
+   @property {GridContent} [children] - Grid items arranged in box (used for default items and shape)
+   @property {ident}     [dragged]  - Dragged item to materialize
+   @property {object}    [dragging] - Dragging event to localize with `onTarget()`
+   @property {object}    [target]   - Insertion target to materialze
+   @property {function}  [onReshape] - Callback with the updated shape
+   @property {function}  [onTarget] - Callback when dragging is a valid target
+   @property {function}  [render] - Rendering function for items
+   @property {React.ref} [targetRef] - Reference for D&D drop target
+   @description
+
+Low-level GridLayout container, with only resizing capabilities.
+Additional rendering options are available:
+
+- `dragged` : item being dragged, materialized with a dragging background;
+- `dragging` : current [D&D](dome-module_dnd.html) event to notify `onTarget()` with;
+- `target` : `{at,dir,left,top,width,height}` insertion position to materialize;
+- `shape:Shape` : desired reshaping of the children grid;
+- `onReshape(Shape)` : notified when grid content has been resized;
+- `onTarget(Target)` : notified when `dragging` corresponds to a new target;
+- `render({id,className,style,handle,children,...})` : rendering function for the provided item.
+
+The rendering function shall return a keyed React element or fragment.
+It receives all properties of the associated item to render, with
+class and style merged with the `dome-xGridLayout-item` base class
+and the absolute coordinates of the item inside the container.
+
+*/
+
+export class GridLayoutCore extends React.Component
+{
+
+  constructor(props) {
+    super(props);
+    this.state = {};
+  }
+
+  setShape( newshape )
+  {
+    const { onReshape, shape } = this.props ;
+    onReshape && onReshape(newshape);
+    this.setState({ resizer:undefined });
+  }
+
+  render() {
+    const { targetRef, padding=0 } = this.props ;
+    return (
+      <div className='dome-xGridLayout' ref={targetRef}>
+        <AutoSizer key='grid'>{({width,height}) => this.renderSized(width-padding,height-padding)}</AutoSizer>
+      </div>
+    );
+  }
+
+  renderSized(width, height)
+  {
+    const { dragged } = this.props ;
+    const { resizer:resizing } = this.state ;
+    let grid,markers=[] ;
+    if (resizing) {
+      // When Resizing
+      grid = resizing.grid ;
+      markers = resizing.splitters ;
+      layoutGrid(grid,width,height);
+    } else {
+      const {
+        shape, dir,
+        dragging, onTarget,
+        items, children
+      } = this.props ;
+      // Compute Layout
+      grid = makeChildrenGrid( dir, items, children, shape );
+      if (grid) layoutGrid(grid,width,height);
+      // Compute Dragging & notify Target
+      let targetted ;
+      if (dragging) {
+        if (grid) {
+          const r = dragging.targetClientRect ;
+          if (r) {
+            const x = dragging.clientX - r.left ;
+            const y = dragging.clientY - r.top ;
+            targetted = targetAt( grid, x, y );
+          }
+        } else {
+          targetted = Object.assign( { width, height } , TARGET_PLAIN ) ;
+        }
+      }
+      if (onTarget) {
+        const { at,dir } = targetted || {} ;
+        const held = dragging ? dragging.held : false ;
+        if ( this.targetAt !== at ||
+             this.targetDir !== dir ||
+             this.targetHeld !== held )
+        {
+          this.targetAt = at ;
+          this.targetDir = dir ;
+          this.targetHeld = held ;
+          if (targetted) targetted.shape = makeShapeOfGrid(grid);
+          setImmediate(() => onTarget(targetted));
+        }
+      }
+      const { target } = this.props ;
+      if (target && target.at !== dragged) {
+        markers.push( <TARGET key='target' target={target}/> );
+      }
+      if (grid && !dragging && !target) {
+        // Splitters
+        splitGrid( { grid,element:this,splitters:markers } ,grid );
+      }
+    }
+    const elements = [];
+    const { render, padding } = this.props ;
+    iter(grid,(item) => {
+      let e = makeElement(render,item,padding);
+      if (e) elements.push(e);
+      if (dragged && dragged === item.id)
+        markers.push(<HOLDER key='holder' position={makePosition(item)}/>);
+    });
+    return [
+      (<React.Fragment key='items'>{elements.sort(orderElements)}</React.Fragment>),
+      (<React.Fragment key='markers'>{markers}</React.Fragment>)
+    ];
+  };
+}
+
+// --------------------------------------------------------------------------
+// --- GridLayout Full
+// --------------------------------------------------------------------------
+
+const positionOfStyle = ({left,top,width,height}) => ({left,top,width,height});
+
+const DRAGGABLEITEM =
+      (dnd,anchor,onSelfDrag,insert) =>
+      ({id,className,style,handle,children}) =>
+      ( id === insert
+        ? ( <HOLDER key='DOME$holder' position={positionOfStyle(style)}/> )
+        : (
+          <DragSource
+            key={id}
+            dnd={dnd}
+            handle={handle}
+            onStart={() => onSelfDrag(id,{x:style.left,y:style.top})}
+            >
+            {(dragging) => {
+              let theStyle = style ;
+              if (dragging && anchor) {
+                theStyle = Object.assign( {} , style );
+                theStyle.left = anchor.x + dragging.deltaX ;
+                theStyle.top  = anchor.y + dragging.deltaY ;
+                className  += ' dome-dragging' ;
+              }
+              return (<div className={className} style={theStyle}>{children}</div>);
+            }}
+          </DragSource>
+        )
+      );
+
+/**
+   @class
+   @summary Grid Container
+   @property {direction} [dir] - Default children layout (`'horizontal'` or `'vertical'`)
+   @property {number}    [padding] - Padding between items
+   @property {Shape}     [shape] - The desired shape (defaults to children grid)
+   @property {GridItem[]} [items] - A collection of items (defaults to children items)
+   @property {Grid}      [children] - Grid items arranged in box (used for default items and shape)
+   @property {string}    [settings] - User-settings key for making the shape persistent
+   @property {DnD}       [dnd] - Optional D&D controller to be used
+   @property {GridItem}  [holding] - Dragged external element
+   @property {function}  [onTarget] - Callback for dragged target proposal
+   @property {function}  [onReshape] - Callback on reshaping self content
+   @property {function}  [onInsert] - Callback on dropping the holding element
+   @property {function}  [onDnd] - Callback on D&D events
+   @property {function}  [onXxx] - Other D&D [callback events](module-dome_dnd.html)
+   @description
+
+Layout several components with nested vertical and horizontal boxes.
+
+Direct children of the container must be either:
+- [GridBox](module-dome_layout_grids.GridBox.html) and its derivated
+- [GridItem](module-dome_layout_grids.GridItem.html) for elementary component
+
+Top-level children are laidout vertically (default) or horizontally according
+to the `dir` property. Nested boxes of same directions are flattened,
+and singleton boxes simply lift-up their content into their parent box.
+
+You may dynamically change the box hierarchy, without causing unmounting
+and re-monting the different items. Actually, the container renders all
+the collected `GridItem` elements as a flat array ordered by keys.
+
+All item specifications are taken from the provided items. However, the grid
+layout of the items is taken from the provided shape, the settings of the
+children grix boxes, in this order of priority. Extra items or missing items are
+simply ignored from the shape taken into account.
+
+GridLayout containers can be used as drop targets, both for re-arranging
+internal grid items, or to insert external drag sources inside.
+If you supply an external [DnD](module-dome_dnd.DnD.html) controller,
+the GridLayout will register itself as a drop target. Otherwize, it will
+use its own controller.
+
+The callbacks `onReshape` and `onTarget` are invoked on drag and drop operations:
+- `onReshape(Shape)` is invoked when the grid elements are resized or rearranged;
+- `onTarget(Target)` in invoked when a drag source targets an element of the grid;
+- `onInsert(Shape,Target)` when a drag source is dropped inside the grid, at some
+target position, possibly resulting in the given new shape.
+
+When dragging a source over the grid, the grid element specified by the `holding`
+property of the grid is used to materialize the insertion of the dragged source
+inside the grid. To decline a dragging operation, the `onTarget()` must return a
+falsy value other than `undefined` (eg: `false`).
+
+A `Target` is a plain serialisable object with the following properties:
+- `at:ident` the box or item identifier where to insert the target,
+or `'*'` when targetting an empty shape;
+- `dir:'NORTH'|'EAST'|'SOUTH'|'WEST'` the direction to insert the target item along,
+or `'*'` when targetting an empty shape;
+- `id:ident` the inserted item identifier.
+
+A `Shape` is a plain serializable object with the following properties:
+- `{ kind:'HBOX', id, content: [Shape,…] }` for horizontal boxes,
+- `{ kind:'VBOX', id, content: [Shape,…] }` for vertical boxes,
+- `{ kind:'ITEM', id, height, width }` for leaf items.
+
+*/
+export class GridLayout extends React.Component
+{
+
+  constructor(props) {
+    super(props);
+    this.state = {};
+    this.dnd = this.props.dnd || new DnD();
+    this.onReloadSettings = this.onReloadSettings.bind(this);
+    this.onReshape = this.onReshape.bind(this);
+    this.onTarget = this.onTarget.bind(this);
+    this.onSelfDrag = this.onSelfDrag.bind(this);
+    this.onDnd = this.onDnd.bind(this);
+    this.container = React.createRef();
+  }
+
+  componentDidMount() {
+    Dome.on('dome.defaults',this.onReloadSettings);
+  }
+
+  componentWillUnmont() {
+    Dome.off('dome.defaults',this.onReloadSettings);
+  }
+
+  computeTargetProposal(target) {
+    if (!target) return undefined;
+    const { at,dir } = target ;
+    const { dragged } = this.state ;
+    if (dragged)
+      return at === dragged ? undefined : { id:dragged,at,dir };
+    else {
+      const { holding } = this.props ;
+      const id = holding && holding.props && holding.props.id ;
+      return at === id ? undefined : { id,at,dir };
+    }
+  }
+
+  computeNewShapeForTarget(target) {
+    if (!target) return [];
+    const { shape,at,dir } = target ;
+    const { holding } = this.props ;
+    const { dragged,inserted:oldInsert } = this.state ;
+    let item,inserted,newShape ;
+    if (dragged) {
+      item = get(shape,dragged);
+      newShape = makeShapeInsert( shape, at, dir, item );
+    }
+    else if (holding && holding.props) {
+      const { id,width,height } = holding.props ;
+      if (at === id) {
+        inserted = oldInsert ;
+        newShape = shape ;
+      } else {
+        inserted = { at,dir,id } ;
+        item = { kind:'ITEM',id,width,height };
+        newShape = makeShapeInsert( shape,at,dir,item );
+      }
+    }
+    return [inserted,newShape];
+  }
+
+  onReloadSettings() {
+    const { settings, onReshape } = this.props ;
+    if (!settings) return;
+    const newShape = Dome.getWindowSetting( settings );
+    this.setState({ shape: newShape });
+    if (onReshape) onReshape( newShape );
+  }
+
+  onReshape(newShape) {
+    const { settings, shape:setShape, onReshape } = this.props ;
+    if (!setShape) this.setState({ shape: newShape });
+    if (settings) Dome.setWindowSetting( settings, newShape );
+    if (onReshape) onReshape( newShape );
+  }
+
+  onTarget(newTarget) {
+    const proposal = this.computeTargetProposal(newTarget);
+    if (!proposal) this.setState({target:undefined});
+    else {
+      const { onTarget } = this.props ;
+      const { held } = this.state ;
+      const approval = onTarget && onTarget(proposal) ;
+      const valid = approval === undefined || approval ;
+      if (valid) {
+        if ( held ) {
+        const [inserted,holdedShape] = this.computeNewShapeForTarget(newTarget);
+          this.setState({held:false,target:undefined,inserted,holdedShape});
+        } else
+          this.setState({target: newTarget});
+      } else
+        this.setState({target: undefined});
+    };
+  }
+
+  // Self dragged callback, see DRAGGABLEITEM
+  onSelfDrag(dragged,anchor) {
+    this.setState({ dragged, anchor });
+  }
+
+  // Generic D&D event
+  onDnd(dragging) {
+    dispatchEvent(dragging,this.props);
+    switch( dragging && dragging.move ) {
+    case 'HOLD': this.setState({held: true}); break;
+    case 'MOVE': this.setState({held: false}); break;
+    case 'DROP':
+      let { target, inserted, holdedShape:shape } = this.state ;
+      if (target)
+        [inserted,shape] = this.computeNewShapeForTarget(target);
+      if (inserted) {
+        const { onInsert } = this.props ;
+        const callback = onInsert || this.onReshape ;
+        callback && callback(shape,inserted);
+      } else {
+        shape && this.onReshape(shape);
+      }
+      // Continue on 'STOP'
+    case 'STOP':
+      this.setState({
+        dragged: undefined,
+        anchor: undefined,
+        target: undefined,
+        held: undefined,
+        inserted: undefined,
+        holdedShape: undefined
+      });
+      break;
+    }
+  }
+
+  render() {
+    const { shape:propShape, dir, items, children, holding, padding, settings } = this.props ;
+    const { shape:newShape, target, dragged, anchor, holdedShape, inserted } = this.state ;
+    const setShape = propShape === null ? {} : propShape ;
+    const insert = inserted ? inserted.id : undefined ;
+    const render = DRAGGABLEITEM(this.dnd,anchor,this.onSelfDrag,insert);
+    const shape = holdedShape || setShape || newShape || Dome.getWindowSetting(settings) ;
+    return (
+      <DropTarget
+        dnd={this.dnd}
+        targetRef={this.container}
+        onDnd={this.onDnd}
+        >
+        {(dragging) => (
+          <GridLayoutCore
+            targetRef={this.container}
+            dir={dir}
+            padding={padding}
+            shape={shape}
+            items={items}
+            dragging={dragging}
+            onReshape={this.onReshape}
+            onTarget={this.onTarget}
+            dragged={dragged}
+            target={target}
+            render={render}
+            >
+            {children}
+            {inserted ? holding : null}
+          </GridLayoutCore>
+        )}
+      </DropTarget>
+    );
+  }
+}
+
+// --------------------------------------------------------------------------
+// --- Defaults
+// --------------------------------------------------------------------------
+
+export default {
+  GridLayout,
+  GridLayoutCore,
+  GridItem, GridBox, GridHbox, GridVbox,
+  iterShape, getShapeItem, removeShapeItem,
+  stringOfShape,
+  makeChildrenShape
+} ;
+
+// --------------------------------------------------------------------------
diff --git a/ivette/src/dome/src/renderer/layout/pages.css b/ivette/src/dome/src/renderer/layout/pages.css
new file mode 100644
index 0000000000000000000000000000000000000000..6d593e550c77eadf0113b3e25e5d41c7282a0059
--- /dev/null
+++ b/ivette/src/dome/src/renderer/layout/pages.css
@@ -0,0 +1,81 @@
+/* -------------------------------------------------------------------------- */
+/* --- Styling Page                                                       --- */
+/* -------------------------------------------------------------------------- */
+
+.dome-xPages-note {
+    background: white ;
+    padding: 2px ;
+}
+
+.dome-xPages-page {
+    flex: 1 1 auto ;
+    overflow: hidden ;
+}
+
+.dome-xPages-sheet {
+    position: relative ;
+    width: calc(100% - 16px) ;
+    height: calc(100% - 16px) ;
+    margin: 8px ;
+    padding: 8px ;
+    background: white ;
+    border-radius: 2px ;
+    box-shadow: 2px 2px 6px 3px lightgray ;
+    overflow: auto ;
+}
+
+/* -------------------------------------------------------------------------- */
+/* --- Styling Content                                                    --- */
+/* -------------------------------------------------------------------------- */
+
+.dome-pages * {
+    user-select: text ;
+}
+
+.dome-pages h1:first-child {
+    margin-top: 0px ;
+}
+
+.dome-pages h1 {
+    margin-top: 24px ;
+    margin-bottom: 5px ;
+}
+
+.dome-pages h2 {
+    margin-top: 16px ;
+    margin-bottom: 5px ;
+}
+
+.dome-pages h3 {
+    margin-top: 10px ;
+    margin-bottom: 5px ;
+}
+
+.dome-pages h4 {
+    margin-top: 6px ;
+    margin-bottom: 3px ;
+}
+
+.dome-pages h5 {
+    margin-top: 3px ;
+    margin-bottom: 3px ;
+}
+
+.dome-pages p
+{
+    margin-top: 3px ;
+    margin-bottom: 3px ;
+}
+
+.dome-pages ul
+{
+    padding-left: 6px ;
+    margin-left: 16px ;
+}
+
+.dome-pages li
+{
+
+}
+
+/* -------------------------------------------------------------------------- */
diff --git a/ivette/src/dome/src/renderer/layout/pages.js b/ivette/src/dome/src/renderer/layout/pages.js
new file mode 100644
index 0000000000000000000000000000000000000000..bd7b1b6113c20eb69771fded05177d369a3e7bc4
--- /dev/null
+++ b/ivette/src/dome/src/renderer/layout/pages.js
@@ -0,0 +1,81 @@
+// --------------------------------------------------------------------------
+// --- Text Pages
+// --------------------------------------------------------------------------
+
+/**
+   @module dome/layout/pages
+   @description
+   A collection of text area and elements do render textual content.
+
+   Inside such areas, all elements are styled like a classical web page.
+   All elements are encapsulated inside a `<div>` with `'dome-pages'`
+   class name, which can be used in your CSS selectors.
+
+   All the textual parts are selectable inside the page, contrarily to
+   most other widget components.
+
+   The behaviour of `<a href=...>` elements differs for local links and external URLs.
+   Local links trigger a ['dome.href'](dome_.html#~event:'dome.href') event, that
+   you can listen to from the originating renderer process, _eg. by using `Dome.useUpdate()`
+   custom hook.
+
+   External links launch the user's default browser.
+
+*/
+
+import React from 'react' ;
+import { AutoSizer } from 'react-virtualized' ;
+import './pages.css' ;
+
+// --------------------------------------------------------------------------
+// --- Page
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @property {object} [style] - additional style elements
+   @property {Elements} [children] - page content
+   @summary a blank HTML page
+   @description
+
+   The page has insets and shadows and fills the entire available area.
+   Large content is crolled inside in both directions.
+*/
+
+export const Page = ({style,children}) => {
+  return (
+    <div className='dome-xPages-page'>
+      <div className='dome-xPages-sheet dome-pages'>
+        {children}
+      </div>
+    </div>
+  );
+};
+
+// --------------------------------------------------------------------------
+// --- Page
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @property {object} [style] - additional style elements
+   @property {Elements} [children] - page content
+   @summary a blank HTML textarea
+   @description
+
+   The area has small padding and no margin, and does not scroll its content.
+*/
+
+export const Note = ({style,children}) => (
+  <div className=' dome-xPages-note dome-pages' style={style}>
+    {children}
+  </div>
+);
+
+// --------------------------------------------------------------------------
+// Export Default
+// --------------------------------------------------------------------------
+
+export default { Page, Note };
+
+// --------------------------------------------------------------------------
diff --git a/ivette/src/dome/src/renderer/layout/sidebars.css b/ivette/src/dome/src/renderer/layout/sidebars.css
new file mode 100644
index 0000000000000000000000000000000000000000..ca4193112159084ad4cad634f8df75fdd4b13889
--- /dev/null
+++ b/ivette/src/dome/src/renderer/layout/sidebars.css
@@ -0,0 +1,107 @@
+/* -------------------------------------------------------------------------- */
+/* --- Styling SideBars                                                   --- */
+/* -------------------------------------------------------------------------- */
+
+.dome-xSideBar {
+    width: 100% ;
+    height: 100% ;
+    overflow-x: hidden ;
+    overflow-y: auto ;
+    padding-top: 6px ;
+    padding-bottom: 6px ;
+}
+
+.dome-window-active .dome-xSideBar.dome-color-frame {
+    background: #e3e8ec ;
+}
+
+/* -------------------------------------------------------------------------- */
+/* --- SideBar Section                                                    --- */
+/* -------------------------------------------------------------------------- */
+
+.dome-xSideBarSection {
+    display: block ;
+    margin-top: 10px ;
+    margin-bottom: 10px ;
+}
+
+.dome-xSideBarSection-title {
+    padding-left: 3px ;
+    padding-right: 2px ;
+    display: flex ;
+    flew-flow: row nowrap ;
+    align-items: center ;
+}
+
+.dome-xSideBarSection-title > .dome-xLabel {
+    flex: 1 1 ;
+    overflow: hidden ;
+    text-overflow: ellipsis ;
+    font-weight: bold ;
+}
+
+.dome-xSideBarSection-title > .dome-xBadge {
+    flex: 0 0 ;
+}
+
+.dome-xSideBarSection-hideshow {
+    flex: 0 0 ;
+    margin: 1px ;
+    color: #aaa ;
+    width: 32px ;
+    font-weight: normal ;
+    display: inline ;
+    visibility: hidden ;
+}
+
+.dome-xSideBarSection-title:hover .dome-xSideBarSection-hideshow {
+    visibility: visible ;
+}
+
+.dome-xSideBarSection-content {
+    overflow: hidden ;
+    transition: max-height 250ms ease-in-out ;
+}
+
+
+/* -------------------------------------------------------------------------- */
+/* --- SideBar Items                                                      --- */
+/* -------------------------------------------------------------------------- */
+
+.dome-xSideBarItem {
+    padding-left: 2px ;
+    padding-right: 2px ;
+    display: flex ;
+    flex-flow: row nowrap ;
+    align-items: center ;
+}
+
+.dome-window-active .dome-xSideBarItem.dome-active {
+    background: #ccc ;
+}
+
+.dome-window-inactive .dome-xSideBarItem.dome-active {
+    background: #ddd ;
+}
+
+.dome-xSideBarItem > .dome-xLabel {
+    flex: 1 1 ;
+    overflow: hidden ;
+    text-overflow: ellipsis ;
+    padding-left: 12px ;
+}
+
+.dome-xSideBarItem.dome-disabled > .dome-xLabel {
+    color: #b0b0b0 ;
+    fill:  #b0b0b0 ;
+}
+
+.dome-xSideBarItem > .dome-xLabel:last-child {
+    margin-right: 20px ;
+}
+
+.dome-xSideBarItem > .dome-xBadge {
+    flex: 0 0 ;
+}
+
+/* -------------------------------------------------------------------------- */
diff --git a/ivette/src/dome/src/renderer/layout/sidebars.js b/ivette/src/dome/src/renderer/layout/sidebars.js
new file mode 100644
index 0000000000000000000000000000000000000000..39f691c478a7ee6391ba59ea407a552b3d4f2b13
--- /dev/null
+++ b/ivette/src/dome/src/renderer/layout/sidebars.js
@@ -0,0 +1,216 @@
+// --------------------------------------------------------------------------
+// --- SideBars
+// --------------------------------------------------------------------------
+
+/** @module dome/layout/sidebars */
+
+import React from 'react' ;
+import Dome from 'dome' ;
+import { Badge } from 'dome/controls/icons' ;
+import { Label } from 'dome/controls/labels' ;
+import './sidebars.css' ;
+
+const SideBarContext = React.createContext();
+
+// --------------------------------------------------------------------------
+// --- SideBar Container
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @summary Container for sidebar items
+   @property {Elements} [children] - Side bar items
+   @property {string} [settings] - Side bar items settings
+   @property {string} [selection] - Current item selection
+   @property {function} [onSelection] - Selection callback
+   @property {function} [onContextMenu] - Context Menu callback
+   @description
+   See also [Frame](module-dome_layout_frames.Frame.html) containers.
+
+   When a base settings is set on the sidebar, all contained
+   sections and items are attributed derived settings based on their identifiers.
+
+   The global selection state and callback are also propagated to the side bar items.
+ */
+export const SideBar = ({ children , ...props }) => (
+  <div className="dome-xSideBar dome-color-frame">
+    <SideBarContext.Provider value={props}>
+      {children}
+    </SideBarContext.Provider>
+  </div>
+);
+
+const makeSettings = ( globalSettings, { settings, label, id } ) => {
+  if (settings) return settings ;
+  if (globalSettings) {
+    let localId = id || label ;
+    if (localId)
+      return globalSettings + '.' + localId ;
+  }
+  return undefined ;
+};
+
+// --------------------------------------------------------------------------
+// --- Badges Specifications
+// --------------------------------------------------------------------------
+
+const makeBadge = ((element) => {
+  switch(typeof(element)) {
+  case 'number':
+  case 'string':
+    return <Badge value={element}/>;
+  default:
+    return element ;
+  }
+});
+
+// --------------------------------------------------------------------------
+// --- SideBar Section Hide/Show Button
+// --------------------------------------------------------------------------
+
+const HideShow = (props) => (
+  <label className='dome-xSideBarSection-hideshow dome-text-label'
+         onClick={props.onClick} >
+    {props.visible ? 'Hide' : 'Show'}
+  </label>
+);
+
+// --------------------------------------------------------------------------
+// --- SideBar Section
+// --------------------------------------------------------------------------
+
+const disableAll = (children) =>
+      React.Children.map( children , (elt) => React.cloneElement( elt , { disabled: true } ) );
+
+/**
+   @class
+   @summary Sidebar Section.
+   @property {string} [id] - Section identifier (used for derived settings)
+   @property {string} label - **Section label**
+   @property {string} [title] - Section short description (label tooltip)
+   @property {boolean} [enabled] - Enabled section (by default)
+   @property {boolean} [disabled] - Disabled section (not by default)
+   @property {boolean} [unfold] - Fold/unfold local state (default to local state)
+   @property {string} [settings] - Fold/unfold window settings (local state)
+   @property {boolean} [defaultUnfold] - Fold/unfold default state (default is `true`)
+   @property {Badge} [summary] - Badge summary (when content is hidden)
+   @property {Item[]} children - Content items
+   @description
+
+   Unless specified, sections can be hidden on click. When items in the section have badge(s)
+   it is highly recommended to provide a badge summary to be displayed
+   when the content is hidden.
+
+   Sections with no items are not displayed.
+*/
+export function Section(props) {
+
+  const context = React.useContext( SideBarContext );
+  const [ state=true, setState ] = Dome.useState(
+    makeSettings(context,props),
+    props.defaultUnfold
+  );
+  const { enabled=true, disabled=false, unfold, children } = props ;
+
+  if (React.Children.count(children) == 0) return null;
+
+  const dimmed = context.disabled || disabled || !enabled ;
+  const foldable = unfold === undefined ;
+  const visible = foldable ? state : unfold ;
+  const onClick = foldable ? (() => setState(!state)) : undefined ;
+  const maxHeight = visible ? 2000 : 0 ;
+  const subContext = Object.assign( {}, context, { disabled: dimmed } );
+
+  return (
+    <div className='dome-xSideBarSection'>
+      <div className='dome-xSideBarSection-title' title={props.label}>
+        <Label className={ dimmed ? 'dome-disabled' : '' } label={props.label} />
+        {!visible && React.Children.map(props.summary,makeBadge)}
+        {foldable && <HideShow visible={visible} onClick={onClick}/>}
+      </div>
+      <div className='dome-xSideBarSection-content' style={{ maxHeight }}>
+        <SideBarContext.Provider value={subContext}>
+          {children}
+        </SideBarContext.Provider>
+      </div>
+    </div>
+  );
+}
+
+// --------------------------------------------------------------------------
+// --- SideBar Items
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @property {string} [id] - Item identifier
+   @property {string} [label] - **Item label**
+   @property {string} [icon] - Item icon
+   @property {string} [title] - Item tooltip
+   @property {boolean} [enabled] - Enabled item (default is `true`)
+   @property {boolean} [disabled] - Disabled item (default is `false`)
+   @property {boolean} [selected] - Item selection state (default is `SideBar` selection)
+   @property {function} [onSelection] - Selection callback
+   @property {function} [onContextMenu] - Context Menu callback
+   @property {Badge} [badge] - Badge element(s)
+   @property {React.Children} [children] - Item additional content
+   @summary Sidebar Section Items
+   @description
+
+   The item will be highlighted if selected _via_ the `selection` property of the
+   englobing sidebar. The `onSelection` and `onContextMenu` callbacks are invoked with the item identifier.
+   Context menu callback also triggers the selection callback (first).
+   In case callbacks are defined from the englobing sidebar, both are invoked.
+
+   Badges can be single or multiple [Badge](module-dome_controls_icons.Badge.html) values.
+   They are displayed stacked on the right edge of the item.
+
+**/
+export function Item(props)
+{
+  const { selection,
+          disabled: disabledSection,
+          onSelection: ctxtOnSelect,
+          onContextMenu: ctxtOnPopup
+        } = React.useContext(SideBarContext);
+  const { id, selected,
+          disabled=false, enabled=true,
+          onSelection:itemOnSelect,
+          onContextMenu:itemOnPopup
+        } = props ;
+  const isDisabled = disabled || !enabled || disabled ;
+  const isSelected = selected !== undefined ? selected : (selection && id && (selection === id)) ;
+  const onClick = isDisabled ? undefined : () => {
+    itemOnSelect && itemOnSelect(id);
+    ctxtOnSelect && ctxtOnSelect(id);
+  };
+  const onContextMenu = isDisabled ? undefined : () => {
+    onClick();
+    itemOnPopup && itemOnPopup(id);
+    ctxtOnPopup && ctxtOnPopup(id);
+  };
+  const classes = 'dome-xSideBarItem'
+        + ( isSelected ? ' dome-active' : ' dome-inactive' )
+        + ( isDisabled ? ' dome-disabled' : '' );
+  return (
+    <div className={classes} title={props.title}
+         onContextMenu={onContextMenu}
+         onClick={onClick}>
+      <Label icon={props.icon} label={props.label} />
+      {props.children}
+      {React.Children.map(props.badge,makeBadge)}
+    </div>
+  );
+}
+
+// --------------------------------------------------------------------------
+
+import { register } from 'dome/misc/register' ;
+
+register( SideBar, 'DOME_SIDEBAR' );
+register( Section, 'DOME_SIDEBAR_ITEM' );
+register( Item,    'DOME_SIDEBAR_ITEM' );
+
+export default { SideBar, Section, Item } ;
+
+// --------------------------------------------------------------------------
diff --git a/ivette/src/dome/src/renderer/layout/splitters.css b/ivette/src/dome/src/renderer/layout/splitters.css
new file mode 100644
index 0000000000000000000000000000000000000000..0b1e80e44131a434d16e6e165003dc7ee317d95c
--- /dev/null
+++ b/ivette/src/dome/src/renderer/layout/splitters.css
@@ -0,0 +1,51 @@
+/* -------------------------------------------------------------------------- */
+/* ---  XSplitter Style Sheet                                             --- */
+/* -------------------------------------------------------------------------- */
+
+.dome-xSplitter-container {
+    position: relative ;
+    width: 100% ;
+    height: 100% ;
+}
+
+.dome-xSplitPane {
+    position: relative ;
+}
+
+.dome-xSplitter-grab {
+    z-index: 1 ;
+    position: relative ;
+    border: 0 ;
+}
+
+.dome-xSplitter-h {
+    left: -1px ;
+    width: 3px ;
+    height: 100% ;
+    cursor: col-resize ;
+}
+
+.dome-xSplitter-v {
+    top: -1px ;
+    height: 3px ;
+    width: 100% ;
+    cursor: row-resize ;
+}
+
+.dome-xSplitter-no-cursor { }
+.dome-xSplitter-h-cursor  { cursor: col-resize ; }
+.dome-xSplitter-v-cursor  { cursor: row-resize ; }
+
+.dome-window-active .dome-xSplitLine {
+    background: #afafaf ;
+}
+
+.dome-window-inactive .dome-xSplitLine {
+    background: #d6d6d6 ;
+}
+
+.dome-xSplitPane, .dome-xSplitLine {
+    transition: all 0.1s ease-out ;
+}
+
+/* -------------------------------------------------------------------------- */
diff --git a/ivette/src/dome/src/renderer/layout/splitters.js b/ivette/src/dome/src/renderer/layout/splitters.js
new file mode 100644
index 0000000000000000000000000000000000000000..18007cfbbfcd7959ce319e884fb520b1af6977ac
--- /dev/null
+++ b/ivette/src/dome/src/renderer/layout/splitters.js
@@ -0,0 +1,389 @@
+// --------------------------------------------------------------------------
+// --- Splitters
+// --------------------------------------------------------------------------
+
+/** @module dome/layout/splitters */
+
+import React from 'react' ;
+import Props from 'prop-types' ;
+import Dome from 'dome' ;
+import { Layout } from 'dome/misc/events' ;
+import './splitters.css' ;
+import _ from 'lodash' ;
+
+// --------------------------------------------------------------------------
+// --- Splitter Layout
+// --------------------------------------------------------------------------
+
+const SUBVIEW = { overflow: 'hidden' };
+
+const LAYOUT = {
+  // Container Class
+  hcursor: 'dome-xSplitter-h-cursor',
+  vcursor: 'dome-xSplitter-v-cursor',
+  nocursor: 'dome-xSplitter-no-cursor',
+  // Grab Class
+  hgrab: 'dome-xSplitter-grab dome-xSplitter-h dome-color-dragzone',
+  vgrab: 'dome-xSplitter-grab dome-xSplitter-v dome-color-dragzone',
+  hdrag: 'dome-xSplitter-grab dome-xSplitter-h dome-color-dragging',
+  vdrag: 'dome-xSplitter-grab dome-xSplitter-v dome-color-dragging',
+  // Non-Positionned Splitter Styles
+  hsplitter: { width:1, height: '100%' },
+  vsplitter: { height:1, width: '100%' },
+  // Hidden style
+  hide: { display: 'none' },
+  // Non-Positionned Pane Styles
+  fixH: { flex: 'none', height: 'auto', width: '100%' },
+  extH: { flex: 'auto', minHeight: 30, width: '100%' },
+  fixW: { flex: 'none', width: 'auto', height: '100%' },
+  extW: { flex: 'auto', minWidth: 10, height: '100%' },
+  // Non-Positionned Contailer Styles
+  horizontal: {
+    display:'flex', flexFlow: 'row nowrap'
+  },
+  vertical: {
+    display:'flex', flexFlow: 'column nowrap'
+  },
+  // Positionned Container Style
+  container: {
+    display:'block'
+  }
+};
+
+const subview = ( primary,secondary ) =>
+      Object.assign( {} , SUBVIEW , { primary,secondary } );
+
+// Tabulated flex-box styles
+const FLEX = {
+  'HORIZONTAL': subview( LAYOUT.extW, LAYOUT.extW ),
+  'LEFT':       subview( LAYOUT.fixW, LAYOUT.extW ),
+  'RIGHT':      subview( LAYOUT.extW, LAYOUT.fixW ),
+  'VERTICAL':   subview( LAYOUT.extH, LAYOUT.extH ),
+  'TOP':        subview( LAYOUT.fixH, LAYOUT.extH ),
+  'BOTTOM':     subview( LAYOUT.extH, LAYOUT.fixH )
+};
+
+// Tabulated hidden styles
+const HIDDEN = {
+  'HORIZONTAL': subview( LAYOUT.hide, LAYOUT.extW ),
+  'LEFT':       subview( LAYOUT.hide, LAYOUT.extW ),
+  'RIGHT':      subview( LAYOUT.extW, LAYOUT.hide ),
+  'VERTICAL':   subview( LAYOUT.hide, LAYOUT.extH ),
+  'TOP':        subview( LAYOUT.hide, LAYOUT.extH ),
+  'BOTTOM':     subview( LAYOUT.extH, LAYOUT.hide )
+};
+
+// Horizontal / Vertical mode
+const LR = {
+  'HORIZONTAL': true,
+  'LEFT':       true,
+  'RIGHT':      true,
+  'VERTICAL':   false,
+  'TOP':        false,
+  'BOTTOM':     false
+};
+
+// --------------------------------------------------------------------------
+// --- Splitter Component
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @summary Draggable split pane
+   @property {Direction} [dir]  - Layout and dimensionning strategy
+   @property {string} [settings] - User-settings key for persistent splitter position
+   @property {number} [margin]  - Minimal margin from container edges
+   @property {boolean} [unfold] - whether to display or not the slider component (by default)
+   @description
+   Layout two panels horizontally or vertically with a draggable splitter.
+   Clicking the splitter bar with any modifier key restore its natural position.
+
+   The visibility of the slider component (the fixed size one, or the primary one) can
+   be defined by the `unfold` property.
+
+   ##### Direction
+
+   | Tag | Description
+   |:-------------------:|-------------------------------------------------
+   | `'HORIZONTAL'`      | Horizontal layout (default)
+   | `'LEFT'`, `'RIGHT'` | Horizontal layout with _fixed_ left or right component
+   | `'VERTICAL'`        | Vertical layout
+   | `'TOP'`, `'BOTTOM'` | Vertical layout with _fixed_ top or bottom component
+
+   When resizing the entire component, free available space is distributed among both children
+   except when one is _fixed_ by the `dir` property.
+   Typically, if your component has a left side-bar, you would provide a `LEFT` direction
+   to the splitter.
+
+
+   ##### Children
+
+   The component is designed for two children. If no-children is given, the
+   component returns `undefined`. If only one children is provided, it is
+   rendered as is-it, without any additional decoration. Extra chidren after the
+   2nd are simply not rendered.
+
+
+   ##### Borders & Margin
+
+   Primary and secondary components are laidout within positionned div elements with hidden overflows.
+   If your components have borders, you should adjust their CSS box-sizing property to `'border-box'`.
+   Margin with `'100%'` height or width should be avoided and replaced by padding, otherwize
+   the left and bottom sides of the views are likely to be cropped.
+
+*/
+
+export class Splitter extends React.Component {
+
+  // --------------------------------------------------------------------------
+  // --- Life Cycle
+  // --------------------------------------------------------------------------
+
+  constructor(props) {
+    super(props);
+    this.handleReset = this.handleReset.bind(this);
+    this.handleClick = this.handleClick.bind(this);
+    this.handleResize = this.handleResize.bind(this);
+    this.handleDragInit = this.handleDragInit.bind(this);
+    this.handleDragMove = this.handleDragMove.bind(this);
+    this.handleDragStop = this.handleDragStop.bind(this);
+    this.doDragMove = _.throttle( this.doDragMove , 100 );
+    this.lr = LR[ this.props.dir ];
+    this.margin = Math.max( 32 , this.props.margin || 0 );
+    this.state = {
+      dragging: false,   // Splitter is currently dragging
+      absolute: false,   // Splitter is positionned with offset
+      anchor: 0,         // Invariant: anchor == mouse position - offset
+      offset: 0          // Position of splitter edge from container edge
+    };
+  }
+
+  // --------------------------------------------------------------------------
+  // --- Mounting
+  // --------------------------------------------------------------------------
+
+  componentDidMount() {
+    if (this.refs.container && this.refs.splitter) {
+      const container = this.refs.container.getBoundingClientRect() ;
+      const dimension = this.lr ? container.width : container.height ;
+      this.range = dimension - this.margin ;
+      const settings = this.props.settings ;
+      if (settings) {
+        const offset = Dome.getWindowSetting(settings,-1);
+        if (this.margin <= offset && offset <= this.range)
+          this.setState({ absolute: true, offset });
+      }
+    }
+    Dome.on( 'dome.defaults', this.handleReset );
+  }
+
+  componentWillUnmount() {
+    Dome.off( 'dome.defaults', this.handleReset );
+  }
+
+  componentDidUpdate() {
+    this.lr = LR[ this.props.dir ];
+    this.margin = Math.max( 32 , this.props.margin || 0 );
+  }
+
+  // --------------------------------------------------------------------------
+  // --- Reset Offset
+  // --------------------------------------------------------------------------
+
+  handleReset() {
+    this.setState({ absolute: false, dragging: false, anchor: 0, offset: 0 });
+    Dome.setWindowSetting(this.props.settings, -1);
+  }
+
+  handleClick(event) {
+    if (event.altKey || event.ctrlKey || event.cmdKey || event.shiftKey)
+      this.handleReset();
+  }
+
+  // --------------------------------------------------------------------------
+  // --- Resizing Handler
+  // --------------------------------------------------------------------------
+
+  handleResize() {
+    // Defensive
+    if (this.refs.container && this.refs.splitter && this.state.absolute) {
+      let container = this.refs.container.getBoundingClientRect() ;
+      let dimension = this.lr ? container.width : container.height ;
+      if (dimension == 0) return;
+      let newrange = dimension - this.margin ;
+      let newoffset ;
+      switch(this.props.dir) {
+      case 'LEFT':
+      case 'TOP':
+        newoffset = this.state.offset ;
+        break;
+      case 'RIGHT':
+      case 'BOTTOM':
+        newoffset = this.state.offset + newrange - this.range ;
+        break;
+      default:
+        newoffset = this.state.offset + (newrange - this.range) / 2 ;
+        break;
+      }
+      this.range = newrange ;
+      if ( this.margin <= this.range) {
+        if (newoffset > this.range) newoffset = this.range ;
+        if (newoffset < this.margin) newoffset = this.margin ;
+      } else {
+        newoffset = dimension / 2 ;
+      }
+      this.setState( { offset: newoffset } );
+    }
+  }
+
+  // --------------------------------------------------------------------------
+  // --- Init Drag
+  // --------------------------------------------------------------------------
+
+  handleDragInit(evt) {
+    evt.preventDefault();
+    // Invariant: offset == position - anchor
+    let position = this.lr ? evt.clientX : evt.clientY ;
+    // Defensive
+    if (this.refs.splitter && this.refs.container) {
+      let splitter = this.refs.splitter.getBoundingClientRect() ;
+      let container = this.refs.container.getBoundingClientRect() ;
+      let dimension = this.lr ? container.width : container.height ;
+      let offset = this.lr
+          ? splitter.left - container.left
+          : splitter.top - container.top ;
+      this.range = dimension - this.margin ;
+      if (this.margin <= offset && offset <= this.range) {
+        let anchor = position - offset ;
+        this.setState( { dragging: true, absolute: true,
+                         anchor: anchor, offset: offset } );
+      }
+    }
+  }
+
+  // --------------------------------------------------------------------------
+  // --- Move Drag
+  // --------------------------------------------------------------------------
+
+  // Throttled 100ms
+  doDragMove(client) {
+    // Invariant: offset == position - anchor
+    let offset = client - this.state.anchor ;
+    if (this.margin <= offset && offset <= this.range)
+      this.setState( { offset: offset, absolute: true } );
+  }
+
+  // Not throttled (non-persistent event)
+  handleDragMove(evt) {
+    this.doDragMove(this.lr ? evt.clientX : evt.clientY);
+  }
+
+  // --------------------------------------------------------------------------
+  // --- Stop Drag
+  // --------------------------------------------------------------------------
+
+  handleDragStop(evt) {
+    Dome.setWindowSetting( this.props.settings, this.state.offset );
+    this.setState({ dragging: false });
+  }
+
+  // --------------------------------------------------------------------------
+  // --- Rendering
+  // --------------------------------------------------------------------------
+
+  render() {
+    const children = this.props.children ;
+    switch(children.length) {
+    case 0:
+      return undefined;
+    case 1:
+      return children[0];
+    default:
+      let dir = this.props.dir;
+      let dragging = this.state.dragging;
+      let offset = this.state.offset;
+      let cursor = dragging
+          ? (this.lr ? LAYOUT.hcursor : LAYOUT.vcursor)
+          : LAYOUT.nocursor ;
+      let grab = this.state.dragging
+          ? (this.lr ? LAYOUT.hdrag : LAYOUT.vdrag )
+          : (this.lr ? LAYOUT.hgrab : LAYOUT.vgrab ) ;
+      let container, splitter, primary, secondary ;
+      let { unfold=true } = this.props ;
+      let absolute = this.state.absolute ;
+      if (!unfold)
+      {
+        container = LAYOUT.container ;
+        splitter = LAYOUT.hide ;
+        let hidden = HIDDEN[dir] ;
+        primary = hidden.primary ;
+        secondary = hidden.secondary ;
+      }
+      else if (!absolute)
+      {
+        container  = this.lr ? LAYOUT.horizontal : LAYOUT.vertical ;
+        splitter   = this.lr ? LAYOUT.hsplitter : LAYOUT.vsplitter ;
+        let flex = FLEX[dir];
+        primary    = flex.primary ;
+        secondary  = flex.secondary ;
+      }
+      else
+      {
+        container  = LAYOUT.container ;
+        if (this.lr) {
+          primary   = { position: 'absolute', left: 0, width: offset, height: '100%' };
+          splitter  = { position: 'absolute', left: offset, width: 1, height: '100%' };
+          secondary = { position: 'absolute', left: offset+1, right: 0, height: '100%' };
+        } else {
+          primary   = { position: 'absolute', top: 0, height: offset, width: '100%' };
+          splitter  = { position: 'absolute', top: offset, height: 1, width: '100%' };
+          secondary = { position: 'absolute', top: offset+1, bottom: 0, width: '100%' };
+        }
+        Object.assign( primary , SUBVIEW );
+        Object.assign( secondary , SUBVIEW );
+      }
+      return (
+        <div ref="container"
+             onMouseUp={dragging ? this.handleDragStop : undefined}
+             onMouseMove={dragging ? this.handleDragMove : undefined}
+             className={'dome-xSplitter-container ' + cursor}
+             style={container}>
+          <div ref="primary" style={primary} className='dome-xSplitPane dome-container'>
+            {children[0]}
+          </div>
+          <div ref="splitter" style={splitter} className='dome-xSplitLine'>
+            <div className={grab}
+                 onMouseDown={this.handleDragInit}
+                 onClick={absolute ? this.handleClick : undefined} />
+          </div>
+          <div ref="secondary" style={secondary} className='dome-xSplitPane dome-container'>
+            {children[1]}
+          </div>
+          <Layout onResize={this.handleResize} />
+        </div>
+      );
+    }
+  }
+
+}
+
+// --------------------------------------------------------------------------
+// --- Props & Defaults
+// --------------------------------------------------------------------------
+
+Splitter.propTypes = {
+  dir: Props.oneOf(['HORIZONTAL','VERTICAL','LEFT','RIGHT','TOP','BOTTOM']),
+  settings: Props.string,
+  margin: Props.number
+};
+
+Splitter.defaultProps = {
+  dir: 'HORIZONTAL',
+  margin: 32
+};
+
+// --------------------------------------------------------------------------
+
+export default { Splitter };
+
+// --------------------------------------------------------------------------
diff --git a/ivette/src/dome/src/renderer/layout/tabs.css b/ivette/src/dome/src/renderer/layout/tabs.css
new file mode 100644
index 0000000000000000000000000000000000000000..f604114d0a72f62f98505e1456e6111fc7375c4e
--- /dev/null
+++ b/ivette/src/dome/src/renderer/layout/tabs.css
@@ -0,0 +1,98 @@
+/* -------------------------------------------------------------------------- */
+/* --- Tabs & Tab Style Sheet                                             --- */
+/* -------------------------------------------------------------------------- */
+
+/* Tabs Bar */
+
+.dome-xTabsBar {
+    flex: 0 1 auto ;
+    position: relative ;
+    height: 20px ;
+    width: calc( 100% + 1px ); /* hides last border */
+    display: flex ;
+    flex: row wrap ;
+    border-width: 1px ;
+    border-bottom-style: solid ;
+}
+
+/* Tab Content */
+
+.dome-xTab-content {
+    width: 100% ;
+    height: 100% ;
+}
+
+/* Tab Item */
+
+.dome-xTab {
+    flex: 1 1 150px ;
+    position: relative ;
+    font-size: 10px ;
+    text-align: center ;
+    padding: 4px 20px 4px 20px ;
+    border-color: inherit ;
+    border-width: 1px ;
+    border-top-style: none ;
+    border-bottom-style: none ;
+    border-left-style: none ;
+    border-right-style: solid ;
+}
+
+.dome-window-inactive .dome-xTab.dome-inactive {
+    color: #606060 ;
+    border-color: inherit ;
+}
+
+.dome-xTab.dome-inactive:hover {
+    background: #bababa ;
+}
+
+.dome-xTab.dome-inactive {
+    color: #606060 ;
+    border-color: #9c9c9c ;
+    background: #b4b4b4 ;
+}
+
+.dome-window-inactive .dome-xTab.dome-inactive {
+    color: #b0b0b0 ;
+    background: #e6e6e6 ;
+}
+
+/* Closing Tab button */
+
+.dome-xTab-closing {
+    display: inline ;
+    border-radius: 4px ;
+    position: absolute ;
+    width: 12px ;
+    height: 12px ;
+    padding: 2px ;
+    top: 2px ;
+    opacity: 0 ;
+    transition: opacity .1s linear 0.1s , background .1s linear 0.1s ;
+}
+
+.dome-platform-darwin .dome-xTab-closing {
+    right: initial ;
+    left: 5px ;
+}
+
+.dome-xTab-closing {
+    right: 5px ;
+    left: initial ;
+}
+
+.dome-active .dome-xTab-closing:hover {
+    background: #c0c0c0 ;
+}
+
+.dome-inactive .dome-xTab-closing:hover {
+    background: #a0a0a0 ;
+}
+
+.dome-xTab:hover .dome-xTab-closing , .dome-xTab:hover .dome-xTab-closing
+{
+    opacity: 1 ;
+}
+
+/* -------------------------------------------------------------------------- */
diff --git a/ivette/src/dome/src/renderer/layout/tabs.js b/ivette/src/dome/src/renderer/layout/tabs.js
new file mode 100644
index 0000000000000000000000000000000000000000..19987a0948c9d522ae7220b72689802227213077
--- /dev/null
+++ b/ivette/src/dome/src/renderer/layout/tabs.js
@@ -0,0 +1,123 @@
+import React from 'react' ;
+import { Icon } from 'dome/controls/icons' ;
+import './tabs.css' ;
+
+/** @module dome/layout/tabs */
+
+// --------------------------------------------------------------------------
+// --- Tabs Bar
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @summary Pure Container for tab buttons.
+   @description
+   Shall contains [Tab](module-dome_layout_tabs.Tab.html) instances.
+*/
+
+export function TabsBar(props) {
+  return (
+    <div className='dome-xTabsBar dome-color-frame'>
+      {props.children}
+    </div>
+  );
+}
+
+// --------------------------------------------------------------------------
+// --- Single Tab
+// --------------------------------------------------------------------------
+
+const VISIBLE = { display: 'block' };
+const HIDDEN  = { display: 'none' };
+
+/**
+   @class
+   @summary Pure Component for rendering a tab button.
+   @property {string} [id] - tab's identifier
+   @property {component} [label] - tab's label
+   @property {string} [title] - tab's tooltip
+   @property {string} [closing] - closing button's tooltip (defaults to `"Close Tab"`)
+   @property {string} [selection] - Currently selected tab identifier
+   @property {function} [onSelection] - selection callcack, feed with value
+   @property {function} [onClose] - selection callcack, feed with value
+   @property {boolean} [default] - if `true`, selected tab by default
+   @property {boolean} [content] - render content instead of tab
+   @description
+   A single tab selector. Shall only be used as a children
+   of [TabsBar](module-dome_layout_tabs.TabsBar.html)
+   or [Frame](module-dome_layout_frames.Frame.html).
+
+   When `content` is positionned, the component renders
+   its content children instead of the tab button. In such a case, content
+   is displayed only when selected.
+
+   __Remark__: on Mac OSX, the close button appears on the left-side of the tab.
+*/
+
+export function Tab(props) {
+  const selected = props.id === props.selection ;
+  if (props.content) {
+    //--- Content Rendering
+    const style = selected ? VISIBLE : HIDDEN ;
+    return (
+      <div className='dome-xTab-content dome-container' style={style}>
+        {props.children}
+      </div>
+    );
+  } else {
+    //--- Tab Rendering
+    const onClick = props.onSelection && (() => props.onSelection(props.id));
+    const onClose = props.onClose && (() => props.onClose(props.id));
+    var closing = onClose ? (
+      <Icon className="dome-xTab-closing"
+            title={props.closing || "Close Tab"}
+            name="CROSS" onClick={onClose} />
+    ) : undefined ;
+    const classes = 'dome-xTab' + ( selected ? ' dome-active' : ' dome-inactive' ) ;
+    return (
+      <label
+        className={classes} title={props.title} onClick={onClick}>
+        {props.label}
+        {closing}
+      </label>
+    );
+  }
+}
+
+// --------------------------------------------------------------------------
+// --- TabsPane
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @summary Pure Container for tab contents.
+   @property {string} selection - tab identifier to render
+   @property {Tab[]} children - tabs (content) to render
+   @description
+   Shall contains [Tab](module-dome_layout_tabs.Tab.html) instances.
+   Only the selected tab identifier will be displayed, although all tab
+   contents remains properly mounted. If no tab is selected,
+   no content is displayed.
+*/
+
+export function TabsPane({ selection, children }) {
+  const showTab = (elt) => React.cloneElement(elt, { content: true, selection });
+  return (
+    <React.Fragment>
+      {React.Children.map(children , showTab)}
+    </React.Fragment>
+  );
+}
+
+// --------------------------------------------------------------------------
+// --- Dome Class (private)
+// --------------------------------------------------------------------------
+
+import { register } from 'dome/misc/register' ;
+
+register(TabsBar, 'DOME_TABSBAR');
+register(Tab,     'DOME_TABSBAR_ITEM');
+
+export default { Tab , TabsBar , TabsPane } ;
+
+// --------------------------------------------------------------------------
diff --git a/ivette/src/dome/src/renderer/layout/toolbars.css b/ivette/src/dome/src/renderer/layout/toolbars.css
new file mode 100644
index 0000000000000000000000000000000000000000..2e5df08568f9ba6b34411a0bb5473e9d31fc2382
--- /dev/null
+++ b/ivette/src/dome/src/renderer/layout/toolbars.css
@@ -0,0 +1,235 @@
+/* -------------------------------------------------------------------------- */
+/* --- Styling ToolBars                                                   --- */
+/* -------------------------------------------------------------------------- */
+
+.dome-xToolBar {
+    flex: 0 0 auto ;
+    width: 100% ;
+    min-height: 24px ;
+    display: flex ;
+    flex-direction: row ;
+    flex-wrap: wrap ;
+    align-items: baseline ;
+    border-width: 1px ;
+    border-bottom-style: solid ;
+}
+
+.dome-xToolBar:last-child {
+    border-top-style: solid ;
+    border-bottom-style: none ;
+}
+
+/* -------------------------------------------------------------------------- */
+/* --- Styling ToolBar Spaces                                             --- */
+/* -------------------------------------------------------------------------- */
+
+.dome-xToolBar-inset {
+    flex: 0 1 5px ;
+}
+
+.dome-xToolBar-space {
+    flex: 0 1 20px ;
+}
+
+.dome-xToolBar-filler {
+    flex: 1 1 20px ;
+}
+
+.dome-xToolBar-separator + .dome-xToolBar-separator {
+    display: none ;
+}
+
+.dome-xToolBar-separator {
+    flex: 0 0 10px ;
+    align-self: center ;
+}
+
+.dome-xToolBar-vrule {
+    width: 2px ;
+    position: relative ;
+    left: 4px ;
+    height: 18px ;
+}
+
+.dome-window-active .dome-xToolBar-vrule {
+    background: #aaa ;
+}
+
+.dome-window-inactive .dome-xToolBar-vrule {
+    background: #ddd ;
+}
+
+/* -------------------------------------------------------------------------- */
+/* --- Styling ToolBar Buttons                                            --- */
+/* -------------------------------------------------------------------------- */
+
+/* Layout */
+
+.dome-xToolBar-Control {
+    flex: 0 0 auto ;
+    height: 21px ;
+    margin: 6px 5px 6px 5px ;
+    padding: 0px 4px 2px 4px ;
+    box-shadow: 0 1px 1px rgba(0, 0, 0, .06);
+    border: 1px solid transparent;
+    border-radius: 4px;
+    outline: none ;
+}
+
+.dome-xToolBar-Control svg {
+    height: 10px ;
+    position: relative ;
+}
+
+.dome-xToolBar-Control svg + label {
+    margin-left: 4px ;
+}
+
+/* Background */
+
+.dome-window-active .dome-xToolBar-Control {
+    background-image: linear-gradient(to bottom, #e8e8e8 0, #f1f1f1 100%);
+}
+
+.dome-window-active .dome-xToolBar-Control:hover:not(:disabled) {
+    background-color: #ffffff ;
+    background-image: none ;
+}
+
+.dome-window-active .dome-xToolBar-Control.dome-xToolBar-positive:not(:disabled) {
+    background-image: linear-gradient(to bottom, #34ff52 0%, #48fd64 100%);
+}
+
+.dome-window-active .dome-xToolBar-Control.dome-xToolBar-positive:hover:not(:disabled) {
+    background-color: #00ff00 ;
+    background-image: none ;
+}
+
+.dome-window-active .dome-xToolBar-Control.dome-xToolBar-negative:not(:disabled) {
+    color: #ccc ;
+    fill: #ccc ;
+    background-image: linear-gradient(to bottom, #ec453e 0%, #ff4c47 100%);
+}
+
+.dome-window-active .dome-xToolBar-Control.dome-xToolBar-negative:hover:not(:disabled) {
+    background-color: red ;
+    background-image: none ;
+}
+
+.dome-window-active .dome-xToolBar-Control.dome-xToolBar-warning:not(:disabled) {
+    background-image: linear-gradient(to bottom, #fece72 0%, #fcaa0e 100%);
+}
+
+.dome-window-active .dome-xToolBar-Control.dome-xToolBar-warning:hover:not(:disabled) {
+    background-color: orange ;
+    background-image: none ;
+}
+
+.dome-window-active .dome-xToolBar-Control.dome-xToolBar-cancel:not(:disabled) {
+    background-color: #c2c0c2 ;
+    background-image: none ;
+}
+
+.dome-window-active .dome-xToolBar-Control.dome-xToolBar-cancel:hover:not(:disabled) {
+    background-image: linear-gradient(to bottom, #e8e8e8 0, #f1f1f1 100%);
+}
+
+.dome-window-inactive .dome-xToolBar-Control {
+    box-shadow: none ;
+    background-image: none ;
+}
+
+/* Activated */
+
+.dome-window-active .dome-xToolBar-Control:active:not(:disabled) {
+    fill: #ddd ;
+    color: #ddd ;
+    background-color: gray ;
+    background-image: none ;
+}
+
+/* Disabled */
+
+.dome-window-active .dome-xToolBar-Control:disabled {
+    fill: #ccc ;
+    color: #ccc ;
+    box-shadow: none ;
+    border-color: #bbb ;
+}
+
+.dome-window-inactive .dome-xToolBar-Control:disabled {
+    fill: #ccc ;
+    color: #ccc ;
+}
+
+/* Selected */
+
+.dome-window-active .dome-xToolBar-Control.dome-selected {
+    fill:  #fff;
+    color: #fff;
+    border: 1px solid transparent ;
+    background-color: #7f7f7f ;
+    background-image: none ;
+}
+
+.dome-window-active .dome-xToolBar-Control.dome-selected:hover {
+    background-color: #888 ;
+    background-image: none ;
+}
+
+.dome-window-inactive .dome-xToolBar-Control.dome-selected {
+    fill:  #eee;
+    color: #eee;
+    background-color: #ccc ;
+}
+
+/* Selected & Disabled */
+
+.dome-window-active .dome-xToolBar-Control.dome-selected:disabled {
+    fill: #ccc ;
+    color: #ccc ;
+    border: 1px solid #bbb ;
+    background-color: #eee ;
+}
+
+.dome-window-inactive .dome-xToolBar-Control.dome-selected:disabled {
+    fill: #ccc ;
+    color: #ccc ;
+    border-color: #ddd ;
+    background-color: #eee ;
+}
+
+/* -------------------------------------------------------------------------- */
+/* --- Styling ToolBar Button Group                                       --- */
+/* -------------------------------------------------------------------------- */
+
+.dome-xToolBar-Group {
+    display: flex ;
+    flex-direction: row ;
+    flex-wrap: nowrap ;
+    margin: 6px 5px 6px 5px ;
+}
+
+.dome-xToolBar-Group .dome-xToolBar-Control {
+    margin: 0 ;
+}
+
+.dome-xToolBar-Group .dome-xToolBar-Control:not(:first-child) {
+    border-left: 0 ;
+}
+
+.dome-xToolBar-Group > .dome-xToolBar-Control:first-child {
+    border-top-right-radius: 0 ;
+    border-bottom-right-radius: 0 ;
+}
+
+.dome-xToolBar-Group > .dome-xToolBar-Control:last-child {
+    border-top-left-radius: 0 ;
+    border-bottom-left-radius: 0 ;
+}
+
+.dome-xToolBar-Group > .dome-xToolBar-Control:not(:first-child):not(:last-child) {
+    border-radius: 0 ;
+}
+
+/* -------------------------------------------------------------------------- */
diff --git a/ivette/src/dome/src/renderer/layout/toolbars.js b/ivette/src/dome/src/renderer/layout/toolbars.js
new file mode 100644
index 0000000000000000000000000000000000000000..0aa477f44ff2a38aafb7b1a5f65c3a4c4a98f6c4
--- /dev/null
+++ b/ivette/src/dome/src/renderer/layout/toolbars.js
@@ -0,0 +1,198 @@
+// --------------------------------------------------------------------------
+// --- ToolBars
+// --------------------------------------------------------------------------
+
+/** @module dome/layout/toolbars */
+
+import React from 'react' ;
+import './toolbars.css' ;
+
+// --------------------------------------------------------------------------
+// --- ToolBar Container
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @summary Container for toolbar items
+   @description
+   See also [Frame](module-dome_layout_frames.Frame.html) containers.
+ */
+export class ToolBar extends React.Component {
+
+  constructor(props) {
+    super(props);
+  }
+
+  render() {
+    const children = this.props.children ;
+    return React.Children.count(children) > 0 && (
+      <div className='dome-xToolBar dome-color-frame'>
+        <div className='dome-xToolBar-inset'/>
+        {children}
+        <div className='dome-xToolBar-inset'/>
+      </div>
+    );
+  }
+
+}
+
+// --------------------------------------------------------------------------
+// --- ToolBar Spaces
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @summary Fixed (tiny) space
+*/
+export const Inset = (() => <div className='dome-xToolBar-inset'/>);
+
+/**
+   @class
+   @summary Fixed space
+*/
+export const Space = (() => <div className='dome-xToolBar-space'/>);
+
+/**
+   @class
+   @summary Extensible space (can be used to right-align controls)
+*/
+export const Filler = (() => <div className='dome-xToolBar-filler'/>);
+
+/**
+   @class
+   @summary Vertical rule
+*/
+export const Separator = () => (
+  <div className='dome-xToolBar-separator'>
+    <div className='dome-xToolBar-vrule'/>
+  </div>
+);
+
+// --------------------------------------------------------------------------
+// --- ToolBar Button
+// --------------------------------------------------------------------------
+
+import { SVG } from 'dome/controls/icons' ;
+
+const SELECT = 'dome-xToolBar-Control dome-selected' ;
+const BUTTON = 'dome-xToolBar-Control dome-color-frame' ;
+const KIND = (kind) => kind ? ' dome-xToolBar-' + kind : '';
+
+const isSelected = ( { selected , selection , value } ) => (
+  selected !== undefined ? selected : ( value !== undefined && value === selection )
+);
+
+const isDisabled = ( { enabled=true, disabled=false } ) => (disabled || !enabled) ;
+const onClick = ( { onClick , value } ) => onClick ? (() => onClick(value)) : undefined ;
+
+/**
+   @class
+   @property {string} [icon] - Button icon name (See [gallery](gallery-icons.html))
+   @property {string} [label] - Button label
+   @property {string} [title] - Button tooltip
+   @property {string} [kind] - Styled button (see below)
+   @property {boolean} [selected] - Selected button (default: `false`)
+   @property {boolean} [disabled] - Disabled button (default: `false`)
+   @property {boolean} [enabled] - Enabled button (default: `true`)
+   @property {any} [value] - button's value
+   @property {any} [selection] - Currently selected value
+   @property {function} [onClick] - Button callback (receives the current value)
+   @summary toolbar button
+   @description
+
+   By default, the propery `selected` is computed from properties `value`
+   and `selection`, when provided.
+
+   The callback is given the `value` property, if any.
+
+   The different available kinds for styling a (non-selected) button are:
+   - `'default'`: normal button;
+   - `'cancel'`: normal button, in dark grey;
+   - `'warning'`: warning button, in orange;
+   - `'positive'`: positive button, in green;
+   - `'negative'`: negative button, in red.
+
+*/
+export const Button = ( props ) => (
+  <button
+    disabled={isDisabled(props)}
+    className={isSelected(props) ? SELECT : (BUTTON + KIND(props.kind))}
+    onClick={onClick(props)}
+    title={props.title}
+    >
+    {props.icon && <SVG id={props.icon} />}
+    {props.label && <label>{props.label}</label>}
+  </button>
+);
+
+// --------------------------------------------------------------------------
+// --- ToolBar Button Group
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @property {Button[]} children - Buttons in the group
+   @property {any} [value] - Passed to children as `selection` property
+   @property {any} [onChange] - Passed to children as `onClick` property
+   @property {any} [...props] - Properties passed to all children
+   @summary toolbar button group
+*/
+export const ButtonGroup =
+  ({ children, value, onChange, ...props }) =>
+  {
+    if (value !== undefined ) props.selection = value ;
+    if (onChange !== undefined ) props.onClick = onChange ;
+    return (
+      <div className='dome-xToolBar-Group'>
+        {React.Children.map(children,(elt) => React.cloneElement(elt,props))}
+      </div>
+    );
+  };
+
+// --------------------------------------------------------------------------
+// --- ToolBar Menu
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @property {any} [value] - selected option's value
+   @property {function} [onChange] - selection callback (receives option value)
+   @property {boolean} [disabled] - disable the selector (default: `false`)
+   @property {boolean} [enabled] - enable the selector (default: `true`)
+   @property {option[]} children - Array of menu options
+   @summary Toolbar selector menu
+   @description
+
+   Behaves likes a standard `<select>` element, except that callback directly
+   receives the select value, not the entire event.
+
+   The list of options shall be given with standard `<option value={...} label={...}>`
+   elements.
+
+*/
+export const Select = (props) => (
+  <select className='dome-xToolBar-Control dome-color-frame'
+          value={props.value}
+          disabled={isDisabled(props)}
+          onChange={(props.onChange && ((evt) => props.onChange(evt.target.value)))}
+    >
+    {props.children}
+  </select>
+);
+
+// --------------------------------------------------------------------------
+// --- Export & Registration
+// --------------------------------------------------------------------------
+
+import { register } from 'dome/misc/register' ;
+
+register( ToolBar, 'DOME_TOOLBAR' );
+register( Inset ,  'DOME_TOOLBAR_ITEM' );
+register( Space ,  'DOME_TOOLBAR_ITEM' );
+register( Separator ,  'DOME_TOOLBAR_ITEM' );
+register( Filler , 'DOME_TOOLBAR_ITEM' );
+register( Button , 'DOME_TOOLBAR_ITEM' );
+
+export default { ToolBar , Space , Inset, Separator, Filler, Button, ButtonGroup, Select };
+
+// --------------------------------------------------------------------------
diff --git a/ivette/src/dome/src/renderer/table/arrays.js b/ivette/src/dome/src/renderer/table/arrays.js
new file mode 100644
index 0000000000000000000000000000000000000000..cfd5ef18555a49c16ca4041311030b3b923bd4c4
--- /dev/null
+++ b/ivette/src/dome/src/renderer/table/arrays.js
@@ -0,0 +1,528 @@
+// --------------------------------------------------------------------------
+// --- Table Models
+// --------------------------------------------------------------------------
+
+/** @module dome/table/arrays */
+
+import _ from 'lodash' ;
+import React from 'react' ;
+import { Model, ASC, DESC } from 'dome/table/models' ;
+
+// --------------------------------------------------------------------------
+// --- Ordering
+// --------------------------------------------------------------------------
+
+// Compute the value of an item
+const getValueWith =
+      ( item , value ) =>
+      ( item === undefined ? undefined :
+        typeof(value) === 'function' ? value(item) :
+        item[value] );
+
+// Compute the primary ordering function
+const comparisonWith = (sorting) => {
+  switch(typeof(sorting)) {
+
+  case 'function':
+    return sorting ;
+
+  case 'string':
+    return (a,b) => {
+      if ( a === b ) return 0 ;
+      if ( a === undefined ) return 1 ;
+      if ( b === undefined ) return -1 ;
+      const va = a[sorting];
+      const vb = b[sorting];
+      if ( va === vb ) return 0;
+      if ( va === undefined ) return 1 ;
+      if ( vb === undefined ) return -1 ;
+      if ( va < vb ) return -1;
+      if ( va > vb ) return 1;
+      return 0;
+    };
+
+  case 'object':
+    const { sortBy, sortDirection=ASC } = sorting ;
+    return (a,b) => {
+      if ( a === b ) return 0 ;
+      if ( a === undefined ) return 1 ;
+      if ( b === undefined ) return -1 ;
+      const isFun = typeof(sortBy)==='function' ;
+      const va = isFun ? sortBy(a) : a[sortBy] ;
+      const vb = isFun ? sortBy(b) : b[sortBy] ;
+      if ( va === vb ) return 0;
+      if ( va === undefined ) return 1 ;
+      if ( vb === undefined ) return -1 ;
+      switch(sortDirection) {
+      case ASC:
+        if (va < vb) return -1;
+        if (va > vb) return 1;
+        break;
+      case DESC:
+        if (va < vb) return 1;
+        if (va > vb) return -1;
+        break;
+      }
+      return 0;
+    };
+
+  default:
+    return () => 0;
+
+  }
+};
+
+// Make a chainable order
+const chainableOrder = ( order ) => {
+
+  const compare = (a,b) => {
+    for (var k = 0; k < order.length ; k++) {
+      const cmp = (order[k])(a,b);
+      if (cmp !==0 ) return cmp;
+    }
+    return 0;
+  };
+
+  compare.thenWith = (sorting) =>
+    sorting ?
+    chainableOrder( order.slice().push(comparisonWith(sorting)) )
+    : compare ;
+
+  return compare ;
+};
+
+/**
+   @summary Comparison helper.
+   @method
+   @param {any} sorting - the sorting properties
+   @return {function} the corresponding comparison function
+   @description
+
+This function is a helper for comparing items, by comparing
+values extracted from them with chaining. It returns
+a comparison function that you can use, for instance,
+in `Array.sort` fort sorting arrays.
+
+##### Comparison
+
+The comparison order is defined according the `sorting` parameter
+provided to the helper:
+
+If `sorting` is a function, it is used as the primary comparison function.
+When items compare equal, chained comparisons are used to refine the
+ordering (see Chaining below).
+
+If `sorting` is a property name (a string), items `a` and `b` are ordered
+by comparing their respective values `a[sorting]` and `b[sorting]` for
+this property.
+
+If `sorting` as an object, it shall provides `{ sortBy, sortDirection }`
+properties and the comparison function is defined as follows:
+- `sortBy` can be a function or a property name.
+If a function is given, items `a` and `b` are ordered by comparing
+values `sortBy(a)` and `sortBy(b)`. Otherwize, they are ordered by
+comparing `a[sortBy]` and `b[sortBy]`.
+- `sortDirection` can be either `ASC` for normal comparison or `DESC`
+for reversing the comparison.
+If no direction is given (or any other value) `ASC` is assumed.
+
+When computing comparison, `undefined` values are _always_ rejected to the
+end of the ordering, whatever the specified direction.
+
+##### Chaining
+
+The returned comparison function can be chained with a secondary
+comparison function, which will be used when two items compare equal.
+
+You can chain as many comparison functions you want by using
+`.thenWith(sorting)` like in the example below. Each call to `.thenWith` returns
+a different comparison function that can be safely forked with subsequent calls
+to `.thenWith` as illustrated in the second example.
+
+Remark than `compareWith` and `.thenWith` also accept undefined or null values, which
+are considered neutral.
+
+@example
+// Chaining Comparison
+items.sort(
+  compareWith({sortBy:'name',sortDirection: ASC})
+    .thenWith( (a,b) => a.priority - b.priority )
+    .thenWith({sortBy:'age',sortDirection: DESC})
+);
+
+@example
+// Forking Comparison
+const primary = compareWith( (a,b) => a.priority - b.prioroty );
+const byName = primary.thenWith('name');
+const byAge = primary.thenWith('age');
+
+*/
+export const compareWith =
+  ( sorting ) =>
+  ( sorting ? chainableOrder([ comparisonWith(sorting) ]) : () => 0 );
+
+// --------------------------------------------------------------------------
+// --- Comparison Ring
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @summary Helper for column comparison.
+   @description
+A comparison ring can be used to implement column ordering, where each
+column selection _refines_ the previous ordering.
+
+Hence, the ring holds the current comparison order and it is well suited
+for being used in conjunction with [Table](module-dome_table_views.Table.html)
+components.
+
+A comparison specification can be a property name or a function.
+See [compareWith](module-dome_table_arrays.html#compareWith) for more details.
+By default, the ring uses the column identifier as a property name for comparing.
+
+Initially, the ordering is _natural_.
+*/
+export class ComparisonRing {
+
+  /**
+     @param {id|function} [natural] - default (natural) order
+  */
+
+  constructor(natural) {
+    this.columns = {} ;
+    this.natural = natural && comparisonWith(natural) ;
+    this.compare = this.compare.bind(this);
+    this.ring = [] ;
+    this.sort = undefined ;
+  }
+
+  /**
+     @summary Current order comparison.
+     @description
+     Returns the comparison function corresponding to the current order.
+     The comparison function is _chainable_, see
+     [compareWith](module-dome_table_arrays.html#compareWith) for more details.
+  */
+  compareWith()
+  {
+    if (!this.sort) {
+      const ordering = this.ring.map(
+        ({sortBy,sortDirection}) =>
+          comparisonWith({
+            sortBy: this.columns[sortBy] || sortBy ,
+            sortDirection
+          })
+      );
+      if (this.natural) ordering.push(this.natural);
+      this.sort = chainableOrder(ordering);
+    }
+    return this.sort;
+  }
+
+  /**
+     @summary Refine current order comparison.
+     @description
+     Short cut for `this.compareWith().thenWith(sorting)`
+  */
+  thenWith(sorting) {
+    return this.compareWith().thenWith(sorting);
+  }
+
+  /**
+     @summary Get value for ordering a column.
+     @param {any} item - the item to compare with
+     @param {string} column - the column identifier
+     @return {any} item's value for the column
+     Returns the value used to order an item within a given column.
+  */
+  getValue(item,column) {
+    return getValueWith(item,this.columns[column] || column);
+  }
+
+  /**
+     @summary Set value to order a column.
+     @param {string} column - column identifier
+     @param {string|function} [value] - value accessor (defaults to `column`)
+     @description
+     Set the sorting specification (property name or function) for the given column.
+  */
+  setValue( column , value ) {
+    this.columns[column] = value ;
+  }
+
+  /** Sets natural ordering (property name or value accessor) */
+  setNatural( natural ) {
+    this.natural = natural ;
+  }
+
+  /** @summary Compare two items with respect to the current ordering.
+      @description
+      You can use `this.compare` as a closure to the current comparison function
+      (no need for `this.compare.bind(this)`).
+   */
+  compare(a,b) {
+    return this.order()(a,b);
+  }
+
+  /** @summary Return current ordering.
+      @return {object} ordering specification
+      @description
+      Return the last `{ sortBy, sortDirection }` ordering.
+      Shall be used for the Table view. */
+  getOrdering() {
+    return this.ring[0] ;
+  }
+
+  /**
+     @summary Specify current ordering.
+     @param {object} sorting - the new ordering
+     @description
+     Use the specified `{ sortBy, sortDirection }` ordering, and refine it with
+     the previous one.
+
+     If `sorting` is undefined, reset the ring to the natural ordering.
+  */
+  setOrdering( sorting ) {
+    if (sorting) {
+      const key = sorting.sortBy ;
+      this.ring = this.ring.filter( (s) => s.sortBy !== key );
+      this.ring.unshift( sorting );
+    } else {
+      this.ring = [] ;
+    }
+    this.sort = undefined ;
+  }
+
+}
+
+// --------------------------------------------------------------------------
+// --- Unsorted Model
+// --------------------------------------------------------------------------
+
+/**
+   @summary A table Model for unsorted datasets.
+   @description
+
+   This class implements a simple [Model](module-dome_table_models.Model.html)
+   where item's are identified by their index. Such a model is not adapted to
+   re-ordering and filtering, because table views will have no way to synchronize
+   the selected index before and after re-ordering, hence the name.
+
+*/
+
+export class UnsortedModel extends Model {
+
+  /**
+      @param {number} [count] - the initial size (default `0`)
+   */
+  constructor(count=0) {
+    super();
+    this.count = count < 0 ? 0 : count ;
+  }
+
+  /**
+     @summary Set the number of items.
+     @param {number} n - the number of items
+     @param {boolean} [reload] - force reloading (false by default)
+     @description
+     Triggers a reload if the item count has changed,
+     unless you force it.
+  */
+  setItemCount(n,reload=false) {
+    if (n<0) n=0;
+    if ( reload || n != this.count ) {
+      this.count = n ;
+      this.reload();
+    }
+  }
+
+  getItemCount() { return this.count; }
+
+  /** Identity, or `undefined` when out of range. */
+  getItemAt(k) {
+    return 0 <= k && k < this.count ? k : undefined ;
+  }
+
+  /** Identity, or `-1` when out of range. */
+  getIndexOf(k) {
+    return 0 <= k && k < this.count ? k : -1 ;
+  }
+
+}
+
+// --------------------------------------------------------------------------
+// --- Array Model
+// --------------------------------------------------------------------------
+
+/**
+   @summary A table Model based on array.
+   @extends Model
+   @description
+
+   This class implements a simple [Model](module-dome_table_models.Model.html)
+   implementation where item's are stored in an array. The model supports built-in
+   ordering thanks with a
+   [ComparisonRing](module-dome_table_arrays.ComparisonRing.html)
+   with additional filtering capabilities.
+
+   The model keep items in sync with their ordered & filtered index by
+   injecting an `index` property in them each time the collection is re-ordered.
+   You shall not use `index` property for your own needs.
+
+   Item objects can be modified in place, but you shall call `model.updateItem(item)`
+   or `model.reload()` to re-renderer the associated (visible) cells.
+ */
+export class ArrayModel extends Model {
+
+  /** Initially empty model */
+  constructor() {
+    super();
+    this.ring = new ComparisonRing('index'); // Used for stable sorting
+    this.data = []; // Array of item elements
+  }
+
+  /** Remove all items (and reload) */
+  clear() {
+    this.data = [] ;
+    this.reload();
+  }
+
+  /** Add one or more items (and reload) */
+  add( ...items ) {
+    this.data.push(...items);
+    this.reload();
+  }
+
+  /**
+     @summary Model items array.
+     @description
+Returns the internal item array holding _all_ the items in the model.
+
+This array is _not_ sortered and filtered. You can obtain the current index of an item in
+table views by accessing its `item.index` property, which is `undefined` if the item has been
+filtered out.
+
+If you modify the internal item array, don't forget to call `reload()` after modifications
+in order to keep views in sync.
+   */
+  getData() { return this.data; }
+
+  /** Replace the entire collection of items */
+  setData(data) {
+    this.data = data || [] ;
+    this.reload();
+  }
+
+  _order() {
+    if (!this.order) {
+      const filter = this.filtering ;
+      const compare = this.ring.compareWith();
+      const ordered = this.data.filter((item,index) => {
+        const ok = filter ? filter(item) : true ;
+        // Index inside initial collection is used for stable sort
+        item.index = ok ? index : undefined ;
+        return ok ;
+      }).sort(compare);
+      // Now set index in filtered & sorted collection
+      ordered.forEach((item,index) => item.index = index);
+      this.order = ordered ;
+    }
+    return this.order ;
+  }
+
+  /** Return a _copy_ of the visible items. */
+  getItems() { return this._order().slice(); }
+
+  // MODEL Interface
+  getItemCount() { return this._order().length; }
+
+  // MODEL Interface
+  getItemAt(index) { return this._order()[index] ; }
+
+  // MODEL Interface
+  getIndexOf(item) { return item && item.index ; }
+
+  /** @summary Current filtering function.
+      @return {function} `undefined` means no filtering
+  */
+  getFiltering() { return this.filtering ; }
+
+  /** @summary Set the filtering function.
+      @param {function} [filter] - The filter function
+      @description
+      The filtering function is used to filter out items to be displayed.
+      It is invoked as `filter(item)` and shall return a truthly value when `item`
+      must be displayed.
+  */
+  setFiltering(filter) {
+    this.filtering = filter ;
+    this.reload();
+  }
+
+  // MODEL Interface
+  getOrdering() { return this.ring.getOrdering(); }
+
+  // MODEL Interface
+  setOrdering(order) {
+    if (order === undefined || order.sortBy !== 'index')
+    {
+      this.ring.setOrdering(order);
+      this.reload();
+    }
+  }
+
+  // MODEL Interface
+  getValue( item , column ) {
+    return this.ring.getValue( item , column );
+  }
+
+  /** Set the value-getter for the given column */
+  setValue( column , value ) {
+    this.ring.setValue(column,value);
+    this.reload();
+  }
+
+  // MODEL Interface
+  reload() {
+    this.order = undefined ;
+    super.reload();
+  }
+}
+
+// --------------------------------------------------------------------------
+// --- Model Hook
+// --------------------------------------------------------------------------
+
+/**
+   @summary Uses a new array model (Custom React Hook).
+   @param {Collection} [items] - the array items
+   @description
+   This hook is a convenient way to have a local array model with full featured
+   sorting and filtering fonctionnalities, which is automatically updated
+   with the provided items.
+
+   The array model is created once and updated at each render.
+   Items can be specified with a lodash collection.
+
+   @example // Array Model
+   const MyView = () => {
+      Dome.useUpdate( MyUpdateEvent );
+      const model = useArrayModel(getMyItems());
+      return (<Table model={model} … >…</Table>) ;
+   };
+
+*/
+export function useArrayModel( items )
+{
+  const model = React.useMemo( () => new ArrayModel() , [] );
+  model.setData( _.toArray(items) );
+  return model;
+}
+
+// --------------------------------------------------------------------------
+// --- Exports
+// --------------------------------------------------------------------------
+
+export default { compareWith , ComparisonRing , UnsortedModel, ArrayModel } ;
+
+// --------------------------------------------------------------------------
diff --git a/ivette/src/dome/src/renderer/table/models.js b/ivette/src/dome/src/renderer/table/models.js
new file mode 100644
index 0000000000000000000000000000000000000000..161a240c418c66a6fb04f4605ce9e42fd096f848
--- /dev/null
+++ b/ivette/src/dome/src/renderer/table/models.js
@@ -0,0 +1,255 @@
+// --------------------------------------------------------------------------
+// --- Models
+// --------------------------------------------------------------------------
+
+/** @module dome/table/models */
+
+import _ from 'lodash' ;
+import { SortDirection } from 'react-virtualized' ;
+
+// --------------------------------------------------------------------------
+// --- Sorting
+// --------------------------------------------------------------------------
+
+/** Ascending Order (SortDirection) */
+export const ASC = SortDirection.ASC ;
+
+/** Descending Order (SortDirection) */
+export const DESC = SortDirection.DESC ;
+
+// --------------------------------------------------------------------------
+// --- Collection Model
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @summary Data Listener.
+   @description
+
+   A Model is responsible for keeping the tables and lists views in sync
+   with their associated data sets. The model listens for updates, retrieves
+   items from their index, and re-render the views when necessary.
+
+
+   Model could be part of the global _Application State_, or part of a
+   statefull React Component, depending on your need as described in
+   the [Application Design](tutorial-application.html) tutorial.
+
+
+   Several tables may connect to the same table model, but they will share the
+   same number and ordering of items. However, each connected table will only
+   render its own range of items and will re-renderer only when impacted by
+   individual updates.
+
+
+   A Model instance may not hold the data directly. A table or list component
+   uses the model only as a _proxy_ reponsible for fetching the items with respect to
+   some current filtering and ordering selection. The model also serves as
+   a proxy for triggering table re-rendering when table data is updated.
+
+
+   To design your data model, you shall extends the base `Model` class and
+   override the public methods to fit your needs.
+
+   - `getItemCount() -> number` (the number of items)
+   - `getItemAt(number) -> item` (the item at the given index)
+   - `getIndexOf(item) -> number` (the index of an item in the current order)
+   - `getValue(item,column) -> any` (the value associated to some item in a column)
+
+
+   To implement sorting, you shall also override the following methods:
+
+   - `getOrdering() -> { sortBy, sortDirection }`
+   - `setOrdering({ sortBy, sortDirection }) -> ()`
+
+
+   Whenever data is added, removed, updated or re-ordered, the `Model` shall be
+   informed by calling one of the following methods:
+
+   - `updateItem(item);` when an individual item shall be re-rendered (if ever visible)
+   - `updateIndex(index[,index]);` when an item or a range of items shall be re-rendered
+   - `reload();` for all other modifications of the collection, including filtering and re-ordering
+
+
+   Items count and items indices shall be consistent with the current filter(s)
+   and order(s) selected by the user. Tables are equipped with
+   callbacks on table headers that can be used to trigger re-ordering of your data, but
+   you can implement your own controls or use menus to do that.
+
+   Items can be represented by any javascript values (string, integers, objects...).
+   Default table cell renderers expect items to be object with one property per column, but you
+   can override those default. The default `getValue` implementation simply returns the
+   item property corresponding to the column identifier.
+
+   When some data is updated, selection and scrolling of the views will be
+   preserved based on item's value. Table and List views will
+   keep each rendered item in sync with their index thanks to methods `getItemAt`
+   and `getIndexOf` that you provide with the Model.
+
+   Hence, items implementation shall contains enough information to uniquely identify them,
+   whatever their current index.
+
+   ##### Model Helpers
+
+   The module [dome/table/arrays](module-dome_table_arrays.html) provides you with
+   usefull helpers to implement Models with filtering and ordering features.
+
+*/
+export class Model {
+
+  constructor() {
+    this._dome_clients = {} ;
+    this._dome_clientId = 0 ;
+  }
+
+  /**
+     @summary Items count.
+     @abstract
+     @return {number} number of items
+     @description
+     Shall return the number of items to be displayed by the table.
+     Negative values are considered as zero.
+     Default implementation returns zero.
+   */
+  getItemCount() { return 0; }
+
+  /**
+     @summary Item at given index.
+     @abstract
+     @param {number} index - item's ordering index
+     @return {any} the item's value
+     @description
+     Shall return the item at a given index in the table with respect to
+     current filtering and ordering (if appropriate).
+     <p>
+     Default implementation returns `undefined`.
+  */
+  getItemAt() { return undefined; }
+
+  /**
+     @summary Index of an item.
+     @abstract
+     @param {any} item - the item
+     @return {number} index of the item in the filtered and ordered collection
+     @description
+     Shall return the index of a given item inside the table with respect to
+     current filtering and ordering, or `undefined` if no such item exists.
+     <p>
+     Default implementation returns `undefined`.
+  */
+  getIndexOf() { return undefined; }
+
+  /**
+     @summary Item value in a column.
+     @param {any} item - an item
+     @param {string} column - a column identifier
+     @return {any} the value associated to the item for the given column.
+     @description
+     Defaults to accessing the column property of the item (ie. `item[column]`).
+     This method can be overriden by custom models and also table columns.
+   */
+  getValue(item,column) { return item[column]; }
+
+  /**
+     @summary Re-render an item.
+     @param {any} item - the updated item
+     @description
+     Signal that a given item has been updated and need to be re-rendered if visible.
+  */
+  updateItem(item) {
+    const k = this.getIndexOf(item);
+    if ( 0 <= k ) this.updateIndex(k);
+  }
+
+  /**
+     @summary Re-render a range of items.
+     @param {number} first - the first updated item index
+     @param {number} [last] - the last updated item index (defaults to `first`)
+     @description
+     Signal that a range of items have been updated and need to be re-rendered if visible.
+  */
+  updateIndex(a,b=a) {
+    _.forOwn(this._dome_clients,({ lower,upper,trigger }) => {
+      if ( a <= upper && lower <= b ) trigger();
+    });
+  }
+
+  /** Re-render all items */
+  reload() { _.forOwn(this._dome_clients,({trigger})=> trigger()); }
+
+  /**
+     @summary Current ordering.
+     @return {object} current sorting order
+     @description
+     Shall return the current ordering `{ sortBy, sortDirection }`
+     for user feedback in table headers, and `undefined` for natural ordering
+     or no ordering at all.
+     <p>
+     Default implementation returns `undefined`.
+  */
+  getOrdering() { return undefined; }
+
+  /**
+     @summary Change ordering of the model.
+     @param {object} [sort] - sorting order
+     @description
+     Callback to user clicks on table headers. This method receives
+     the new `{ sortBy, sortDirection }` ordering requested by the user action,
+     of `undefined` to reset initial, natural ordering of items.
+     You can also invoke this method on your own, away from any table view.
+     <p>
+     The method shall eventually reorder the items internally, and finally
+     signal completion with a call to `Model.reload()` in order to sync the views.
+     If re-ordering can take a while, this shall be performed asynchronously.
+     <p>
+     Default implementation does nothing.
+  */
+  setOrdering() { }
+
+  /**
+     @summary Connect a trigger to the model.
+     @protected
+     @param {Function} trigger - callback to force table update
+     @return {ClientID} client identifier
+     @description
+     Returns a _client_ identifier for removing and watching.
+     The trigger function is called for each update watched by the _client_.
+     Initially, the watching range is empty.
+  */
+  _bind(trigger) {
+    const client = "#" + this._dome_clientId++ ;
+    this._dome_clients[client] = { lower:0, upper:0, trigger };
+    return client;
+  }
+
+  /**
+     @summary Disconnect the _client_ from the model.
+     @protected
+     @param {ClientID} client - the identifier of the client to disconnect
+  */
+  _remove(client) {
+    delete this._dome_clients[client];
+  }
+
+  /**
+     @summary Set the current range of items watched by the _client_.
+     @protected
+     @param {ClientID} client - the identifier of the client to disconnect
+     @param {number} first - first index of the range
+     @param {number} last - last index of the range
+     @description
+     Data updates tha fall outside this range will _not_ trigger
+     re-rendering of the client.
+  */
+  _watch(client,a,b) {
+    const listener = this._dome_clients[client];
+    if (listener) { listener.lower = a ; listener.upper = b; }
+  }
+
+}
+
+// --------------------------------------------------------------------------
+
+export default { Model , ASC , DESC } ;
+
+// --------------------------------------------------------------------------
diff --git a/ivette/src/dome/src/renderer/table/tables.css b/ivette/src/dome/src/renderer/table/tables.css
new file mode 100644
index 0000000000000000000000000000000000000000..bf732426f386057343a6d9a1cf89ca777e3b2f10
--- /dev/null
+++ b/ivette/src/dome/src/renderer/table/tables.css
@@ -0,0 +1,121 @@
+/* -------------------------------------------------------------------------- */
+/* --- Styling Dome Tables                                                --- */
+/* -------------------------------------------------------------------------- */
+
+.dome-xTable {
+    flex: 1 1 auto ;
+    width: 100% ;
+    height: 100% ;
+}
+
+.dome-xTable * {
+    outline: none ;
+}
+
+/* -------------------------------------------------------------------------- */
+/* --- Styling Table Headers                                              --- */
+/* -------------------------------------------------------------------------- */
+
+.dome-xTable .ReactVirtualized__Table__headerRow {
+    display: flex ;
+    align-items: stretch ;
+    background: #eee ;
+    color: #555 ;
+    fill: #777 ;
+}
+
+.dome-xTable .ReactVirtualized__Table__headerColumn {
+    overflow: hidden ;
+    padding-left: 4px ;
+    padding-right: 4px ;
+    margin: 0px ; /* Necessary for column alignement */
+    border-color: #cfcfcf ;
+    border-bottom-width: 1px ;
+    border-bottom-style: solid ;
+}
+
+.dome-xTable .ReactVirtualized__Table__headerColumn:not(:last-child) {
+    border-right-width: 1px ;
+    border-right-style: solid ;
+}
+
+.dome-xTable-header {
+    /* flex: is inlined by react-virtualized */
+    /* height: must be kept in sync with table.js */
+    display: flex ;
+    height: 22px ;
+    flex-flow: row no-wrap ;
+    align-items: center ;
+}
+
+.dome-xTable-header-icon {
+    flex: 0 0 auto ;
+    margin-left: 3px ;
+    margin-right: 3px ;
+}
+
+.dome-xTable-header-label {
+    flex: 1 1 auto ;
+    overflow: hidden ;
+    margin-left: 3px ;
+    margin-right: 3px ;
+}
+
+.dome-xTable-header-sorter {
+    flex: 0 0 auto ;
+    margin-right: 2px ;
+}
+
+.dome-xTable-resizer {
+    position: absolute ;
+    cursor: col-resize ;
+    width: 3px ;
+    top: 0px ;
+    bottom: 0px ;
+    z-index: 1 ;
+}
+
+/* -------------------------------------------------------------------------- */
+/* --- Styling Table Rows                                                 --- */
+/* -------------------------------------------------------------------------- */
+
+.dome-xTable .ReactVirtualized__Table__row {
+    display: flex ;
+    align-items: stretch ;
+}
+
+.dome-xTable .ReactVirtualized__Table__rowColumn {
+    /* flex: is inlined by react-virtualized */
+    /* height: must be kept in sync with table.js */
+    display: block ;
+    position: relative ;
+    margin: 0 ; /* Necessary for column alignement */
+    padding-top: 3px ;
+    padding-left: 4px ;
+    padding-right: 4px ;
+    vertical-align: baseline ;
+}
+
+.dome-xTable-odd {
+    background: #fdfdfd ;
+}
+
+.dome-xTable-even {
+    background: #efefef ;
+}
+
+.ReactVirtualized__Table__row.dome-color-selected {
+    border-bottom-style: solid ;
+    border-bottom-width: 1px ;
+}
+
+/* -------------------------------------------------------------------------- */
+/* --- Styling Table Cells                                                --- */
+/* -------------------------------------------------------------------------- */
+
+.dome-xTable-cell {
+    display: block ;
+    position: relative ;
+}
+
+/* -------------------------------------------------------------------------- */
diff --git a/ivette/src/dome/src/renderer/table/views.js b/ivette/src/dome/src/renderer/table/views.js
new file mode 100644
index 0000000000000000000000000000000000000000..84aedb303861a6578f3cd725c724cc942a1ab72a
--- /dev/null
+++ b/ivette/src/dome/src/renderer/table/views.js
@@ -0,0 +1,674 @@
+// --------------------------------------------------------------------------
+// --- Tables
+// --------------------------------------------------------------------------
+
+/** @module dome/table/views */
+
+import React from 'react' ;
+import Dome from 'dome' ;
+import { DraggableCore } from 'react-draggable';
+import { SVG } from 'dome/controls/icons' ;
+import {
+  AutoSizer,
+  SortDirection,
+  Table as VTable,
+  Column as VColumn
+} from 'react-virtualized' ;
+import _ from 'lodash' ;
+import './tables.css' ;
+
+// --------------------------------------------------------------------------
+// --- Header Renderer
+// --------------------------------------------------------------------------
+
+const headerRowRenderer =
+      (contextMenu) =>
+      // Borrowed from react-virtualized Table.defaultHeaderRowRenderer
+      ({
+        className,
+        columns,
+        style
+      }) => (
+        <div role="row"
+             className={className}
+             style={style}
+             onContextMenu={contextMenu} >
+          {columns}
+        </div>
+      );
+
+const headerIcon = (icon) => (
+  icon && <div className='dome-xTable-header-icon'><SVG id={icon}/></div>
+);
+
+const headerLabel = (label) => (
+  label &&
+    (<label className='dome-xTable-header-label dome-text-label'>
+     {label}
+     </label>)
+);
+
+const makeSorter = (id) => (
+  <div className='dome-xTable-header-sorter'>
+    <SVG id={id} size={8}/>
+  </div>
+);
+
+const headerSorter = {} ;
+headerSorter[ SortDirection.ASC ] = makeSorter('ANGLE.UP');
+headerSorter[ SortDirection.DESC ] = makeSorter('ANGLE.DOWN');
+
+const headerRenderer =
+      ({
+        columnData: { label, icon, title, headerRef },
+        dataKey,
+        sortBy,
+        sortDirection
+      }) => {
+        const tooltip = title || label ;
+        const onRef = (elt) => headerRef(dataKey,elt) ;
+        const sorter = dataKey === sortBy ? headerSorter[sortDirection] : undefined ;
+        return (
+          <div className='dome-xTable-header' title={tooltip} ref={onRef} >
+            { headerIcon(icon) }
+            { headerLabel(label) }
+            { sorter }
+          </div>
+        );
+      };
+
+// --------------------------------------------------------------------------
+// --- Cell Renderer
+// --------------------------------------------------------------------------
+
+const cellDataGetter =
+      (getValue) =>
+      ({ rowData:{ model , item }, dataKey:id }) =>
+      ( item == undefined ? undefined :
+        getValue ? getValue(item) :
+        model.getValue(item,id)
+      );
+
+const cellRenderer =
+      (renderValue) =>
+      ({ cellData: data }) =>
+      (
+        data === undefined ? undefined :
+        renderValue ? renderValue(data) :
+        (<div className='dome-xTable-cell dome-text-data'>{data}</div>)
+      );
+
+// --------------------------------------------------------------------------
+// --- Column Resizer
+// --------------------------------------------------------------------------
+
+const DRAGGING = 'dome-xTable-resizer dome-color-dragging' ;
+const DRAGZONE = 'dome-xTable-resizer dome-color-dragzone' ;
+
+const Resizer = (props) => (
+  <DraggableCore
+    onStart={props.onStart}
+    onStop={props.onStop}
+    onDrag={(_elt,data)=> props.onDrag(props.left,props.right,data.x - props.offset)}
+    >
+    <div
+      className={ props.id === props.dragging ? DRAGGING : DRAGZONE }
+      style={{ left: props.offset-2 }}
+      />
+  </DraggableCore>
+);
+
+const computeWidth = (elt) => {
+  const parent = elt && elt.parentElement ;
+  return parent && parent.getBoundingClientRect().width ;
+};
+
+// --------------------------------------------------------------------------
+// --- Table Columns
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @summary Table Column.
+   @property {string} id - Column unique identifier (required)
+   @property {string} [icon] - Header icon
+   @property {string} [label] - Header label
+   @property {string} [title] - Header tooltip
+   @property {string} [align] - Column alignment (`'left'`, `'center'`, `'right'`)
+   @property {number} [width] - Column base width (in pixels, default `60`)
+   @property {boolean} [fill] - Extensible column (not by default)
+   @property {boolean} [fixed] - Non-resizable column (not by default)
+   @property {boolean} [disableSort] - Do not trigger sorting callback for this column
+   @property {boolean|string} [visible] - Default column visibility
+   @property {function} [getValue] - Obtain an item's value for this column
+   @property {function} [renderValue] - Render item's value in each table cell
+   @description
+
+   Each column displays a specific value derived from the item displayed in a
+   row. Column properties enforce a separation between how to extract the value
+   from an item and how to render it in the cell.
+
+   - `getValue(item) : any` shall returns the value to render for the _item_
+   - `renderValue(any) : Element` shall returns the (React) element to display the item
+
+
+   By default, values are obtained from the underlying model by invoking
+   [Model.getValue](module-dome_table_models.Model.html#getValue) with the column
+   identifier.
+
+   The default `renderValue` renders the item's value
+   packed in a `<label>` with class `dome-text-data` as described in the
+   [Styling Component](tutorial-styling.html) tutorial.
+
+   This separation of concerns allows for defining
+   Column types, where for instance the renderer is already defined and you only need to
+   know how to extract the expected value of items.
+   See [DefineColumn](module-dome_table_views.DefineColumn.html)
+   for more informations and examples.
+
+   A table should have at least one extensible column to occupy the available width.
+   If no column in the table is explicitely declared to be extensible, the last
+   one would be implicitely set to fill.
+
+   Default visiblity can be set to a boolean value ; alternatively, you may specify
+   `visible='never'` to make the column invisible to the user, or `visible='always'`
+   to force the column to be visible.
+
+*/
+export const Column = () => null;
+// Fake component only used to store props.
+// Virtualized column is rendered with function below:
+const vColumn = ({
+  headerRef,
+  columnResize,hasFill,lastElt,
+  contextMenu
+}) => (elt) => {
+  const defaults = elt.type._DOME_COLUMN_DEFAULTS || {} ;
+  const forcers = !hasFill && elt == lastElt ? { fill:true } : {} ;
+  const { id,label,title,icon,align,width,fill,disableSort,getValue,renderValue }
+        = Object.assign( {}, defaults , elt.props , forcers ) ;
+  return (
+    <VColumn
+      key={id}
+      displayName='React-Virtualized-Column'
+      width={columnResize[id] || width || 60}
+      flexGrow={ fill ? 1 : 0 }
+      dataKey={id}
+      columnData={{label,title,icon,headerRef}}
+      headerRenderer={headerRenderer}
+      cellRenderer={cellRenderer(renderValue)}
+      cellDataGetter={cellDataGetter(getValue)}
+      headerStyle={{ textAlign: align }}
+      disableSort={disableSort}
+      style={{ textAlign: align }}
+      />
+  );
+};
+
+const defaultVisible = (visible) => {
+  switch(visible) {
+  case 'always':
+  case undefined:
+    return true;
+  case 'never':
+  case null:
+    return false;
+  default:
+    return visible;
+  }
+};
+
+// --------------------------------------------------------------------------
+// --- Specific Columns
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @summary Define specific Column instances.
+   @param {Object} properties - default Column properties
+   @return {Column} a new Column class of Component
+   @description
+
+   Allow to define specialized instances of
+   [Column](module-dome_table_views.Column.html)
+
+   @example // Example of column type
+   import { DefineColumn } from 'dome/table/views' ;
+   export const ColumnCheck = DefineColumn({
+     align: 'center',
+     renderValue: (ok) => <Icon id={ok ? 'CHECK' : 'CROSS'}/>
+   });
+
+   @example // Usage in a Table
+   <Table ...>
+      <Column id='name' label='Name' fill />
+      <ColumnCheck id='check' label='Checked' />
+   </Table>
+
+ */
+export const DefineColumn = (DEFAULTS) => {
+  function Component() { return null; };
+  Component._DOME_COLUMN_DEFAULTS = DEFAULTS ;
+  return Component ;
+};
+
+// --------------------------------------------------------------------------
+// --- Table Rows
+// --------------------------------------------------------------------------
+
+const rowClassName =
+      (multipleSelection,selected) =>
+      ({index}) =>
+      (multipleSelection
+       ? 0 <= _.sortedIndexOf( selected , index )
+       : (index === selected))
+      ? 'dome-color-selected' :
+      index & 1 ? 'dome-xTable-even' : 'dome-xTable-odd' ;
+
+// --------------------------------------------------------------------------
+// --- Table View
+// --------------------------------------------------------------------------
+
+// Must be kept in sync with table.css
+const CSS_HEADER_HEIGHT = 22 ;
+const CSS_ROW_HEIGHT = 20 ;
+const DEFAULT_STATE = { width:{}, resize:{}, visible:{} };
+
+/**
+   @class
+   @summary Table View.
+   @property {Model} model - table data proxy (required)
+   @property {Column[]} children - one or more table columns (required)
+   @property {string} [settings] - window settings for column size & visibility (optional)
+   @property {any} [selection] - current selection (depends on `multipleSelection`)
+   @property {function} [onSelection] - callback to selection changes (depends on `multipleSelection`)
+   @property {boolean} [multipleSelection] - select single or multiple selection
+   @property {any} [scrollToItem] - ensures the item is visible
+   @property {function} [renderEmpty] - callback to render an empty table
+   @description
+
+   This component is base on [React-Virtualized
+   Tables](https://bvaughn.github.io/react-virtualized/#/components/Table),
+   offering a lazy, super-optimized rendering process that scales on huge
+   datasets.
+
+   A table shall be connected to an instance of
+   [Model](module-dome_table_models.Model.html) class to retrieve the data and
+   get informed of data updates.
+
+   The table columns shall be instances of
+   [Column](module-dome_table_views.Column.html) class.
+
+   Clicking on table headers trigger re-ordering callback on the model with the
+   expected column and direction, unless disabled _via_ the column
+   specification. However, actual sorting (and corresponding feedback on table
+   headers) would only take place if the model supports re-ordering and
+   eventually triggers a reload.
+
+   Right-clicking the table headers displays a popup-menu with actions to reset natural ordering,
+   reset column widths and select column visibility.
+
+   Tables do not control item selection state. Instead, you shall supply the selection
+   state and callback _via_ properties, like any other controlled React components.
+
+   Item selection can be based either on single-row or multiple-row. In case of
+   single-row selection (`multipleSelection:false`, the default), selection state
+   must be a single item or `undefined`, and the `onSelection` callback is called
+   with the same type of values.
+
+   In case of multiple-row selection (`multipleSelection:true`), the selection state
+   shall be an _array_ of items, and `onSelection` callback also. Single items are _not_
+   accepted, but `undefined` selection can be used in place of an empty array.
+
+   Clicking on a row triggers the `onSelection` callback with the updated selection.
+   In single-selection mode, the clicked item is sent to the callback. In
+   multiple-selection mode, key modifiers are taken into account for determining the new
+   slection. By default, the new selection only contains the clicked item. If the `Shift`
+   modifier has been pressed, the current selection is extended with a range of items
+   from the last selected one, to the newly selected one. If the `CtrlOrCmd` modifier
+   has been pressed, the selection is extended with the newly clicked item.
+   Clicking an already selected item with the `CtrlOrCmd` modifier removes it from
+   the current selection.
+
+ */
+export class Table extends React.Component {
+
+  constructor(props)
+  {
+    super(props);
+
+    // Table Reload
+    this.tableRef = undefined ;
+    this.setTableRef = (ref) => this.tableRef = ref ;
+    this.reloadTable = () => {
+      this.reloaded = true ;
+      setImmediate(() => {
+        const ref = this.tableRef ;
+        if (ref) {
+          this.forceUpdate();
+          ref.forceUpdateGrid();
+        }
+      });
+    };
+
+    // Model Watching
+    this.watchModel = ({startIndex,stopIndex}) => {
+      this.props.model._watch( this.client , startIndex , stopIndex );
+    };
+
+    // Default Context Menu
+    this.resetOrdering = () => this.props.model.setOrdering() ;
+
+    // Column States
+    this.state = Object.assign(
+      DEFAULT_STATE,
+      Dome.getWindowSetting( this.props.settings )
+    );
+    this.restoreDefaults = () => this.setState( DEFAULT_STATE );
+
+    // Header Reset Resizing
+    this.resetResizing = () => this.setState({ width:{}, resize:{} });
+
+    // Header Column References
+    this.headerRef = (id,elt) => {
+      const old = this.state.width[id] ;
+      const current = computeWidth(elt);
+      if (elt && old !== current) {
+        const columns = Object.assign( {}, this.state.width );
+        columns[id] = current ;
+        this.setState({ width: columns });
+      }
+    };
+
+    // Column Resizing
+    this.resizeColumns = (lcol,rcol,delta) => {
+      const columnSize = this.state.width ;
+      const wl = columnSize[lcol] + delta ;
+      const wr = columnSize[rcol] - delta ;
+      if (wl > 40 && wr > 40) {
+        const resize = Object.assign( {}, this.state.resize );
+        resize[lcol] = wl ;
+        resize[rcol] = wr ;
+        this.setState({ resize });
+      }
+    };
+
+    // Column Visibility
+    this.isVisible = (visible) => (elt) => {
+      const props = elt.props ;
+      const v = visible[props.id] ;
+      if (v !== undefined) return v;
+      const p = props.visible ;
+      switch( p ) {
+      case 'never':
+      case null:
+        return false;
+      case 'always':
+      case undefined:
+        return true;
+      default:
+        return p;
+      }
+    };
+
+    // Selection
+    this.selectRow = this.selectRow.bind(this);
+    this.contextMenu = this.contextMenu.bind(this);
+
+  }
+
+  // --- Life Cycle (binding to model)
+
+  componentDidMount()
+  {
+    Dome.on('dome.defaults',this.restoreDefaults );
+    this.client = this.props.model._bind(this.reloadTable);
+  }
+
+  componentWillUnmont()
+  {
+    Dome.off('dome.defaults',this.restoreDefaults );
+    this.props.model._remove(this.client);
+    this.tableRef = undefined ;
+  }
+
+  componentDidUpdate()
+  {
+    Dome.setWindowSetting( this.props.settings, this.state );
+  }
+
+  // --- Column Resizers
+
+  computeResizers(columns) {
+    // Insert a resizer on each side of non-fixed columns,
+    // provided there also exists some non-fixed column on both side.
+    if (columns.length < 2) return null;
+    const resizing = columns.map( ({props:{id,fixed}}) => ({id,fixed}) );
+    var k, cid ;
+    for (cid = undefined, k = 0; k < columns.length; k++) {
+      const r = resizing[k];
+      r.left = cid ;
+      if (!r.fixed) cid = r.id ;
+    }
+    for (cid = undefined, k = columns.length-1; 0 <= k ; k--) {
+      const r = resizing[k];
+      r.right = cid ;
+      if (!r.fixed) cid = r.id ;
+    }
+    var offset = 0 , resizers = [] ;
+    const columnSize = this.state.width ;
+    for (k = 0; k < columns.length - 1 ; k++) {
+      const width = columnSize[resizing[k].id] ;
+      if (!width) return null;
+      offset += width ;
+      const a = resizing[k];
+      const b = resizing[k+1];
+      if ((!a.fixed || !b.fixed) && a.right && b.left) {
+        const id = k ;
+        const onStart = () => { this.dragging = id ; this.forceUpdate(); };
+        const onStop = () => { this.dragging = undefined ; this.forceUpdate(); };
+        const resizer = (
+          <Resizer key={id}
+                   id={id}
+                   dragging={this.dragging}
+                   onStart={onStart}
+                   onStop={onStop}
+                   onDrag={this.resizeColumns}
+                   offset={offset}
+                   left={b.left}
+                   right={a.right} />
+        );
+        resizers.push(resizer);
+      }
+    }
+    return resizers ;
+  }
+
+  // --- Context Menu
+
+  contextMenu() {
+    var has_order ;
+    var has_width ;
+    var has_default ;
+    const children = this.props.children ;
+    React.Children.forEach(children, (elt) => {
+      if (elt) {
+        const { fixed, disableSort, visible } = elt.props ;
+        if (!disableSort) has_order = true ;
+        if (!fixed) has_width = true ;
+        if (visible!=='always' && visible!=='never')
+          has_default = true ;
+      }
+    });
+    const items = [
+      { label: 'Reset Ordering',
+        display:has_order, onClick:this.resetOrdering },
+      { label: 'Reset Column widths',
+        display:has_width, onClick:this.resetResizing },
+      { label: 'Restore Columns defaults',
+        display:has_default, onClick:this.restoreDefaults },
+      'separator'
+    ];
+    const visible = Object.assign( {}, this.state.visible );
+    React.Children.forEach(children, (elt) => {
+      if (elt) {
+        switch(elt.props.visible) {
+        case 'never':
+        case 'always':
+          break;
+        default:
+          const { id, label, title } = elt.props ;
+          const checked = this.isVisible(visible)(elt);
+          const onClick = () => {
+            visible[id] = !checked ;
+            this.setState({ visible });
+          };
+          items.push({ label: label || title, checked, onClick });
+        }
+      }
+    });
+    Dome.popupMenu(items);
+  }
+
+  // --- Row Selection
+
+  selectRow({event, index, rowData:{item}}) {
+    this.focus = item ;
+    if (item) {
+      const { model, multipleSelection , selection, onSelection } = this.props ;
+      if (multipleSelection) {
+        const selectedItems =
+              selection === undefined ? [] :
+              Array.isArray(selection) ? selection :
+              [selection] ;
+        if (event.metaKey || event.ctrlKey) {
+          var s, a ;
+          const isClicked = (e) => model.getIndexOf(e) === index ;
+          if (_.find( selectedItems , isClicked )) {
+            s = _.filter( selectedItems, (e) => model.getIndexOf(e) !== index );
+          } else {
+            s = selectedItems.slice();
+            s.push(item);
+            a = index ;
+          }
+          this.anchor = a ;
+          this.anchored = undefined ;
+          onSelection(s);
+        }
+        else if (event.shiftKey && this.anchor) {
+          var old = this.anchored || (this.anchored = selection) ;
+          var updated = old.slice();
+          var anchor = this.anchor ;
+          var k ;
+          if (anchor < index)
+            for (k = anchor ; k <= index ; k++) {
+              updated.push(model.getItemAt(k));
+            }
+          else
+            for (k = anchor ; index <= k ; k--) {
+              updated.push(model.getItemAt(k));
+            }
+          // No anchor modification
+          onSelection(_.uniqBy(updated, model.getIndexOf.bind(model)));
+        }
+        else {
+          this.anchor = index ;
+          this.anchored = undefined ;
+          onSelection([item]);
+        }
+      } else {
+        onSelection(item);
+      }
+    }
+  }
+
+  // --- Rendering
+
+  render() {
+
+    const {
+      model, renderEmpty,
+      multipleSelection, selection, onSelection,
+      scrollToItem
+    } = this.props ;
+
+    const itemCount = model.getItemCount();
+    const ordering = model.getOrdering();
+    var selected = undefined ;
+    if (selection)
+      if (multipleSelection && Array.isArray(selection)) {
+        selected = selection.map((elt) => {
+          var k = model.getIndexOf(elt);
+          return Number.isInteger(k) ? k : -1 ;
+        }).sort((a,b) => a-b);
+      } else
+        selected = model.getIndexOf(selection);
+
+    const rowGetter = ({index}) => ({
+      model , item: (index < itemCount ? model.getItemAt(index) : undefined)
+    }) ;
+
+    const isVisible = this.isVisible(this.state.visible);
+    const columns = React.Children.toArray(this.props.children).filter(isVisible);
+    var hasFill = false ;
+    var lastElt = undefined ;
+    React.Children.forEach(columns,(elt) => {
+      if (elt.props.fill) hasFill = true ; else lastElt = elt ;
+    });
+    const SizedTable = ({ height, width }) => {
+      const tableHeight = CSS_HEADER_HEIGHT + CSS_ROW_HEIGHT * itemCount ;
+      const smallHeight = itemCount > 0 && tableHeight < height ;
+      const rowCount = ( smallHeight ? itemCount + 1 : itemCount) ;
+      const reloaded = this.reloaded ;
+      if (reloaded) this.reloaded = false ;
+      const scrollToIndex =
+            scrollToItem ? model.getIndexOf(scrollToItem) :
+            reloaded && this.focus ? model.getIndexOf(this.focus) : undefined ;
+      const resizers = this.computeResizers(columns);
+      const renderColumn = vColumn({
+        headerRef: this.headerRef,
+        hasFill, lastElt,
+        columnResize:this.state.resize
+      });
+      return (
+        <React.Fragment>
+          <VTable
+            ref={this.setTableRef}
+            key='table'
+            displayName='React-Virtualized-Table'
+            width={width}
+            height={height}
+            rowCount={rowCount}
+            noRowsRenderer={renderEmpty}
+            rowGetter={rowGetter}
+            rowClassName={rowClassName(multipleSelection,selected)}
+            rowHeight={CSS_ROW_HEIGHT}
+            headerHeight={CSS_HEADER_HEIGHT}
+            headerRowRenderer={headerRowRenderer(this.contextMenu)}
+            onRowsRendered={this.watchModel}
+            onRowClick={onSelection && this.selectRow}
+            sortBy={ordering && ordering.sortBy}
+            sortDirection={ordering && ordering.sortDirection}
+            sort={model.setOrdering.bind(model)}
+            scrollToIndex={ scrollToIndex }
+            scrollToAlignment='center'
+            >
+            {React.Children.map(columns,renderColumn)}
+          </VTable>
+          {resizers}
+        </React.Fragment>
+      );
+    };
+    return (
+      <div className='dome-xTable'>
+        <AutoSizer key='table'>{SizedTable}</AutoSizer>
+      </div>
+    );
+  }
+}
+
+// --------------------------------------------------------------------------
+
+export default { Table, Column, DefineColumn };
+
+// --------------------------------------------------------------------------
diff --git a/ivette/src/dome/src/renderer/text/buffers.js b/ivette/src/dome/src/renderer/text/buffers.js
new file mode 100644
index 0000000000000000000000000000000000000000..5b67d43eceaa172baffb9c121546b4236940ec71
--- /dev/null
+++ b/ivette/src/dome/src/renderer/text/buffers.js
@@ -0,0 +1,458 @@
+// --------------------------------------------------------------------------
+// --- Text Documents
+// --------------------------------------------------------------------------
+
+/** @module dome/text/buffers */
+
+import Emitter from 'events' ;
+import CodeMirror from 'codemirror/lib/codemirror.js' ;
+
+const combineClass = (a,b) => a ? (b ? (a + " " + b) : a) : b ;
+
+// --------------------------------------------------------------------------
+// --- Marker Proxy
+// --------------------------------------------------------------------------
+
+class Proxy {
+
+  clear() { this.marker && this.marker.clear(); }
+  changed() { this.marker && this.marker.changed(); }
+  find() { return this.marker && this.marker.find(); }
+  _link(marker) { this.marker = marker ; }
+
+}
+
+// --------------------------------------------------------------------------
+// --- Buffer
+// --------------------------------------------------------------------------
+
+/**
+   @summary Rich Text Content and State.
+   @description
+
+   A buffer encapsulate a CodeMirror document instance inside an standard
+   Node event emitter. CodeMirror signals are automatically linked back to
+   the event emitter (on a lazy basis).
+
+   The `maxlines` will control the maximum number of lines kept in the buffer.
+   By default, it is set to `10000`, but you can use `null`, `0` or any negative
+   value to disable this behavior. See also `this.setMaxlines()` method.
+
+   Additionnaly, a Buffer maintains an _edited_ state internally that can
+   be manually updated, and that is automatically set to `true` when the
+   underlying CodeMirror document is changed. It is associated to an `'edited'`
+   event of the buffer, and can be used to manage the « Saved » / « Not-Saved »
+   state of an editor, for instance.
+
+   Typically, a sequence of changed events would fire a unique `'edited'` buffer
+   event, untill `setEdited(false)` is invoked. The `clear()` method also resets
+   the _edited_ state to `false`, but sill emit an `'edited'` event if the
+   buffer was not empty.
+
+   Buffers can also be updated programmatically by various methods. In addition to
+   specified CodeMirror modes, you can also attach text markers programmatically with
+   a push/pop API.
+
+   Text markers can be associated with an identifier, that can be used for
+   dynamic highlighting, called Decorations. Decorations are class names that
+   add styling attributes upon the current mode. Typically, consider only using
+   background colors, underlines and/or strike-through for decorations, since
+   font styles and colors are likely to be managed by CodeMirror modes, unless
+   you know exactly what your are doing.
+
+   The `setDecorator` method can be used to set or update
+   the highlighting function that computes the decoration of a text marker
+   from its identifier. When the decorations may have change, you shall either
+   set again the highlighting function with a call to `setDecorator()` or call
+   the `updateDecorations()` method. This also triggers the `'decorated'` event.
+
+ */
+
+export class Buffer extends Emitter {
+
+  /**
+     @param {object} [props] - Constructor properties (see below)
+     @param {string | object} [props.mode] - CodeMirror [mode](https://codemirror.net/mode/index.html) specification
+     @param {number} [props.maxlines] - Maximum number of lines in the buffer
+  */
+  constructor(props = {}) {
+    super();
+    const { mode , maxlines } = props ;
+    this._doc = new CodeMirror.Doc('',mode);
+    this._stacked = [] ;
+    this._edited = false ;
+    this._focused = false ;
+    this._markid = 0 ;
+    this._markers = {} ;
+    this._decorator = undefined ;
+    this.setMaxlines(maxlines);
+    this.setEdited = this.setEdited.bind(this);
+    this.setFocused = this.setFocused.bind(this);
+    this.clear = this.clear.bind(this);
+    this.append = this.append.bind(this);
+    this.log = this.log.bind(this);
+    this._doc.on('change', ( _target , { origin } ) => {
+      if (origin !== 'buffer') this.setEdited(true);
+    });
+  }
+
+  /**
+     @summary CodeMirror document instance.
+     @return {CodeMirror.Doc} internal [document](https://codemirror.net/doc/manual.html#api_doc)
+     @description
+     Returns the `CodeMirror.Doc` instance holding the buffer contents.
+     This can be used for further customization.
+     <p>
+     This document will never be bound to a `CodeMirror` instance. Instead, `Text` views
+     will use _linked_ documents to the buffer one. This allows for several views to
+     share the same document (and history).
+  */
+  getDoc() { return this._doc; }
+
+  // --------------------------------------------------------------------------
+  // --- Buffer Manipulation
+  // --------------------------------------------------------------------------
+
+  /** Clear buffer contents and resets _edited_ state. */
+  clear() {
+    this._doc.setValue('');
+    this._edited = false;
+    this._stacked = [] ;
+    this._focused = false ;
+    this._markid = 0 ;
+    this._markers = {} ;
+  }
+
+  /**
+     @summary Writes in the buffer.
+     @param {any} [value] - content to append in the buffer
+     @description
+     Appends textual contents to the end of the buffer.
+     All parameters are converted to string and joined with spaces.
+     The generated change event has origin `'buffer'` and it does not
+     modifies the _edited_ internal state.
+  */
+  append(...value) {
+    if (value.length > 0) {
+      const doc = this._doc ;
+      const from = doc.posFromIndex(Infinity);
+      const text = value.join(' ');
+      doc.replaceRange(text,from,undefined,'buffer');
+      this.shrink();
+    }
+  }
+
+  /**
+     @summary Starts a new line in the buffer.
+     @description
+     If the current buffer content does not finish at the beginning of a fresh line,
+     inserts a newline character.
+  */
+  flushline() {
+    const doc = this._doc ;
+    const from = doc.posFromIndex(Infinity);
+    if (from.ch > 0) doc.replaceRange('\n',from,undefined,'flush');
+  }
+
+  /**
+     @summary Appends with newline and auto-scrolling.
+     @param {any} [value] - content to append in the buffer
+     @description
+     This is a short-cut to `flushline()` followed by `append(...value,'\n')` and `scroll()`.
+   */
+  log(...value) {
+    this.flushline();
+    this.append(...value,'\n');
+    this.scroll();
+  }
+
+  /**
+     @summary Opens a text marker.
+     @param {object} options - CodeMirror
+       [text marker](https://codemirror.net/doc/manual.html#api_marker) options
+     @return {CodeMirror.TextMarker} the associated
+       [text marker](https://codemirror.net/doc/manual.html#api_marker) (proxy)
+     @description
+Opens a text marker at the current (end) position in the buffer.
+
+The text marker is stacked and would be actually created on the
+matching `closeTextMarker()` call. It will be applied to the full range of text
+inserted between its associated `openTextMarker()` and `closeTextMarker()` calls.
+
+The returned text marker is actually a _proxy_ to the text marker that
+will be eventually created by `closeTextMarker()`. Its methods (`find`, `clear` and `changed`)
+are automatically forwarded to the actual `CodeMirror.TextMarker` instance, once created.
+Hence, you can safely invoke these methods on either the _proxy_ or the _final_
+text marker at your convenience.
+
+Additionnaly to standard `CodeMirror.TextMarker` options, you may also the
+following Dome specific ones:
+- `id: string` assigns an identifier to the marker (expected to be unique) ;
+- `hover: boolean` makes the text-marker highlighted on mouse-hover ; this is compatible with
+  _nested_ markers, which is not possible with CSS `:hover` pseudo selectors ; defaults to `true` for
+  identified text markers and `false` otherwize.
+*/
+
+  openTextMarker( { id , hover, className, ...options } ) {
+    const from = this._doc.posFromIndex(Infinity);
+    const proxy = new Proxy();
+    this._stacked.push( { from , id , hover, className, options , proxy } );
+    return proxy ;
+  }
+
+  /**
+     @summary Close last opened marker.
+     @return {CodeMirror.TextMarker} the (actual)
+     [text marker](https://codemirror.net/doc/manual.html#api_marker)
+     @description
+     Closes the lastly opened text marker with `openTextMarker()`.
+     The method returns the _actual_
+     text marker ; the _proxy_ text marker provided by the corresponding
+     call to `openTextMarker()` is automatically bound to the actual one.
+  */
+  closeTextMarker() {
+    const tag = this._stacked.pop();
+    if (tag) {
+      const { id, hover, from } = tag ;
+      const to = this._doc.posFromIndex(Infinity);
+      var classNameWithId ;
+      var tagid ;
+      if ( id || hover ) {
+        const mhover = hover !== undefined ? hover : (id !== undefined) ;
+        const cid = id ? 'dome-xMark-' + id : ('dome-xHover-' + (++this._markid)) ;
+        const p = this._doc.indexFromPos(from);
+        const q = this._doc.indexFromPos(to);
+        const m = this._markers[cid];
+        if (m) console.warn('[Dome.text.buffers] duplicate marker',id);
+        tagid = this._markers[cid] = { id, hover:mhover, classNameId:cid, length: q-p } ;
+        classNameWithId = id ? "dome-xMarked " + cid : cid ;
+      }
+      const className = combineClass( tag.className, classNameWithId );
+      const options = Object.assign( { shared:true, className } , tag.options );
+      const marker = this._doc.markText( from , to , options );
+      tag.proxy._link(marker);
+      if (tagid) tagid.marker = marker ;
+      this.shrink();
+      return marker ;
+    } else
+      return undefined ;
+  }
+
+  /**
+     @description Lookup a text marker
+     @param {string} id - requested identifier
+     @return {CodeMirror.TextMarker} the identified text marker, or `undefined` if not found.
+  */
+  findTextMarker(id) {
+    if (id) {
+      const m = this._markers['dome-xMark-' + id] ;
+      if (m) return m.marker ;
+    }
+    return undefined;
+  }
+
+  /**
+     @summary Lookup a hover class.
+     @param {string} className - a class name, possibly identifying a hover element
+     @return {hover} the associated hovered element `{id, classNameId, length}`, or
+     `undefined` if the provided `className` does not identify any such element.
+   */
+  findHover(name) { return this._markers[name]; }
+
+  // --------------------------------------------------------------------------
+  // --- Highlighter
+  // --------------------------------------------------------------------------
+
+  /**
+     @summary Define highlighter.
+     @param {function} highlighter - the function that computes
+     the decoration class of a marker id.
+  */
+  setDecorator(f) { this._decorator = f ; this.emit('decorated'); }
+
+  /**
+     @summary Current highlighter.
+     @return {function} the current highlighting function
+   */
+  getDecorator() { return this._decorator ; }
+
+  /**
+     @summary Rehighlight document.
+     @description
+     Emits the `'decorated'` event to make editors
+     updating the decorations of identified text markers.
+  */
+  updateDecorations() { this.emit('decorated'); }
+
+  // --------------------------------------------------------------------------
+  // --- Edited State
+  // --------------------------------------------------------------------------
+
+  /**
+     @summary Set edited state.
+     @param {boolean} [state] - the new edited state (defaults to `true`).
+     @description
+
+Set the _edited_ internal state. The method is automatically invoked by editor
+views when the user actually edit the document.  The state is _not_ modified
+when modifying the document from buffer's own methods, _eg._ `append()` and
+`clear()`.
+
+The method fires the `'edited'` event on modifications.  This method is bound to
+`this`, hence `this.setEdited` can be used as a valid callback function.
+  */
+  setEdited(state = true) {
+    if ( state !== this._edited ) {
+      this._edited = state ;
+      this.emit('edited',state);
+    }
+  }
+
+  /**
+     @summary Set focused state.
+     @param {boolean} [focus] - the new focused state (defaults to `true`).
+     @description
+
+Set the _focused_ internal state. The method is automatically invoked by editor
+views when they gain or lose focus or when the user actually interact with the
+view, eg. mouse-scrolling, edition, cursor move, etc.  The escape key `ESC`
+explicitly relax the _focused_ state, although the editor view might actually
+keep the _focus_.
+
+When a buffer is _focused_, shrinking and auto-scrolling are temporarily deactivated
+to avoid confusing user's experience.
+
+The method fires `'focused'` events on modifications. This method is bound to
+`this`, hence `this.setEdited` can be used as a valid callback function.
+  */
+  setFocused(state = true) {
+    if ( state !== this._focused ) {
+      this._focused = state ;
+      this.emit('focused',state);
+      this.shrink();
+    }
+  }
+
+  /** Returns the current _edited_ state. */
+  isEdited() { return this._edited; }
+
+  /** Returns the current _focused_ state. */
+  isFocused() { return this._focused; }
+
+  // --------------------------------------------------------------------------
+  // --- Document Scrolling
+  // --------------------------------------------------------------------------
+
+  /**
+     @summary Set (or unset) the maximum number of lines.
+     @param {number} [maxlines] - maximum number of lines
+     @description
+
+By default, the maximum number of lines is set to `10,000` to avoid
+unwanted memory leaks. Setting `null`, `0` of any negative value cancel
+the management of maximum number of lines.
+
+Although CodeMirror is powerfull enough to manage huge buffers,
+you should turn this limit _off_ with care.
+  */
+  setMaxlines(maxlines=10000) {
+    this._maxlines = maxlines > 0 ? 1 + maxlines : 0 ;
+    this.shrink();
+  }
+
+  /**
+      @summary Remove head lines to fits into maximum lines.
+      @description
+
+Shrinking is activated when `maxlines` property is set to a strictly
+positive number. When the number of lines in the buffer is larger than
+the max, buffer is trimmed by removing extra _heading_ lines.
+
+When the buffer if _focused_ or when there are still opened text marks pending,
+shrinking is automatically postponed until focus is lost and all pending marks
+have been closed.
+   */
+  shrink()
+  {
+    if (!this._focused && this._maxlines > 0 && this._stacked.length == 0) {
+        const lines = this._doc.lineCount();
+        if (lines > this._maxlines) {
+          const p = this._doc.firstLine();
+          const q = p + lines - this._maxlines ;
+          this._doc.replaceRange('',{line:p,ch:0},{line:q,ch:0},'buffer');
+        }
+    }
+  }
+
+  /**
+     @summary Requires all connected views to scroll to the specified position in the buffer.
+     @param {...any} [args] - the position or range to be made visible
+     @description
+Typical usage:
+ - `scroll()` to the end of buffer
+ - `scroll(id)` to identified marked `id`;
+ - `scroll(p)` a position: line number or `{line,ch?}` CodeMirror position;
+ - `scroll(p,q)` a range of two positions (like above);
+ - `scroll({from,to})` an object range of two positions (like above).
+
+When the buffer is _focused_, programmatic auto-scrolling with `scroll()`
+is blocked.
+   */
+  scroll(a,b) {
+    switch(typeof(a)) {
+    case 'undefined':
+      if (this._focused) return;
+      this.emit('scroll',{line:this._doc.lastLine(),ch:0});
+      break;
+    case 'string':
+      const tm = this.findTextMarker(a);
+      const rg = tm && tm.find();
+      if (rg) this.emit('scroll',rg);
+      break;
+    default:
+      const isLineCh = (a) => (
+        a && Number.isInteger(a.line)
+          && (a.ch === undefined || Number.isInteger(a.ch))
+      );
+      const getPos = (a) => (
+        Number.isInteger(a) ? { line: a, ch:0 } :
+        isLineCh(a) ? a : console.warn('[Dome.text.buffers] can not scroll to',a)
+      );
+      var from,to ;
+      if (a && a.from && a.to) {
+        from = getPos(a.from);
+        to = getPos(a.to);
+      } else {
+        from = getPos(a);
+        to = b && getPos(b);
+      }
+      if (from && to) this.emit('scroll',{from,to});
+      else if (from) this.emit('scroll',from);
+    }
+  }
+
+  // --------------------------------------------------------------------------
+  // --- Document Linking
+  // --------------------------------------------------------------------------
+
+  /**
+     @summary Linked CodeMirror document.
+     @description
+     Returns a CodeMirror document linked to this buffer (with shared history).
+  */
+  linkedDoc() { return this._doc.linkedDoc( { sharedHist: true } ); }
+
+  /**
+     @summary Release a linked CodeMirror document.
+     @description
+     Unlinks a CodeMirror document obtained by `linkedDoc()`.
+  */
+  unlinkDoc(doc) { this._doc.unlinkDoc( doc ); }
+
+}
+
+// --------------------------------------------------------------------------
+
+export default { Buffer };
+
+// --------------------------------------------------------------------------
diff --git a/ivette/src/dome/src/renderer/text/editors.css b/ivette/src/dome/src/renderer/text/editors.css
new file mode 100644
index 0000000000000000000000000000000000000000..664097705dc1179709d0fe70c3d091b9957b081b
--- /dev/null
+++ b/ivette/src/dome/src/renderer/text/editors.css
@@ -0,0 +1,29 @@
+/* -------------------------------------------------------------------------- */
+/* --- Styling Code Editors                                               --- */
+/* -------------------------------------------------------------------------- */
+
+.dome-xText {
+    flex: 1 1 auto ;
+    overflow: hidden ;
+    position: relative ;
+    fontSize: smaller ;
+}
+
+.dome-xText .CodeMirror {
+    width: 100% ;
+    height: 100% ;
+}
+
+.dome-xText-hover {
+    background: lightgreen ;
+}
+
+.dome-xText-select {
+    background: #ffda95 !important ;
+}
+
+.dome-xText-select.dome-xText-hover {
+    background: orange !important ;
+}
+
+/* -------------------------------------------------------------------------- */
diff --git a/ivette/src/dome/src/renderer/text/editors.js b/ivette/src/dome/src/renderer/text/editors.js
new file mode 100644
index 0000000000000000000000000000000000000000..2caad57cbe7af675ca1aa391f456b3c85f637be2
--- /dev/null
+++ b/ivette/src/dome/src/renderer/text/editors.js
@@ -0,0 +1,394 @@
+// --------------------------------------------------------------------------
+// --- Text Documents
+// --------------------------------------------------------------------------
+
+/** @module dome/text/editors */
+
+import React from 'react' ;
+import Dome from 'dome' ;
+import CodeMirror from 'codemirror/lib/codemirror.js' ;
+import 'codemirror/lib/codemirror.css' ;
+import './editors.css' ;
+import _ from 'lodash' ;
+
+const CSS_HOVERED = 'dome-xText-hover' ;
+const CSS_SELECTED = 'dome-xText-select' ;
+
+// --------------------------------------------------------------------------
+// --- Text View
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @summary Rich Text Editor.
+   @property {Buffer} buffer -
+      associated [Buffer](module-dome_text_buffers.Buffer.html) holding the text content
+   @property {string} className - additional class name(s)
+   @property {object} style - additional CSS style
+   @property {number} fontSize - editor font-size
+   @property {string} selection - currently selected markder identifier
+   @property {function} onSelection - callback used when an identified marker is clicked
+   @property {function} onContextMenu - selection callback on right-click
+   @property {object} [...options] - additional CodeMirror
+      [configuration](https://codemirror.net/doc/manual.html#config) properties
+   @description
+
+   A component rendering the content of a text buffer, that shall be instances
+   of the [Buffer](module-dome_text_buffers.Buffer.html) base class.
+
+   The view is based on a [CodeMirror](https://codemirror.net) component linked with
+   the internal Code Mirror Document from the associated buffer.
+
+   Multiple views might share the same buffer as source content. The buffer will be
+   kept in sync with all its linked views.
+
+   The Text component never update its mounted NODE element, however, all property
+   modifications (including buffer) are propagated to the internal CodeMirror instance.
+   Undefined properties are set (or reset) to the CodeMirror defaults.
+
+   ##### Themes
+
+   The CodeMirror `theme` option allow you to style your document,
+   especially when using modes.
+   Themes are only accessible if you load the associated CSS style sheet.
+   For instance, to use the `'ambiance'` theme provided with CodeMirror, you shall
+   import `'codemirror/theme/ambiance.css'` somewhere in your application.
+
+   ##### Modes & Adds-On
+
+   You can install modes and adds-on provided by the CodeMirror distribution by
+   simply importing (once, before being used) the associated modules in your
+   application.  For instance, to use the `'javascript'` mode option, you shall
+   import `'codemirror/mode/javascript/javascript.js'` file in your application.
+
+   ##### Further Customization
+
+   You can register your own extensions directly into the global `CodeMirror`
+   class instance.  However, the correct instance must be retrieved by using
+   `import CodeMirror from 'codemirror/lib/codemirror.js'` ; using `from
+   'codemirror'` returns a different instance of `CodeMirror` class and will
+   not work.
+
+ */
+
+export class Text extends React.Component {
+
+  constructor(props) {
+    super(props);
+    this.mountPoint = this.mountPoint.bind(this);
+    this.refresh = this.refresh.bind(this);
+    this.autoRefresh = this.autoRefresh.bind(this);
+    this.onBlur = this.onBlur.bind(this);
+    this.onFocus = this.onFocus.bind(this);
+    this.onScroll = this.onScroll.bind(this);
+    this.onClick = this.onClick.bind(this);
+    this.onContextMenu = this.onContextMenu.bind(this);
+    this.onMouseMove = this.onMouseMove.bind(this);
+    this.handleKey = this.handleKey.bind(this);
+    this.handleUpdate = this.handleUpdate.bind(this);
+    this.handleHover = _.throttle(this.handleHover,50);
+    this.handleScrollTo = this.handleScrollTo.bind(this);
+    this.rootElement = undefined ;
+    this.decorations = {} ;
+    this.hover = undefined ;
+  }
+
+  // --------------------------------------------------------------------------
+  // --- Mounting
+  // --------------------------------------------------------------------------
+
+  mountPoint(elt) {
+    this.rootElement = elt ;
+    if (elt) {
+      // Mounting...
+      const { buffer,
+              selection,     /* ignored */
+              onSelection,   /* ignored */
+              onContextMenu, /* ignored */
+              fontSize,      /* ignored */
+              className,     /* ignored */
+              style,         /* ignored */
+              ...config } = this.props ;
+      const value = buffer ? buffer.linkedDoc() : "" ;
+      const cm = this.codeMirror = new CodeMirror(elt, { value });
+      // Passing all options to constructor does not work (Cf. CodeMirror's BTS)
+      for (var opt in config) cm.setOption( opt , config[opt] );
+      cm.on('update',this.handleUpdate);
+      cm.on('keyHandled',this.handleKey);
+      buffer.on('decorated',this.handleUpdate);
+      buffer.on('scroll',this.handleScrollTo);
+      Dome.on('dome.update',this.refresh);
+      this.refreshPolling = setInterval( this.autoRefresh, 250 );
+      this.handleUpdate();
+    } else {
+      // Unmounting...
+      const polling = this.refreshPolling ;
+      if (polling) {
+        clearInterval( polling );
+        this.refreshPolling = undefined ;
+      }
+      const cm = this.codeMirror ;
+      Dome.off('dome.update',this.refresh);
+      const { buffer } = this.props ;
+      if (cm && buffer) {
+        buffer.unlinkDoc(cm.getDoc());
+        buffer.off('decorated',this.handleUpdate);
+        buffer.off('scroll',this.handleScrollTo);
+      }
+      this.codeMirror = undefined ;
+    }
+  }
+
+  // --------------------------------------------------------------------------
+  // --- Auto Refresh
+  // --------------------------------------------------------------------------
+
+  refresh() {
+    const elt = this.rootElement ;
+    const cm = this.codeMirror ;
+    if (cm && elt) {
+      this.currentWidth = elt.offsetWidth ;
+      this.currentHeight = elt.offsetHeight ;
+      this.currentParent = elt.offsetParent ;
+      cm.refresh();
+    }
+  }
+
+  // Polled every 250ms
+  autoRefresh() {
+    const elt = this.rootElement ;
+    const cm = this.codeMirror ;
+    if (cm && elt) {
+      const eltW = elt.offsetWidth ;
+      const eltH = elt.offsetHeight ;
+      const eltP = elt.offsetParent ;
+      if (eltW !== this.currentWidth ||
+          eltH !== this.currentHeight ||
+          eltP !== this.currentParent)
+      {
+        this.currentWidth = eltW ;
+        this.currentHeight = eltH ;
+        this.currentParent = eltP ;
+        cm.refresh();
+      }
+    }
+  }
+
+  // --------------------------------------------------------------------------
+  // --- Hover
+  // --------------------------------------------------------------------------
+
+  _findHover(elt) {
+    const buffer = this.props.buffer ;
+    var best = undefined ;
+    elt.classList.forEach((name) => {
+      const hover = buffer.findHover(name);
+      if (hover && (!best || hover.length < best.length )) best = hover ;
+    });
+    return best;
+  }
+
+  _findDecoration(classes,buffer,decorator)
+  {
+    var best_hover = undefined ;
+    var best_decor = undefined ;
+    var best_decoration = undefined ;
+    classes.forEach((name) => {
+
+      const hover = buffer.findHover(name);
+      var decoration = hover && hover.id && decorator(hover.id);
+
+      if (hover && (!best_hover || hover.length < best_hover.length))
+        best_hover = hover ;
+
+      if (decoration && (!best_decor || hover.length < best_decor.length)) {
+        best_decor = hover ;
+        best_decoration = decoration ;
+      }
+
+    });
+    return best_hover && { classNameId: best_hover.classNameId , decoration: best_decoration } ;
+  }
+
+  _markElementsWith(classNameId,className) {
+    const root = this.rootElement ;
+    const tohover = root && root.getElementsByClassName(classNameId);
+    const n = tohover ? tohover.length : 0 ;
+    for (var k = 0; k < n; k++) tohover[k].classList.add(className);
+  }
+
+  _unmarkElementsWith(className) {
+    const root = this.rootElement ;
+    const hovered = root && root.getElementsByClassName(className);
+    const n = hovered ? hovered.length : 0 ;
+    if (n==0) return ;
+    const elts = new Array(n); ;
+    for (var k = 0; k < n; k++) elts[k] = hovered[k];
+    elts.forEach((elt) => elt.classList.remove(className));
+  }
+
+  handleHover(target) {
+    // Throttled (see constructor)
+    const older = this.hover ;
+    const hover = this._findHover(target);
+    if (older !== hover) {
+      if (older) this._unmarkElementsWith( CSS_HOVERED );
+      if (hover && hover.hover) this._markElementsWith( hover.classNameId, CSS_HOVERED );
+      this.hover = hover ;
+    }
+  }
+
+  handleUpdate() {
+    const root = this.rootElement ;
+    const marked = root && root.getElementsByClassName('dome-xMarked');
+    const n = marked ? marked.length : 0 ;
+    if (n==0) return;
+    const hover = this.hover ;
+    const hovered = hover && hover.hover && hover.classNameId ;
+    const selection = this.props.selection ;
+    const selected = selection && ('dome-xMark-' + selection) ;
+    const buffer = this.props.buffer;
+    const decorator = buffer.getDecorator();
+    if ( !hovered && !selection && !decorator ) return;
+    const actual = {} ;
+    for (var k = 0; k < n; k++) {
+      const elt = marked[k];
+      const classes = elt.classList ;
+      if (hovered && classes.contains(hovered)) classes.add( CSS_HOVERED );
+      if (selected && classes.contains(selected)) classes.add( CSS_SELECTED );
+      if (decorator) {
+        const hover = this._findDecoration(classes,buffer,decorator,hovered,selected);
+        if (hover) {
+          const prev = this.decorations[ hover.classNameId ];
+          const curr = hover.decoration ;
+          if (prev !== curr && prev) classes.remove(prev);
+          if (curr) { classes.add(curr); actual[ hover.classNameId ] = curr ; }
+        }
+      }
+    }
+    this.decorations = actual ;
+  }
+
+  onMouseMove(evt) {
+    // Not throttled (can not leak Synthetic Events)
+    this.handleHover( evt.target );
+  }
+
+  onMouseClick(evt,callback) {
+    // No need for throttling
+    const target = evt.target ;
+    if ( target && callback ) {
+      const hover = this._findHover(target);
+      if ( hover && hover.id ) callback( hover.id );
+    }
+    this.props.buffer.setFocused(true);
+  }
+
+  onClick(evt) { this.onMouseClick(evt,this.props.onSelection); }
+  onContextMenu(evt) { this.onMouseClick(evt,this.props.onContextMenu); }
+
+  // --------------------------------------------------------------------------
+  // --- Scrolling
+  // --------------------------------------------------------------------------
+
+  handleScrollTo(position) {
+    try {
+      const cm = this.codeMirror;
+      cm && cm.scrollIntoView(position);
+    } catch(_error) { } // Out of range
+  }
+
+  // --------------------------------------------------------------------------
+  // --- Focus
+  // --------------------------------------------------------------------------
+
+  handleKey(cm,key,_evt) {
+    switch(key) {
+    case 'Esc':
+      this.props.buffer.setFocused(false);
+      break;
+    default:
+      this.props.buffer.setFocused(true);
+      break;
+    }
+  }
+
+  onFocus() { this.props.buffer.setFocused(true); }
+  onBlur() { this.props.buffer.setFocused(false); }
+  onScroll() {
+    const cm = this.codeMirror;
+    if (cm) {
+      const rect = cm.getScrollInfo();
+      const delta = rect.height-rect.top-rect.clientHeight;
+      const buffer = this.props.buffer;
+      buffer.setFocused(delta > 0);
+    }
+  }
+
+  // --------------------------------------------------------------------------
+  // --- Rendering
+  // --------------------------------------------------------------------------
+
+  shouldComponentUpdate(newProps) {
+    const cm = this.codeMirror ;
+    if (cm) {
+      // Swap documents if necessary
+      const { buffer:oldBuffer,
+              selection:oldSelect,
+              ...oldConfig } = this.props ;
+      const { buffer:newBuffer,
+              selection:newSelect,
+              ...newConfig } = newProps ;
+      if (oldBuffer !== newBuffer) {
+        const newDoc = newBuffer.linkedDoc();
+        const oldDoc = cm.swapDoc( newDoc );
+        oldBuffer.unlinkDoc( oldDoc );
+      }
+      // Incremental update options
+      var opt ;
+      for ( opt in oldConfig ) if (!(opt in newConfig)) {
+        const defValue = CodeMirror.defaults[opt];
+        if (defValue)
+          cm.setOption( opt , defValue );
+      }
+      for ( opt in newConfig ) {
+        const oldValue = oldConfig[opt];
+        const newValue = newConfig[opt];
+        if (newValue !== oldValue) {
+          cm.setOption( opt , newValue );
+        }
+      }
+      // Update selection
+      if ( oldSelect !== newSelect ) {
+        if (oldSelect) this._unmarkElementsWith( CSS_SELECTED );
+        if (newSelect) this._markElementsWith( 'dome-xMark-' + newSelect, CSS_SELECTED );
+      }
+    }
+    // Keep mounted node unchanged
+    return false;
+  }
+
+  render() {
+    const { className, fontSize, style } = this.props ;
+    const theStyle = Object.assign( {} , style );
+    if (fontSize) theStyle.fontSize = fontSize ;
+    return (
+      <div className={'dome-xText ' + className}
+           style={theStyle}
+           ref={this.mountPoint}
+           onClick={this.onClick}
+           onContextMenu={this.onContextMenu}
+           onBlur={this.onBlur}
+           onFocus={this.onFocus}
+           onScroll={this.onScroll}
+           onMouseMove={this.onMouseMove}
+           />);
+  }
+
+}
+
+// --------------------------------------------------------------------------
+
+export default { Text };
+
+// --------------------------------------------------------------------------
diff --git a/ivette/src/dome/template/Application.js b/ivette/src/dome/template/Application.js
new file mode 100644
index 0000000000000000000000000000000000000000..f7720a2a81fa1ff5a15b2e0f58f28cf95140f1c0
--- /dev/null
+++ b/ivette/src/dome/template/Application.js
@@ -0,0 +1,22 @@
+// --------------------------------------------------------------------------
+// --- Main React Component rendered by './index.js'
+// --------------------------------------------------------------------------
+
+/*
+   Template from $(DOME)/template/Application.js
+
+   This module shall export a React Component that
+   will be rendered (with empty props and children)
+   in the main window of your application.
+
+*/
+
+import React from 'react' ;
+import Dome from 'dome' ;
+import 'dome/misc/exports' ;
+
+export default (() => (
+  <h1 className='dome-text-title' style={{margin: 24}}>
+    Hello World!
+  </h1>
+));
diff --git a/ivette/src/dome/template/Preferences.js b/ivette/src/dome/template/Preferences.js
new file mode 100644
index 0000000000000000000000000000000000000000..542d1d7c97a42463aa3f0f6f528e1262b58a4e07
--- /dev/null
+++ b/ivette/src/dome/template/Preferences.js
@@ -0,0 +1,20 @@
+// --------------------------------------------------------------------------
+// --- Main React Component rendered by './index.js'
+// --------------------------------------------------------------------------
+
+/*
+   Template from $(DOME)/template/Settings.js
+
+   This module shall export a React Component that
+   will be rendered (with empty props and children)
+   in the settings window of your application.
+
+*/
+
+import React from 'react' ;
+
+export default (() => (
+  <h1 className='dome-text-title' style={{margin: 24}}>
+    Settings (none)
+  </h1>
+));
diff --git a/ivette/src/dome/template/babelrc.json b/ivette/src/dome/template/babelrc.json
new file mode 100644
index 0000000000000000000000000000000000000000..3e895f0c581bd6d45a280384ee46860f7d37c213
--- /dev/null
+++ b/ivette/src/dome/template/babelrc.json
@@ -0,0 +1,21 @@
+// --------------------------------------------------------------------------
+// --- Template .babelrc configuration file for Dome
+// --------------------------------------------------------------------------
+
+/*
+   Template from $(DOME)/template/babelrc.json
+
+   This configuration is a necessary extension to electron-webpack
+   for enabling react-hot-reloading.
+*/
+
+{
+  "presets": [
+    [ "@babel/env", { "modules": false } ],
+    [ "@babel/react" ]
+  ],
+  "plugins": [
+    "@babel/plugin-proposal-object-rest-spread",
+    "react-hot-loader/babel"
+  ]
+}
diff --git a/ivette/src/dome/template/dome-pull.sh b/ivette/src/dome/template/dome-pull.sh
new file mode 100755
index 0000000000000000000000000000000000000000..b579cf35a1a2492f9cbe452e63abbc1a6dcf7244
--- /dev/null
+++ b/ivette/src/dome/template/dome-pull.sh
@@ -0,0 +1,50 @@
+# --------------------------------------------------------------------------
+# ---  Pulling Dome Updates
+# --------------------------------------------------------------------------
+
+if [ ! -z "$(git status --porcelain)" ]
+then
+    echo "Local non-commited modifications, aborted."
+    exit 1
+fi
+
+if [ ! -d .gitdome ]
+then
+    git clone git@git.frama-c.com:dome/electron.git .gitdome
+fi
+
+head=$(git rev-parse --abbrev-ref HEAD)
+commit=$(git -C .gitdome rev-parse HEAD)
+remote=$(git -C .gitdome rev-parse --abbrev-ref HEAD)
+
+git checkout stable/dome
+
+if [ "$?" != "0" ]
+then
+    echo "Missing local branch stable/dome, aborted."
+    exit 1
+fi
+
+echo "Pulling Dome updates from $remote to stable/dome..."
+git -C .gitdome pull --prune
+
+for f in $(git -C .gitdome ls-files)
+do
+    mkdir -p $(dirname $f)
+    cp -f .gitdome/$f $f
+    git add $f
+done
+
+if [ ! -z "$(git status --porcelain)" ]
+then
+    git commit -m "[Dome] $commit"
+    echo "Dome repository updated."
+    git checkout $head
+    echo "Merging Dome updates..."
+    git merge stable/dome
+else
+    echo "Dome is already up-to-date."
+    git checkout $head
+fi
+
+# --------------------------------------------------------------------------
diff --git a/ivette/src/dome/template/dome-push.sh b/ivette/src/dome/template/dome-push.sh
new file mode 100755
index 0000000000000000000000000000000000000000..60816ed9ecb0103ee1206d5c28db7ecada3737e5
--- /dev/null
+++ b/ivette/src/dome/template/dome-push.sh
@@ -0,0 +1,56 @@
+# --------------------------------------------------------------------------
+# ---  Pulling Dome Updates
+# --------------------------------------------------------------------------
+
+branch=$1
+app=$(basename $1)
+
+if [ "$branch" == "" ]
+then
+    echo "Missing branch name, aborted."
+    exit 1
+fi
+
+if [ ! -z "$(git status --porcelain)" ]
+then
+    echo "Local non-commited modifications, aborted."
+    exit 1
+fi
+
+if [ ! -d .gitdome ]
+then
+    git clone git@git.frama-c.com:dome/electron.git .gitdome
+fi
+
+head=$(git rev-parse --abbrev-ref HEAD)
+commit=$(git rev-parse HEAD)
+remote=$(git -C .gitdome rev-parse --abbrev-ref HEAD)
+
+echo "[dome] update $branch"
+git -C .gitdome fetch --prune
+git -C .gitdome checkout $branch
+
+if [ "$?" != "0" ]
+then
+    git -C .gitdome checkout -b $branch
+else
+    git -C .gitdome pull --rebase
+fi
+
+echo "[dome] push updates from $head to $branch..."
+
+for f in $(git ls-files)
+do
+    mkdir -p .gitdome/$(dirname $f)
+    cp -f $f .gitdome/$f
+    git -C .gitdome add $f
+done
+
+echo "[dome] commit $branch"
+git -C .gitdome commit -e -m "[$app] $commit"
+echo "[dome] push $branch"
+git -C .gitdome push origin -f -u $branch
+echo "[dome] back to $remote"
+git -C .gitdome checkout $remote
+
+# --------------------------------------------------------------------------
diff --git a/ivette/src/dome/template/electron-webpack.json b/ivette/src/dome/template/electron-webpack.json
new file mode 100644
index 0000000000000000000000000000000000000000..048484644edbcf136112d58accdf3e584cda1979
--- /dev/null
+++ b/ivette/src/dome/template/electron-webpack.json
@@ -0,0 +1,22 @@
+// --------------------------------------------------------------------------
+// --- Electron-webpack configuration file for Dome
+// --------------------------------------------------------------------------
+
+/*
+   Template from $(DOME)/template/electron-webpack.json
+
+   This configuration file makes electron-webpack loading
+   webpack extensions to resolve Dome packages differently
+   for electron main-process and renderer-process.
+
+   You may configure further electron-webpack to your convenience.
+*/
+
+{
+  "main": {
+    "webpackConfig": "webpack.main.js"
+  },
+  "renderer": {
+    "webpackConfig": "webpack.renderer.js"
+  }
+}
diff --git a/ivette/src/dome/template/export.sh b/ivette/src/dome/template/export.sh
new file mode 100755
index 0000000000000000000000000000000000000000..ea5dc39d5fee0fabce5501431253fce3e19bf788
--- /dev/null
+++ b/ivette/src/dome/template/export.sh
@@ -0,0 +1,27 @@
+#!/bin/bash
+
+# --------------------------------------------------------------------------
+# ---  Export Dome API
+# --------------------------------------------------------------------------
+
+DOME=$1
+COND=''
+COND+=' -not -path "*/demo/*"'
+COND+=' -not -name "Application.js"'
+COND+=' -not -name "Preferences.js"'
+COND+=' -not -name "dome.js"'
+COND+=' -not -name "index.js"'
+
+FILES=`cd $DOME/src ; find renderer -name "*.js" $COND`
+
+echo "{ let m = require('react'); register('react',m); }"
+echo "{ let m = require('dome'); register('dome',m); }"
+echo "{ let m = require('dome/system'); register('dome/system',m); }"
+for f in $FILES
+do
+    j=${f/renderer/dome}
+    m=${j/.js/}
+    echo "{ let m = require('$m'); register('$m',m); }"
+done
+
+# --------------------------------------------------------------------------
diff --git a/ivette/src/dome/template/git-ignore b/ivette/src/dome/template/git-ignore
new file mode 100644
index 0000000000000000000000000000000000000000..da037a9e2fa9fa6eb676dcadabf0f8486c4b79bf
--- /dev/null
+++ b/ivette/src/dome/template/git-ignore
@@ -0,0 +1,14 @@
+# --------------------------------------------------------------------------
+# --- Template .gitignore file for Dome
+# --------------------------------------------------------------------------
+
+.dome-*.stamp
+.dome-*.back
+node_modules
+yarn.lock
+yarn-error.log
+/bin
+/dist
+/doc/html
+
+# --------------------------------------------------------------------------
diff --git a/ivette/src/dome/template/main.js b/ivette/src/dome/template/main.js
new file mode 100644
index 0000000000000000000000000000000000000000..d102caa8c34b4a2c33a0166f86e23fbea55f6bf2
--- /dev/null
+++ b/ivette/src/dome/template/main.js
@@ -0,0 +1,17 @@
+// --------------------------------------------------------------------------
+// --- Electron main-process entry-point for Dome.
+// --------------------------------------------------------------------------
+
+/*
+   Template of ./src/main/index.js
+   Imported from $(DOME)/template/main.js
+
+   The call to Dome.start() will initialize the Dome application
+   and create the main window that will run the renderer process
+   from `src/renderer/index.js`.
+
+   You may add your own code to be run in the Electron main-process
+   before or after the call to `Dome.start()`.
+*/
+
+import Dome from 'dome' ;
diff --git a/ivette/src/dome/template/makefile b/ivette/src/dome/template/makefile
new file mode 100644
index 0000000000000000000000000000000000000000..8abb88101324bc5b41a363d3d6ef56fa0e6cf5d3
--- /dev/null
+++ b/ivette/src/dome/template/makefile
@@ -0,0 +1,378 @@
+# --------------------------------------------------------------------------
+# ---  Main Makefile for building Dome applications
+# --------------------------------------------------------------------------
+
+# DOMEMK_ reserved for makefile derived variables
+
+APP?=$(shell basename $(CURDIR))
+COPYRIGHT?=CEA Tech
+
+DOME_MK_NAME=$(shell echo $(APP) | tr [:upper:] [:lower:])
+
+DOME?=dome
+DOME_ARGS?=
+DOME_DEV_ARGS?=
+DOME_APP_ARGS?=
+DOME_EXPORTS?=
+DOME_PLUGINS?=
+DOME_CLI?=./bin/$(DOME_MK_NAME)
+DOME_DOC?=./doc/html
+DOME_API?=
+
+# --------------------------------------------------------------------------
+# ---  Makefile Help
+# --------------------------------------------------------------------------
+
+.PHONY: dome-help
+
+dome-help:
+	@echo
+	@echo "[Dome] Builtin Targets:"
+	@echo "  make dome-help    This help"
+	@echo "  make dome-dev     Run application in development mode"
+	@echo "  make dome-app     Compile application"
+	@echo "  make dome-dist    Package application for production"
+	@echo "  make dome-doc     Generate Dome documentation"
+	@echo "  make dome-api     Generate Application documentation"
+	@echo "  make dome-pkg     Install development packages"
+	@echo "  make dome-templ   Update templates"
+	@echo "  make dome-reboot  Rebuild templates from scratch"
+	@echo "  make dome-clean   Remove dome temporary files"
+	@echo "  make dome-plugins Package plugins for distribution"
+	@echo
+	@echo "[Dome] Development:"
+	@echo "  Dome framework  DOME         = '$(DOME)'"
+	@echo "  Local command   DOME_CLI     = '$(DOME_CLI)'"
+	@echo "  Local arguments DOME_ARGS    = '$(DOME_ARGS)'"
+	@echo "  Extra arg.(dev) DOME_DEV     = '$(DOME_DEV)'"
+	@echo "  Export modules  DOME_EXPORTS = '$(DOME_EXPORTS)'"
+	@echo "  Plugin modules  DOME_PLUGINS = '$(DOME_PLUGINS)'"
+	@echo
+	@echo "[Dome] Documentation:"
+	@echo "  Application     APP          = '$(APP)'"
+	@echo "  Copyright       COPYRIGHT    = '$(COPYRIGHT)'"
+	@echo "  Output dir      DOME_DOC     = '$(DOME_DOC)'"
+	@echo "  Public API      DOME_API     = '$(DOME_API)'"
+	@echo
+
+# --------------------------------------------------------------------------
+# ---  Application Cleaning
+# --------------------------------------------------------------------------
+
+.PHONY: dome-clean
+
+dome-clean:
+	@echo "[Dome] remove temporary files"
+	@rm -fr dist .dome-*.stamp
+
+# --------------------------------------------------------------------------
+# ---  Development Packages Init & Installation
+# --------------------------------------------------------------------------
+
+.PHONY: dome-pkg
+
+include $(DOME)/template/makefile.packages
+
+DOME_MK_BIN=$(CURDIR)/node_modules/.bin
+DOME_MK_PKG= \
+	package.json node_modules \
+	$(DOME)/template/makefile.packages \
+	$(DOME)/template/package.sh
+
+node_modules:
+	mkdir $@
+
+dome-pkg: .dome-pkg.stamp
+.dome-pkg.stamp: $(DOME_MK_PKG)
+	@echo "[Dome] ----------------------------------------------------------------"
+	@echo "[Dome] installing development packages"
+	@echo "[Dome] ----------------------------------------------------------------"
+	@$(DOME)/template/package.sh --dev $(DOME_DEV_PACKAGES)
+	@echo "[Dome] ----------------------------------------------------------------"
+	@echo "[Dome] installing application packages"
+	@echo "[Dome] ----------------------------------------------------------------"
+	@$(DOME)/template/package.sh --app $(DOME_APP_PACKAGES)
+	@echo "[Dome] ----------------------------------------------------------------"
+	@echo "[Dome] building application dependencies"
+	@echo "[Dome] ----------------------------------------------------------------"
+	yarn install
+	yarn run electron-builder install-app-deps
+	@touch $@
+
+package.json:
+	yarn init
+
+# --------------------------------------------------------------------------
+# ---  Dome Synchronization
+# --------------------------------------------------------------------------
+
+ifneq ("$(DOME)",".")
+
+.PHONY: dome-pull dome-push
+
+dome-pull:
+	@echo "[Dome] ----------------------------------------------------------------"
+	@echo "[Dome] Synchronize with updates"
+	@echo "[Dome] ----------------------------------------------------------------"
+	@(cd $(DOME) && ./template/dome-pull.sh)
+
+dome-push:
+	@echo "[Dome] ----------------------------------------------------------------"
+	@echo "[Dome] Publishing Dome updates to feature/app/$(DOME_MK_NAME)"
+	@echo "[Dome] ----------------------------------------------------------------"
+	@(cd $(DOME) && ./template/dome-push.sh feature/app/$(DOME_MK_NAME))
+
+endif
+
+# --------------------------------------------------------------------------
+# ---  Application Templating
+# --------------------------------------------------------------------------
+
+.PHONY: dome-templ dome-reboot
+
+DOME_MK_TEMPLATES= \
+	.gitignore \
+	.babelrc \
+	webpack.main.js \
+	webpack.plugin.js \
+	webpack.renderer.js \
+	electron-webpack.json \
+	src/main/index.js \
+	src/renderer/index.js
+
+dome-templ: $(DOME_MK_TEMPLATES)
+dome-reboot:
+	echo "[Dome] Backup templates"
+	@for f in $(DOME_MK_TEMPLATES) ; do mv -fv $$f $$f.backup ; done
+	$(MAKE) dome-templ dome-pkg
+
+# Templated files
+
+ifeq ("$(DOME)",".")
+.gitignore:
+	@cp -f $(DOME)/template/git-ignore $@
+else
+.gitignore: $(DOME)/template/update.sh $(DOME)/template/git-ignore
+	@((tail -r $(DOME)/template/git-ignore | tail -n +3 | tail -r) || head -n -3 $(DOME)/template/git-ignore)  > $(DOME)/template/git-ignore.local
+	@echo "/$(DOME)" >> $(DOME)/template/git-ignore.local
+	@tail -2 $(DOME)/template/git-ignore >> $(DOME)/template/git-ignore.local
+	@$^.local $@
+	@rm -f $(DOME)/template/git-ignore.local
+endif
+
+.babelrc: $(DOME)/template/update.sh $(DOME)/template/babelrc.json
+	@$^ $@
+
+webpack.main.js: $(DOME)/template/update.sh $(DOME)/template/webpack.main.js
+	@$^ $@
+
+webpack.renderer.js: $(DOME)/template/update.sh $(DOME)/template/webpack.renderer.js
+	@$^ $@
+
+webpack.plugin.js: $(DOME)/template/update.sh $(DOME)/template/webpack.plugin.js
+	@$^ $@
+
+electron-webpack.json: $(DOME)/template/update.sh $(DOME)/template/electron-webpack.json
+	@$^ $@
+
+src/renderer/index.js: $(DOME)/template/update.sh $(DOME)/template/renderer.js
+	@$^ $@
+
+src/main/index.js: $(DOME)/template/update.sh $(DOME)/template/main.js
+	@cp $(DOME)/template/main.js       $(DOME)/template/main.js.local
+	@echo "Dome.setName('$(APP)');" >> $(DOME)/template/main.js.local
+	@echo "Dome.start();"           >> $(DOME)/template/main.js.local
+	@$^.local $@
+	@rm -f $(DOME)/template/main.js.local
+
+# --------------------------------------------------------------------------
+# ---  Custom Entry Points
+# --------------------------------------------------------------------------
+
+ifndef DOME_CUSTOM_ENTRIES
+
+dome-templ: src/renderer/Application.js
+src/renderer/Application.js:
+	@echo "[Dome] creating $@ from template"
+	@mkdir -p src/renderer
+	@cp -f $(DOME)/template/Application.js $@
+
+dome-templ: src/renderer/Preferences.js
+src/renderer/Preferences.js:
+	@echo "[Dome] creating $@ from template"
+	@mkdir -p src/renderer
+	@cp -f $(DOME)/template/Preferences.js $@
+
+endif
+
+# --------------------------------------------------------------------------
+# ---  Exported API for Plugins
+# --------------------------------------------------------------------------
+
+.PHONY: dome-export-dev dome-export-app
+
+DOME_MK_EXPORTS_JS=$(DOME)/src/misc/exports.js
+
+# --------------------------------------------------------------------------
+
+dome-export-dev:
+	@rm -f $(DOME_MK_EXPORTS_JS)
+	@echo "// Static Plugins Loader" > $(DOME_MK_EXPORTS_JS)
+	@for m in $(DOME_EXPORTS) ;\
+	 do \
+		echo "[Dome] installing '$$m'" ;\
+		echo "import '$$m';" \
+		>> $(DOME_MK_EXPORTS_JS) ;\
+	 done
+	@for m in $(DOME_PLUGINS) ;\
+	 do \
+		echo "[Dome] loading plugin $$m" ;\
+		echo "import '@plugins/$$m';" >> $(DOME_MK_EXPORTS_JS) ;\
+	 done
+	@chmod a-w $(DOME_MK_EXPORTS_JS)
+
+# --------------------------------------------------------------------------
+
+dome-export-app:
+	@rm -f $(DOME_MK_EXPORTS_JS)
+	@echo "// Static Loader (Generated by Dome)" > $(DOME_MK_EXPORTS_JS)
+	@echo "import { register } from 'dome/misc/plugins' ;" >> $(DOME_MK_EXPORTS_JS) ;
+	@echo "[Dome] export 'dome'" ;
+	@$(DOME)/template/export.sh $(DOME) >> $(DOME_MK_EXPORTS_JS)
+	@for m in $(DOME_EXPORTS) ;\
+	 do \
+		echo "[Dome] export '$$m'" ;\
+		echo "{ let m = require('$$m'); register('$$m',m); }" \
+		>> $(DOME_MK_EXPORTS_JS) ;\
+	 done
+	@for m in $(DOME_PLUGINS) ;\
+	 do \
+		echo "[Dome] install '@plugins/$$m'" ;\
+		echo "{ let m = require('@plugins/$$m'); register('@plugins/$$m',m); }" \
+		>> $(DOME_MK_EXPORTS_JS) ;\
+	 done
+	@chmod a-w $(DOME_MK_EXPORTS_JS)
+
+# --------------------------------------------------------------------------
+# ---  Packaging Plugins
+# --------------------------------------------------------------------------
+
+.PHONY: dome-plugins dome-plugin-%
+
+DOME_MK_PLUGINS_TARGET=$(addprefix dome-plugin-, $(DOME_PLUGINS))
+DOME_MK_PLUGINS_SOURCE=$(addprefix src/plugins/, $(DOME_PLUGINS))
+DOME_MK_PLUGINS_CONFIG=$(addsuffix /package.json, $(DOME_MK_PLUGINS_SOURCE))
+
+dome-plugins: $(DOME_MK_PLUGINS_CONFIG) $(DOME_MK_PLUGINS_TARGET)
+
+dome-plugin-%: src/plugins/%/package.json webpack.plugin.js
+	@echo "[Dome] building plugin '$*'"
+	@rm -fr dist/plugins/$*
+	@mkdir -p dist/plugins/$*
+	@node $(DOME)/template/packplugin.js '$*'
+	DOME=$(DOME) DOME_PLUGIN='$*' yarn run webpack -p --config webpack.plugin.js
+	@echo "[Dome] plugin packaged in ./dist/plugins/$*"
+
+src/plugins/%/package.json:
+	@echo "[Dome] creating package.json for plugin '$*'"
+	@echo '{' > $@
+	@echo '  "name": "$*",' >> $@
+	@echo '  "version": "0.1",' >> $@
+	@echo '  "license": "UNLICENSED"' >> $@
+	@echo '}' >> $@
+
+# --------------------------------------------------------------------------
+# ---  Application Development
+# --------------------------------------------------------------------------
+
+DOME_MK_CLIDIR=$(dir $(DOME_CLI))
+
+.PHONY: dome-dev
+
+dome-dev: dome-pkg dome-templ dome-export-dev
+	@echo "[Dome] linking $(DOME_CLI)"
+	@mkdir -p $(DOME_MK_CLIDIR)
+	@rm -f $(DOME_CLI)
+	@echo "#!/bin/sh" >> $(DOME_CLI)
+	@echo "export DOME='$(DOME)'" >> $(DOME_CLI)
+	@echo "export DOME_ENV='dev'" >> $(DOME_CLI)
+	@echo 'exec $(DOME_MK_BIN)/electron-webpack dev $(DOME_ARGS) $(DOME_DEV) "$$@"' >> $(DOME_CLI)
+	@chmod 555 $(DOME_CLI)
+	@echo "[Dome] running application (development)"
+	@$(DOME_CLI)
+
+# --------------------------------------------------------------------------
+# ---  Application Building
+# --------------------------------------------------------------------------
+
+.PHONY: dome-app
+
+dome-app: dome-pkg dome-templ dome-export-app
+	@echo "[Dome] compiling application (production)"
+	DOME=$(DOME) DOME_ENV='app' yarn run electron-webpack app
+	@echo "[Dome] linking $(DOME_CLI)"
+	@mkdir -p $(DOME_MK_CLIDIR)
+	@rm -f $(DOME_CLI)
+	@echo "#!/bin/sh" >> $(DOME_CLI)
+	@echo "export DOME_LOCAL=true" >> $(DOME_CLI)
+	@echo 'exec $(DOME_MK_BIN)/electron $(CURDIR)/dist/main/main.js $(DOME_ARGS) "$$@"' >> $(DOME_CLI)
+	@chmod 555 $(DOME_CLI)
+
+# --------------------------------------------------------------------------
+# ---  Application Packaging
+# --------------------------------------------------------------------------
+
+.PHONY: dome-dist
+
+dome-dist: dome-app
+	@echo "[Dome] packaging application (production)"
+	DOME=$(DOME) yarn run electron-builder --dir -c.compression=store -c.mac.identity=null
+	@echo "[Dome] application packaged in ./dist"
+
+# --------------------------------------------------------------------------
+# --- Icon gallery
+# --------------------------------------------------------------------------
+
+DOME_MK_ICON_MAKE=$(DOME)/doc/icons.js
+DOME_MK_ICON_DATA=$(DOME)/src/renderer/controls/icons.json
+DOME_MK_ICON_HTML=$(DOME)/doc/template/gallery-icons.html
+
+$(DOME_MK_ICON_HTML): $(DOME_MK_ICON_MAKE) $(DOME_MK_ICON_DATA)
+	@echo "[Dome] generating icons gallery"
+	@node $(DOME_MK_ICON_MAKE) $(DOME_MK_ICON_DATA) > $(DOME_MK_ICON_HTML)
+
+# --------------------------------------------------------------------------
+# ---  Dome Documentation
+# --------------------------------------------------------------------------
+
+DOME_MK_DOC_SRC=$(DOME_API) $(addprefix src/plugins/, $(DOME_PLUGINS))
+DOME_MK_DOC_API=$(addprefix $(CURDIR)/, $(DOME_MK_DOC_SRC))
+DOME_MK_DOC_OUT=$(addprefix $(CURDIR)/, $(DOME_DOC))
+
+dome-doc: $(DOME_MK_BIN)/jsdoc $(DOME_MK_ICON_HTML)
+	@echo "[Dome] $(APP) documentation"
+	@mkdir -p $(DOME_DOC)
+	@rm -f $(DOME_DOC)/*.html
+	@rm -f $(DOME_DOC)/*.png
+	@rm -f $(DOME_DOC)/*.css
+	@rm -fr $(DOME_DOC)/fonts
+	@rm -fr $(DOME_DOC)/styles
+	@(cd $(DOME) && DOME="." DOME_PWD=$(CURDIR) APP=$(APP) COPYRIGHT="$(COPYRIGHT)" \
+	  $(DOME_MK_BIN)/jsdoc -c ./doc/config.json $(DOME_MK_DOC_API) \
+	  -d $(DOME_MK_DOC_OUT))
+	@cp -f $(DOME)/doc/gallery.css $(DOME_DOC)/styles/
+	@cp -f $(DOME)/doc/tutorials/*.png $(DOME_DOC)/
+	@cat \
+		$(DOME)/doc/template/gallery-head.html \
+		$(DOME)/doc/template/gallery-icons.html \
+		$(DOME)/doc/template/gallery-tail.html \
+		> $(DOME_DOC)/tutorial-icons.html
+	@echo "[Dome]   $(DOME_DOC)/index.html"
+
+# --- Doc binaries ---------------------------------------------------------
+
+$(DOME_MK_BIN)/jsdoc:
+	@echo "[Dome] installing jsdoc"
+	@yarn add -D jsdoc
+
+# --------------------------------------------------------------------------
diff --git a/ivette/src/dome/template/makefile.app.packages b/ivette/src/dome/template/makefile.app.packages
new file mode 100644
index 0000000000000000000000000000000000000000..58aa6a3ad516168d48315864bfcf57376187f2b0
--- /dev/null
+++ b/ivette/src/dome/template/makefile.app.packages
@@ -0,0 +1,15 @@
+# --------------------------------------------------------------------------
+# ---  Package Template Definitions
+# --------------------------------------------------------------------------
+
+# Additional development yarn package specifications:
+
+DOME_PKG_DEV=
+
+# --------------------------------------------------------------------------
+
+# Additional production yarn package specifications:
+
+DOME_PKG_APP=
+
+# --------------------------------------------------------------------------
diff --git a/ivette/src/dome/template/makefile.packages b/ivette/src/dome/template/makefile.packages
new file mode 100644
index 0000000000000000000000000000000000000000..05b8cb03aece9be54813a53d3952d9e804943839
--- /dev/null
+++ b/ivette/src/dome/template/makefile.packages
@@ -0,0 +1,30 @@
+# --------------------------------------------------------------------------
+# ---  Packages Definitions for Dome environment                         ---
+# --------------------------------------------------------------------------
+
+DOME_DEV_PACKAGES= \
+	@babel/core@^7 \
+	@babel/preset-env \
+	@babel/preset-react \
+	@babel/plugin-proposal-object-rest-spread \
+	electron@^7 \
+	electron-builder \
+	electron-webpack \
+	electron-devtools-installer \
+	webpack \
+	babel-loader \
+	css-loader \
+	react-hot-loader \
+	@hot-loader/react-dom \
+	@types/react
+
+DOME_APP_PACKAGES= \
+	react@^16.8 \
+	react-dom \
+	source-map-support \
+	lodash \
+	react-virtualized \
+	react-draggable \
+	codemirror
+
+# --------------------------------------------------------------------------
diff --git a/ivette/src/dome/template/package.sh b/ivette/src/dome/template/package.sh
new file mode 100755
index 0000000000000000000000000000000000000000..58e4428ffb8b421a8c3c99266057dbb0962bc1fe
--- /dev/null
+++ b/ivette/src/dome/template/package.sh
@@ -0,0 +1,60 @@
+#!/bin/sh
+
+# --------------------------------------------------------------------------
+# ---  Compute mode
+# --------------------------------------------------------------------------
+
+OPT=""
+LOG=""
+
+case $1 in
+    "--dev"|"-D")
+        OPT="--dev"
+        LOG=".dome-pkg-dev"
+        shift
+        ;;
+    "--app"|"-A")
+        OPT=""
+        LOG=".dome-pkg-app"
+        shift
+        ;;
+    *)
+        echo "Require --dev or --app option"
+        exit 1
+        ;;
+esac
+
+# --------------------------------------------------------------------------
+# ---  Check for Updates
+# --------------------------------------------------------------------------
+
+rm -f $LOG.tmp
+echo $* > $LOG.tmp
+
+if [ -f $LOG.lock ]
+then
+    diff $LOG.tmp $LOG.lock
+    if [ $? -eq 0 ]
+    then
+        rm -f $LOG.tmp
+        echo "Packages are up-to-date."
+        exit 0
+    fi
+fi
+
+# --------------------------------------------------------------------------
+# ---  Updates Packages
+# --------------------------------------------------------------------------
+
+echo "yarn add $OPT $*"
+yarn add $OPT $*
+
+if [ $? -eq 0 ]
+then
+    mv -f $LOG.tmp $LOG.lock
+else
+    echo "Package installation failed."
+    exit 1
+fi
+
+# --------------------------------------------------------------------------
diff --git a/ivette/src/dome/template/packplugin.js b/ivette/src/dome/template/packplugin.js
new file mode 100644
index 0000000000000000000000000000000000000000..d0a9f265c2826d99fc11b7fed2a32ba4a574fb00
--- /dev/null
+++ b/ivette/src/dome/template/packplugin.js
@@ -0,0 +1,31 @@
+#!/bin/node
+
+const fs = require('fs');
+const name = process.argv[2];
+const INFOS = [
+  'name',
+  'version',
+  'description',
+  'homepage',
+  'bugs',
+  'keywords',
+  'author',
+  'contributors',
+  'repository',
+  'license'
+];
+
+const dst = {
+  name,
+  version: '0.1',
+  license: 'UNLICENSED',
+  main: 'bundle.js'
+};
+
+let src = './src/plugins/' + name + '/package.json' ;
+let tgt = './dist/plugins/' + name + '/package.json' ;
+let pkg = JSON.parse( fs.readFileSync( src , 'UTF-8' ) );
+INFOS.forEach((fd) => { let d = pkg[fd] ; if (!d) dst[fd] = d });
+fs.writeFileSync( tgt , JSON.stringify(dst) , 'UTF-8' );
+
+// End.
diff --git a/ivette/src/dome/template/renderer.js b/ivette/src/dome/template/renderer.js
new file mode 100644
index 0000000000000000000000000000000000000000..1544ac9e11373febc48161e9672b71456c6819f6
--- /dev/null
+++ b/ivette/src/dome/template/renderer.js
@@ -0,0 +1,41 @@
+// --------------------------------------------------------------------------
+// --- Electron renderer-process entry-point for Dome.
+// --------------------------------------------------------------------------
+
+/*
+   Template of ./src/renderer/index.js
+   Imported from $(DOME)/template/renderer.js
+
+   You can modify this template and add your own code before or
+   after rendering the main components.
+
+   However, please note that React hot-reloading is fragile.
+   Component that are not imported by <Application/> or <Preferences/>
+   may suffer from well known limitations of hot-reloading and
+   may lead to state losses or complete application reloads.
+*/
+
+// Enable live-editing in React:
+import 'react-hot-loader/patch' ;
+import React from 'react' ;
+import Dome from 'dome' ;
+
+// You can change the name of the main components,
+// provided you define the makefile variable
+// `DOME_CUSTOM_ENTRIES` to prevent files 'Application.js' and 'Preferences.js'
+// from being generated by the template makefile.
+
+import Application from './Application' ;
+import Preferences from './Preferences' ;
+
+// Define the application main components for each window:
+Dome.setApplicationWindow(Application);
+Dome.setPreferencesWindow(Preferences);
+
+// Mark the main application reloadable and enable live updates:
+module.hot && Dome.isApplicationWindow() &&
+  module.hot.accept('./Application',() => Dome.setApplicationWindow(Application));
+module.hot && Dome.isPreferencesWindow() &&
+  module.hot.accept('./Preferences',() => Dome.setPreferencesWindow(Preferences));
+
+// --------------------------------------------------------------------------
diff --git a/ivette/src/dome/template/update.sh b/ivette/src/dome/template/update.sh
new file mode 100755
index 0000000000000000000000000000000000000000..56d22295ef24fa4f37c19a100797c6005561921d
--- /dev/null
+++ b/ivette/src/dome/template/update.sh
@@ -0,0 +1,75 @@
+#!/bin/sh
+
+# --------------------------------------------------------------------------
+# ---  Update Templated File (merge with user modifications)
+# --------------------------------------------------------------------------
+
+USER=$2
+TEMPLATE=$1
+BACK=$(dirname $USER)/.dome-$(basename $USER).back
+ORIGIN=$(dirname $TEMPLATE)/.dome-$(basename $TEMPLATE).orig
+DIFF=$(dirname $USER)/.dome-diff
+
+if [ ! -f $TEMPLATE ]
+then
+    echo "[Dome] missing template ('$TEMPLATE')"
+    exit 2
+fi
+
+if [ -f $USER ]
+then
+    if [ ! -f $ORIGIN ]
+    then
+        echo "[Dome] lost original template for '$USER' (search for diffs)"
+        cp -f $TEMPLATE $ORIGIN
+    fi
+    cp -f $USER $BACK
+    rm -f $USER
+    diff3 -m -L YOURS $BACK -L ORIGIN $ORIGIN -L TEMPLATE $TEMPLATE > $USER
+    MERGED=$?
+    cp -f $TEMPLATE $ORIGIN
+    if [ $MERGED -eq 0 ]
+    then
+        diff $USER $BACK > $DIFF
+        if [ $? -eq 0 ]
+        then
+            echo "[Dome] updated '$USER' with new template (no modifications)"
+        else
+            echo "[Dome] merged '$USER' with new template (backup saved)."
+        fi
+        rm -f $DIFF
+        exit 0
+    else
+        while true
+        do
+              echo "[Dome] conflicting '$USER' with new template."
+              echo " (d) show diffs   (e) edit conflict"
+              echo " (k) keep current (t) use template"
+              read -p "Your choice ? [t] " ACTION
+              case $ACTION in
+                  d)
+                      echo "Differences ( > yours, < template ):"
+                      diff $TEMPLATE $BACK
+                      ;;
+                  e)
+                      echo "Previous content saved in '$BACK'."
+                      echo "Run 'make dome-templ' after '$USER' has been fixed."
+                      exit 1
+                      ;;
+                  k)
+                      exit 0
+                      ;;
+                  t|"")
+                      cp -f $TEMPLATE $USER
+                      exit 0
+                      ;;
+              esac
+        done
+    fi
+else
+    echo "[Dome] creating '$USER' from template"
+    cp -f $TEMPLATE $USER
+    cp -f $TEMPLATE $ORIGIN
+fi
+
+# --------------------------------------------------------------------------
diff --git a/ivette/src/dome/template/webpack.main.js b/ivette/src/dome/template/webpack.main.js
new file mode 100644
index 0000000000000000000000000000000000000000..608d29372314dcb2e5cd7406dac737c35371daf6
--- /dev/null
+++ b/ivette/src/dome/template/webpack.main.js
@@ -0,0 +1,38 @@
+// --------------------------------------------------------------------------
+// --- Webpack extension for electron main-process
+// --------------------------------------------------------------------------
+
+/*
+   Template of ./webpack.main.js from $(DOME)/template/webpack.main.js
+
+   This webpack definitions will be merged into electron-webpack
+   ones thanks to electron-webpack.json configuration file.
+
+   You may extend it with your own additions.
+*/
+
+const path = require('path');
+const DOME = process.env.DOME || path.resolve(__dirname , 'dome');
+const ENV = process.env.DOME_ENV ;
+
+// Do not use electron-devtools-installer in production mode
+function domeDevtools() {
+  switch(ENV) {
+  case 'dev':
+    return 'electron-devtools-installer';
+  default:
+    return path.resolve( DOME , 'src/misc/devtools.js' );
+  }
+}
+
+module.exports = {
+  resolve: {
+    alias: {
+      'dome$':         path.resolve( DOME , 'src/main/dome.js' ),
+      'dome/system$':  path.resolve( DOME , 'src/misc/system.js' ),
+      'dome/devtools': domeDevtools()
+    }
+  }
+} ;
+
+// --------------------------------------------------------------------------
diff --git a/ivette/src/dome/template/webpack.plugin.js b/ivette/src/dome/template/webpack.plugin.js
new file mode 100644
index 0000000000000000000000000000000000000000..f60bdbf7ab8cda15ad9dcba2c747a5a3d82e57ed
--- /dev/null
+++ b/ivette/src/dome/template/webpack.plugin.js
@@ -0,0 +1,41 @@
+// --------------------------------------------------------------------------
+// --- Webpack configuration for packing plugins
+// --------------------------------------------------------------------------
+
+/*
+   Template of ./webpack.plugin.js from $(DOME)/template/webpack.plugin.js
+
+   This webpack definitions will be used to make plugin bundles.
+
+   You may extend it with your own additions.
+*/
+
+const path = require('path');
+const DOME = process.env.DOME || path.resolve( __dirname , 'dome' );
+const PLUGIN = process.env.DOME_PLUGIN ;
+const ENTRY = path.resolve( __dirname , 'src/plugins' , PLUGIN );
+const DIST = path.resolve( __dirname , 'dist/plugins' , PLUGIN );
+
+module.exports = {
+  entry: ENTRY,
+  output: {
+    path: DIST,
+    filename: 'bundle.js',
+    libraryTarget: 'commonjs2'
+  },
+  module: {
+    rules: [
+      { test: /\.css$/, use: [ 'style-loader', 'css-loader' ] },
+      { test: /\.js$/, use: [ 'babel-loader' ], exclude: /(node_modules)/ }
+    ]
+  },
+  externals: [
+    'lodash',
+    'react',
+    /^dome\/.+$/,
+    /^@plugins\/.+$/,
+    /^@\/.+$/
+  ]
+};
+
+// --------------------------------------------------------------------------
diff --git a/ivette/src/dome/template/webpack.renderer.js b/ivette/src/dome/template/webpack.renderer.js
new file mode 100644
index 0000000000000000000000000000000000000000..5cba35d887a82293a2c5ee5c482eda406729fdd4
--- /dev/null
+++ b/ivette/src/dome/template/webpack.renderer.js
@@ -0,0 +1,35 @@
+// --------------------------------------------------------------------------
+// --- Webpack extension for electron main-process
+// --------------------------------------------------------------------------
+
+/*
+   Template from $(DOME)/template/webpack.main.js
+
+   This webpack definitions will be merged into electron-webpack
+   ones thanks to electron-webpack.json configuration file.
+
+   You may extend it with your own additions.
+*/
+
+const path = require('path');
+const DOME = process.env.DOME || path.resolve( __dirname , 'dome' );
+
+module.exports = {
+  module: {
+    rules: [
+      { test: /\.css$/, use: [ 'css-loader' ] }
+    ]
+  },
+  resolve: {
+    alias: {
+      '@plugins':     path.resolve( __dirname , 'src/plugins' ),
+      'dome/misc':    path.resolve( DOME , 'src/misc' ),
+      'dome/system':  path.resolve( DOME , 'src/misc/system.js' ),
+      'dome$':        path.resolve( DOME , 'src/renderer/dome.js' ),
+      'dome':         path.resolve( DOME , 'src/renderer' ),
+      'react-dom':    '@hot-loader/react-dom'
+    }
+  }
+} ;
+
+// --------------------------------------------------------------------------
diff --git a/ivette/src/frama-c/labviews.css b/ivette/src/frama-c/labviews.css
new file mode 100644
index 0000000000000000000000000000000000000000..1b6893fd996b7985af7b39b50199026d0f1ef621
--- /dev/null
+++ b/ivette/src/frama-c/labviews.css
@@ -0,0 +1,53 @@
+/* -------------------------------------------------------------------------- */
+/* --- Styling LabViews                                                   --- */
+/* -------------------------------------------------------------------------- */
+
+.dome-window-active .labview-icon {
+    fill: #7d7d7d ;
+}
+
+.labview-field {
+    position: relative ;
+    left: -8px ;
+}
+
+.labview-stock:hover {
+    background: #ccc ;
+}
+
+.labview-stock:hover * {
+    cursor: move ;
+}
+
+.labview-stock.dome-dragging
+{
+    border-radius: 4px ;
+    background: #ccc ;
+    border: thin solid black ;
+}
+
+.labview-content
+{
+    background: #e6e6e6 ;
+}
+
+.labview-titlebar
+{
+    background: #ccc ;
+}
+
+.labview-titlebar .dome-xLabel
+{
+    padding-top: 3px ;
+}
+
+.labview-handle
+{
+    flex: 1 1 auto ;
+    cursor: move ;
+}
+
+.labview-close:hover
+{
+    fill: red ;
+}
diff --git a/ivette/src/frama-c/labviews.js b/ivette/src/frama-c/labviews.js
new file mode 100644
index 0000000000000000000000000000000000000000..1e8e5bf830c54ba801b9e780ad666fbf852e5f25
--- /dev/null
+++ b/ivette/src/frama-c/labviews.js
@@ -0,0 +1,609 @@
+// --------------------------------------------------------------------------
+// ---  Lab View Component
+// --------------------------------------------------------------------------
+
+/** @module frama-c/labviews */
+
+import _ from 'lodash' ;
+import React from 'react' ;
+import Dome from 'dome' ;
+import { Catch } from 'dome/errors' ;
+import { DnD, DragSource } from 'dome/dnd' ;
+import { SideBar, Section, Item } from 'dome/layout/sidebars' ;
+import { Splitter } from 'dome/layout/splitters' ;
+import Grids, { GridLayout, GridItem } from 'dome/layout/grids' ;
+import { Hbox, Hfill, Vfill, Filler } from 'dome/layout/boxes' ;
+import { IconButton, Field } from 'dome/controls/buttons' ;
+import { Label } from 'dome/controls/labels' ;
+import { Icon } from 'dome/controls/icons' ;
+import { Item as ItemToRender,
+         Render as RenderItem } from 'dome/layout/dispatch' ;
+import './labviews.css' ;
+
+// --------------------------------------------------------------------------
+// --- Library
+// --------------------------------------------------------------------------
+
+class Library {
+
+  constructor() {
+    this.modified = false ;
+    this.virtual = {} ;
+    this.collection = {} ;
+    this.items = [] ;
+  }
+
+  commit() {
+    if (!_.isEqual(this.collection,this.virtual)) {
+      this.collection = Object.assign({},this.virtual) ;
+      this.items = _.sortBy(this.collection , [ 'order', 'id' ]);
+      this.modified = false ;
+      Dome.emit('labview.library');
+    }
+  }
+
+  useItem(id,gcontext,path,props) {
+    if (!this.modified) {
+      this.modified = true ;
+      setImmediate(() => this.commit());
+    }
+    if (!id) return undefined;
+    let order = props.rank === undefined
+        ? path
+        : path.slice(0,-1).concat([props.rank]);
+    let group = props.group || gcontext ;
+    let collection = this.virtual ;
+    collection[id] = Object.assign( { id, order, group }, props );
+    return () => delete collection[id];
+  }
+
+}
+
+// --------------------------------------------------------------------------
+// --- Library Components
+// --------------------------------------------------------------------------
+
+const isItemId = (fd,id) => id.startsWith(fd) && id[fd.length] === '.' ;
+const getItemId = (fd,id) => isItemId(fd,id) ? id.substring(fd.length+1) : undefined ;
+const getItems = (items,fd) => items.filter((item) => isItemId(fd,item.id));
+
+const LibraryManager = React.createContext();
+
+const useLibraryItem = (fd,{id, ...props}) => {
+  let context = React.useContext(LibraryManager);
+  React.useEffect(() => {
+    if (context) {
+      let { group, order, library } = context ;
+      let itemId = fd + '.' + id ;
+      return library.useItem(itemId,group,order,props);
+    } else
+      return undefined ;
+  });
+  return context ;
+};
+
+
+const Rankify = ({ library, group, order, children }) => {
+  let rank = 0;
+  let rankify = (elt) => {
+    let context = { group, order:order.concat([rank++]), library };
+    return (
+      <LibraryManager.Provider value={context}>
+        {elt}
+      </LibraryManager.Provider>
+    );
+  };
+  return (
+    <React.Fragment>
+      {React.Children.map( children, rankify )}
+    </React.Fragment>
+  );
+};
+
+const HIDDEN = { display: 'none' };
+
+const UseLibrary = ( { library, children } ) => (
+  <div style={HIDDEN}>
+    <Rankify library={library} order={[]}>
+      {children}
+    </Rankify>
+  </div>
+);
+
+/**
+   @class
+   @summary Ordered collection of LabView Components.
+   @description
+   Renderers its children in the specified order.
+   Otherwized, elements are ordered by `rank` and `id`.
+ */
+export const Fragment = ({ group, children }) => {
+  let context = React.useContext(LibraryManager);
+  if (!context)
+    return null;
+  else {
+    return (
+      <Rankify
+        group={group || context.group}
+        order={context.order}
+        library={context.library} >
+        {children}
+      </Rankify>
+    );
+  }
+};
+
+/**
+   @class
+   @summary Group of LabView Components.
+   @property {string} id - group identifier
+   @property {string} label - displayed name
+   @property {string} [title] - description tooltip
+   @property {React.Children} [children] - group content
+   @description
+   Defines a group of components. The components rendered
+   _inside_ its content are implicitely affected to this group,
+   unless specified. The group content are also rendered
+   in their specified order. For nested collections of components,
+   use `<Fragment/>` instead of `<React.Fragment/>` to specify order.
+ */
+export const Group = ({ children,...props}) => {
+  let context = useLibraryItem('groups',props);
+  return (
+    <Rankify
+      group={props.id}
+      order={context.order}
+      library={context.library} >
+      {children}
+    </Rankify>
+  );
+};
+
+// --------------------------------------------------------------------------
+// --- Views
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @summary Layout of LabView Components.
+   @property {string} id - view identifier
+   @property {string} label - displayed name
+   @property {string} [title] - description tooltip
+   @property {boolean} [defaultView] - use this view by default
+   @property {GridContent} children - grid content of the view
+   @description
+   Defines a predefined layout of components. The view is organized
+   into a GridContent, which must _only_ consists of:
+   - `<GridHbox>…</GridHbox>` an horizontal grid of `GridContent` elements;
+   - `<GridVbox>…</GridVbox>` a vertical grid of `GridContent` elements;
+   - `<GridItem id=…>` a single component.
+
+   These grid content components must be imported from the `dome/layout/grids` module:
+   ```
+   import { GridItem, GridHbox, GridVbox } from 'dome/layout/grids';
+   ```
+ */
+export const View = (props) => {
+  useLibraryItem('views',props);
+  return null;
+};
+
+// --------------------------------------------------------------------------
+// --- Components
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @summary LabView Component.
+   @property {string} id - component identifier
+   @property {string} label - displayed name
+   @property {number} [rank] - ordering index
+   @property {string} [group] - attachment group
+   @property {string} [title] - description tooltip
+   @property {React.Children} children - component rendering elements
+   @description
+   Defines a component and its content when incorporated inside a view.
+   Unless specified, the component will be implicitely attached
+   to the current enclosing group. The `rank` property can be used
+   for adjusting component ordering (see also `<Fragment/>` and `<Group/>`).
+ */
+export const Component = (props) => {
+  useLibraryItem('components',props);
+  return null;
+};
+
+const TitleContext = React.createContext();
+
+/**
+   @class
+   @summary LabView Component's title bar.
+   @property {string} [icon] - displayed icon
+   @property {string} [label] - displayed name
+   @property {string} [title] - description tooltip
+   @property {React.Children} children - additional components to render
+   @description
+   Defines an alternative component title bar.
+   If specified, the icon, label and title properties are rendered in an `<Label/>` component.
+   By default, the component title bar is labelled according to the component properties.
+ */
+export const TitleBar = ({icon,label,title,children}) => {
+  let context = React.useContext(TitleContext);
+  return (
+    <ItemToRender id={'labview.title.'+context.id}>
+      <Label className='labview-handle' icon={icon}
+             label={label || context.label}
+             title={title || context.title}/>
+      {children}
+    </ItemToRender>
+  );
+};
+
+// --------------------------------------------------------------------------
+// --- Grid Item
+// --------------------------------------------------------------------------
+
+const GRIDITEM = {
+  className: 'dome-container dome-xBoxes-vbox dome-xBoxes-box',
+  handle: '.labview-handle',
+  resize: 'both',
+  fill: 'none',
+  shrink: 'none',
+  minWidth: 40,
+  minHeight: 40,
+  width: 120,
+  height: 120
+};
+
+const GRIDITEM_PLAIN = { fill: 'both' };
+const GRIDITEM_HPANE = { fill: 'horizontal' };
+const GRIDITEM_VPANE = { fill: 'vertical' };
+
+const makeGridItem = (customize,onClose) => comp => {
+  let { id:libId, label, title, layout='PLAIN', children } = comp ;
+  let id = getItemId('components',libId);
+  let properties = Object.assign( {}, GRIDITEM );
+  switch( layout ) {
+  case 'PLAIN':
+    Object.assign( properties, GRIDITEM_PLAIN );
+    break;
+  case 'HPANE':
+    Object.assign( properties, GRIDITEM_HPANE );
+    break;
+  case 'VPANE':
+    Object.assign( properties, GRIDITEM_VPANE );
+    break;
+  default:
+    console.warn(`[labviews] unexpected layout for ${id} component`,layout);
+    break;
+  }
+  for (let fd in properties) {
+    let prop = comp[fd] ;
+    if (prop) properties[fd] = prop ;
+  }
+  let CLOSING ;
+  if (customize) CLOSING = (
+    <IconButton
+      className='labview-close'
+      offset={-1} icon='CROSS'
+      onClick={() => onClose(id)}/>
+  );
+  return (
+    <GridItem id={id} {...properties}>
+      <Vfill className='labview-content'>
+        <Hbox className='labview-titlebar'>
+          <Hfill>
+            <Catch title={id}>
+              <RenderItem id={ 'labview.title.' + id }>
+                <Label className='labview-handle'
+                       label={label} title={title} />
+              </RenderItem>
+            </Catch>
+          </Hfill>
+          { CLOSING }
+        </Hbox>
+        <TitleContext.Provider value={{id,label,title}}>
+          <Catch title={id}>{children}</Catch>
+        </TitleContext.Provider>
+      </Vfill>
+    </GridItem>
+  );
+};
+
+// --------------------------------------------------------------------------
+// --- Customization Views
+// --------------------------------------------------------------------------
+
+function CustomViews( { settings, shape, setShape, views:libViews } )
+{
+  const [ local, setLocal ] = Dome.useState( settings, {} );
+  const [ customs, setCustoms ] = Dome.useGlobalSetting( settings, {} );
+  const [ edited, setEdited ] = React.useState();
+  const triggerDefault = React.useRef();
+  const { current , shapes={} } = local ;
+
+  let theViews = {};
+
+  _.forEach( libViews, (view) => {
+    const { id: origin, order, label='(Stock View)', title, defaultView } = view ;
+    const id = 'builtin.' + origin ;
+    theViews[ id ] = { id, order, label, title, builtin: true, defaultView, origin };
+  });
+
+  _.forEach( customs, (view) => {
+    const { id, order, label='(Custom View)', title, origin } = view ;
+    if (id && !theViews[id])
+      theViews[id] = { id, order, label, title, builtin: false, origin };
+  });
+
+  const getStock = (origin) => (
+    (origin
+     && _.find( libViews, (v) => v.id === origin ))
+      || _.find( libViews, (v) => v.defaultView )
+      || libViews[0]
+  );
+
+  const getDefaultShape = ( view ) => {
+    let stock = getStock( view && view.origin );
+    return stock && Grids.makeChildrenShape(stock.children);
+  };
+
+  const SELECT = (id) => {
+    if (id && current !== id) {
+      if (current) shapes[current] = shape ;
+      setLocal({ current: id, shapes });
+      setShape( shapes[id] || getDefaultShape(theViews[id]) );
+    }
+  };
+
+  const POPUP = (id) => {
+    let view = theViews[id];
+    if (!view) return;
+    let isCurrent = current === id ;
+    let isCustom = !view.builtin ;
+
+    const DEFAULT = () => {
+      shapes[id] = undefined;
+      setLocal({ current:id, shapes });
+      setShape( getDefaultShape(view) );
+    };
+
+    const RENAME = () => setEdited(id);
+
+    const DUPLICATE = () => {
+      let base = 'custom.' + view.origin ;
+      let stock = getStock(view.origin);
+      let k = 1, newId = base ;
+      while (theViews[newId]) newId = base + '~' + (++k) ;
+      let newOrder = view.order ;
+      if (newOrder && newOrder.concat) newOrder = newOrder.concat([k]);
+      let newLabel = 'Custom ' + stock.label ;
+      if (k>1) newLabel += '~' + k ;
+      customs[newId] = {
+        id: newId,
+        label: newLabel,
+        order: newOrder,
+        title:'Derived from ' + stock.label,
+        origin: view.origin,
+        builtin: false
+      };
+      setCustoms(customs);
+      if (current) shapes[current] = shape;
+      setLocal({ current:newId, shapes });
+      setEdited(newId);
+    };
+
+    const REMOVE = () => {
+      delete customs[id];
+      delete shapes[id];
+      setCustoms(customs);
+      let newCurrent = current === id ? undefined : current ;
+      setLocal({ current: newCurrent, shapes });
+    };
+
+    Dome.popupMenu([
+      { label: "Rename View", display:(!edited && isCustom), onClick: RENAME },
+      { label: "Restore Default", display:isCurrent, onClick: DEFAULT },
+      { label: "Duplicate View",onClick: DUPLICATE },
+      'separator',
+      { label: "Remove View", display:isCustom, onClick: REMOVE }
+    ]);
+  };
+
+  const makeViewItem = ({ id, label, title, builtin, origin }) => {
+    if (edited === id) {
+      const RENAMED = (newLabel) => {
+        if (newLabel) {
+          let custom = customs[id] ;
+          if (custom) custom.label = newLabel ;
+          setCustoms( customs );
+        }
+        setEdited( undefined );
+      };
+      const FIELD=(
+        <Field className='labview-field'
+               placeholder='View Name'
+               autoFocus value={label}
+               onEnter={RENAMED} />
+      );
+      return (
+        <Item key={id} id={id} icon='DISPLAY' title={title} label={FIELD}/>
+      );
+    } else {
+      const FLAGS=[] ;
+      if (builtin) FLAGS.push('LOCK');
+      return (
+        <Item
+          key={id} id={id}
+          icon='DISPLAY'
+          label={label}
+          title={title}
+          selected={id && current === id}
+          onSelection={SELECT}
+          onContextMenu={POPUP}
+          >
+          {FLAGS.map((icn) => (
+            <Icon key={icn} className='labview-icon' size={9} offset={1} id={icn} />
+          ))}
+        </Item>
+      );
+    }
+  };
+
+  if (!current && !triggerDefault.current) {
+    const theDefault = _.find( theViews, (item) => item.defaultView );
+    triggerDefault.current = theDefault ;
+    if (theDefault) setTimeout(() => {
+      SELECT(theDefault.id);
+    });
+  }
+
+  return (
+    <Section label='Views'>
+      {_.sortBy(theViews,['order','id']).map(makeViewItem)}
+    </Section>
+  );
+}
+
+// --------------------------------------------------------------------------
+// --- Customization Components
+// --------------------------------------------------------------------------
+
+const DRAGOVERLAY = { className: 'labview-stock' };
+
+function CustomGroup( { dnd, shape, setDragging, id, title, label, components } )
+{
+
+  const makeComponent = ({ id, label, title }) => {
+    let itemId = getItemId('components',id);
+    let disabled = Grids.getShapeItem(shape,itemId) !== undefined ;
+    return (
+      <DragSource key={id} dnd={dnd}
+                  disabled={ disabled }
+                  overlay={ disabled ? undefined : DRAGOVERLAY }
+                  onStart={() => setDragging(itemId)} >
+        <Item icon='COMPONENT' disabled={ disabled } label={label} title={title} />
+      </DragSource>
+    );
+  };
+
+  return (
+    <Section id={id} label={label} title={title}>
+      { components.map(makeComponent) }
+    </Section>
+  );
+}
+
+// --------------------------------------------------------------------------
+// --- Customization Panel
+// --------------------------------------------------------------------------
+
+function CustomizePanel({ dnd, settings, library, shape, setShape, setDragging })
+{
+
+  Dome.useUpdate( 'labview.library' );
+  const items = library.items ;
+  const views = getItems(items,'views');
+  const groups = getItems(items,'groups');
+  const components = getItems(items,'components');
+  const setting_folds = settings && settings + '.folds' ;
+  const setting_views = settings && settings + '.views' ;
+  const contents = {} ;
+
+  groups.unshift({ id:'nogroup', label:'Components' });
+  groups.forEach((g) => contents[g.id] = []);
+
+  components.forEach((c) => {
+    let gid = c.group ? 'groups.' + c.group : 'nogroup' ;
+    let content = contents[gid] ;
+    if (content === undefined) content = contents.nogroup ;
+    content.push(c);
+  });
+
+  return (
+    <SideBar settings={setting_folds}>
+      <CustomViews
+        key='views'
+        settings={setting_views}
+        shape={shape}
+        setShape={setShape}
+        views={views} />
+      {groups.map((g) => (
+        <CustomGroup
+          key={g.id} id={g.id}
+          label={g.label} title={g.title}
+          dnd={dnd} setDragging={setDragging} shape={shape}
+          components={contents[g.id]} />
+      ))}
+    </SideBar>
+  );
+}
+
+// --------------------------------------------------------------------------
+// --- LabView Container
+// --------------------------------------------------------------------------
+
+/**
+   @class
+   @summary Reconfigurable Container (React Component)
+   @property {boolean} [customize] - show components panel (false by default)
+   @property {string} [settings] - window settings to make views persistent
+   @property {React.Children} children - the labview content
+   @description
+   This container displays its content into a reconfigurable view.
+
+   The entire content is rendered, but elements must be packed into `<Component/>`
+   containers, otherwise, they would remain invisible.
+   Content may also contains `<View/>` and `<Group/>` definitions, and the content
+   can be defined through any kind of React components.
+*/
+export function LabView(props)
+{
+  // Parameters
+  const { settings, customize=false, children } = props ;
+  const setting_split = settings && settings + '.split' ;
+  const setting_shape = settings && settings + '.shape' ;
+  const setting_panel = settings && settings + '.panel' ;
+  // Hooks & State
+  Dome.useUpdate('labview.library','dome.defaults');
+  const dnd = React.useMemo(() => new DnD(),[]);
+  const lib = React.useMemo(() => new Library(),[]);
+  const [ shape, setShape ] = Dome.useState(setting_shape);
+  const [ dragging, setDragging ] = React.useState();
+  // Preparation
+  const onClose = (id) => setShape(Grids.removeShapeItem(shape,id));
+  const components = _.filter( lib.collection, (item) => isItemId('components',item.id) );
+  const gridItems = components.map(makeGridItem(customize,onClose));
+  const holding = dragging ? gridItems.find((elt) => elt.props.id === dragging) : undefined ;
+  // Make view
+  return (
+    <React.Fragment>
+      <UseLibrary library={lib}>
+        {children}
+      </UseLibrary>
+      <Splitter settings={setting_split} unfold={customize} dir='RIGHT'>
+        <GridLayout
+          dnd={dnd}
+          padding={2}
+          className='labview-container'
+          items={gridItems}
+          shape={shape}
+          onReshape={setShape}
+          holding={holding} />
+        <CustomizePanel
+          dnd={dnd}
+          settings={setting_panel}
+          shape={shape}
+          setShape={setShape}
+          setDragging={setDragging}
+          library={lib} />
+      </Splitter>
+    </React.Fragment>
+  );
+
+}
+
+// --------------------------------------------------------------------------
+
+export default { LabView, View, Group, Component, TitleBar } ;
+
+// --------------------------------------------------------------------------
diff --git a/ivette/src/frama-c/server.js b/ivette/src/frama-c/server.js
new file mode 100644
index 0000000000000000000000000000000000000000..d2de63e1d21b4c6d1b45f2e565a548acf31b6188
--- /dev/null
+++ b/ivette/src/frama-c/server.js
@@ -0,0 +1,821 @@
+// --------------------------------------------------------------------------
+// --- Frama-C Server
+// --------------------------------------------------------------------------
+
+/**
+    @module frama-c/server
+    @description
+    Manage the current Frama-C server/client interface
+*/
+
+import _ from 'lodash' ;
+import React from 'react' ;
+import Dome from 'dome' ;
+import System from 'dome/system' ;
+import { Buffer } from 'dome/text/buffers' ;
+import { Request } from 'zeromq' ;
+
+// --------------------------------------------------------------------------
+// --- Events
+// --------------------------------------------------------------------------
+
+/**
+   @event
+   @name 'frama-c.server.status'
+   @summary Server Status Notification Event
+   @description
+   This event is emitted whenever the server status changes.
+
+   Exported as `Server.STATUS' in public API.
+*/
+export const STATUS = 'frama-c.server.status' ;
+
+/**
+   @event
+   @name 'frama-c.server.ready'
+   @summary Server is actually started and running.
+   @description
+   This event is emitted when ther server _enters_ the `RUNNING` state.
+   It is now ready to handle requests.
+
+   Exported as `Server.READY' in public API.
+*/
+export const READY = 'frama-c.server.ready' ;
+
+/**
+   @event
+   @name 'frama-c.server.shutdown'
+   @summary Server Status Notification Event
+   @description
+   This event is emitted when ther server _leaves_ the `RUNNING` state.
+   It is no more able to handle requests until re-start.
+
+   Exported as `Server.SHUTDOWN' in public API.
+*/
+export const SHUTDOWN = 'frama-c.server.shutdown' ;
+
+/**
+   @event
+   @name 'frama-c.server.signal.*'
+   @summary Server Signal Prefix
+   @description
+   Event `frama-c.server.signal.<id>'` for signal `<id>`.
+   The prefix `'frama-c.server.signal.'` is exported as `Server.SIGNAL` in public API.
+*/
+export const SIGNAL = 'frama-c.server.signal.' ;
+
+/**
+   @event
+   @name 'frama-c.server.activity.*'
+   @summary Server Signal Activity Prefix
+   @param {boolean} active - whether the server is listening or not to the signal
+   @description
+   Event `frama-c.server.activity.<id>'` for signal `<id>`.
+   The prefix `'frama-c.server.activity.'` is exported as
+   `Server.ACTIVITY` in public API.
+*/
+export const ACTIVITY = 'frama-c.server.activity.' ;
+
+// --------------------------------------------------------------------------
+// --- Server Status
+// --------------------------------------------------------------------------
+
+/**
+   @typedef STATUS
+   @summary Server Status Codes.
+   @description
+- `OFF` Server off
+- `STARTED` Frama-C command launched
+- `RUNNING` Server ready
+- `KILLING` Server shutdown, waiting for exit
+- `RESTART` Server shutdown, will reboot on exit
+- `FAILED` Server halted on error
+ */
+
+export const OFF = 'OFF' ;
+export const STARTED = 'STARTED' ; // Command started
+export const RUNNING = 'RUNNING' ; // Server connected
+export const KILLING = 'KILLING' ; // Waiting for halt
+export const RESTART = 'RESTART' ; // Restart when halt
+export const FAILED  = 'FAILED'  ; // Error issued
+
+// --------------------------------------------------------------------------
+// --- Server Global State
+// --------------------------------------------------------------------------
+
+var status = OFF;
+var error;     // process error
+var rqid;      // Request ID
+var pending;   // Pending promise callbacks
+var queue_cmd; // Queue of server commands to be sent
+var queue_ids; // Waiting request ids to be sent
+var polling;   // Timeout Polling timer
+var flushing;   // Immediate Flushing timer
+var config;    // Server config
+var process;   // Server process
+var socket;    // ZMQ (REQ) socket
+var busy;      // ZMQ socket is busy
+var killing;    // killing timeout
+
+// --------------------------------------------------------------------------
+// --- Server Console
+// --------------------------------------------------------------------------
+
+export const buffer = new Buffer({ maxlines: 200 });
+export const feedback = '' ;
+
+// --------------------------------------------------------------------------
+// --- Server Status
+// --------------------------------------------------------------------------
+
+/**
+   @summary Current Server Status.
+   @return {STATUS} the current server status
+   @description
+   See [STATUS](module-frama-c_server.html#~STATUS) code definitions.
+*/
+export function getStatus() { return status; }
+
+/**
+   @summary Hook on current server (Custom React Hook).
+   @return {STATUS} the current server status
+   @description
+   See [STATUS](module-frama-c_server.html#~STATUS) code definitions.
+*/
+export function useStatus() {
+  Dome.useUpdate(STATUS);
+  return status;
+}
+
+/** Return `FAILED` status message. */
+export function getError() { return error; }
+
+/**
+   @summary Frama-C Server is running and ready to handle requests.
+   @return {boolean} status is `RUNNING`.
+*/
+export function isRunning() { return status === RUNNING; }
+
+/**
+   @summary Number of requests still pending.
+   @return {number} pending requests
+*/
+export function getPending() {
+  return _.reduce( pending , (_,n) => n+1, 0 );
+}
+
+/**
+   @summary Register callback on READY event.
+   @param {function} callback - invoked when the server enters RUNNING status
+ */
+export function onReady(callback) { Dome.on(READY,callback); }
+
+/**
+   @summary Register callback on SHUTDOWN event.
+   @param {function} callback - invoked when the server enters SHUTDOWN status
+ */
+export function onShutdown(callback) { Dome.on(SHUTDOWN,callback); }
+
+/**
+   @summary Register callback on Signal ACTIVITY event.
+   @param {function} callback - invoked with `callback(signal,active)`
+ */
+export function onActivity(signal,callback) {
+  Dome.on(ACTIVITY + signal,callback);
+}
+
+// --------------------------------------------------------------------------
+// --- Status Update
+// --------------------------------------------------------------------------
+
+function _status(new_s,err) {
+  if (Dome.DEVEL && err) console.error('[Server]',err);
+  if (new_s !== status || err) {
+    let old_s = status ;
+    status = new_s;
+    error = err ? err.toString() : undefined ;
+    Dome.emit(STATUS);
+    if (old_s === RUNNING) Dome.emit(SHUTDOWN);
+    if (new_s === RUNNING) Dome.emit(READY);
+  }
+}
+
+// --------------------------------------------------------------------------
+// --- Server Control (Start)
+// --------------------------------------------------------------------------
+
+/**
+   @summary Start the Server.
+   @description
+   If the server is started or running, this is a no-op.
+   If the server is being shutdown, it will reboot.
+   Otherwise, the Frama-C Server is spawned.
+*/
+export function start() {
+  switch(status) {
+  case OFF:
+  case FAILED:
+    _status(STARTED);
+    _launch()
+      .then(() => _status(RUNNING))
+      .catch((err) => _status(FAILED,err));
+    return;
+  case KILLING:
+    _status(RESTART);
+    return;
+  case STARTED:
+  case RUNNING:
+  case RESTART:
+  default:
+    return;
+  }
+}
+
+// --------------------------------------------------------------------------
+// --- Server Control (Stop)
+// --------------------------------------------------------------------------
+
+/**
+   @summary Stop the Server.
+   @description
+   If the server is starting, it is hard killed.
+   If the server is running, it is shutdown gracefully.
+   When the server is shutting down, restart is canceled.
+   Otherwise, this is a no-op.
+*/
+export function stop() {
+  switch(status) {
+  case STARTED:
+    _kill();
+    _status(KILLING);
+    return;
+  case RUNNING:
+    _shutdown();
+    _status(KILLING);
+    return;
+  case RESTART:
+    _status(KILLING);
+    return;
+  case OFF:
+  case FAILED:
+  case KILLING:
+  default:
+    return;
+  }
+}
+
+// --------------------------------------------------------------------------
+// --- Server Control (Kill)
+// --------------------------------------------------------------------------
+
+/**
+   @summary Terminate the Server.
+   @description
+   If the server is starting or running or shutting down,
+   it is hard killed and restart is canceled.
+   Otherwize, this is no-op.
+
+   This function is automatically called when the `module` emits the `KILL` signal.
+*/
+export function kill() {
+  switch(status) {
+  case STARTED:
+  case RUNNING:
+  case KILLING:
+  case RESTART:
+    _kill();
+    _status(KILLING);
+    return;
+  case OFF:
+  case FAILED:
+  default:
+    return;
+  }
+}
+
+// --------------------------------------------------------------------------
+// --- Server Control (Restart)
+// --------------------------------------------------------------------------
+
+/**
+   @summary Re-start the Server.
+   @description
+   If paused, simply start the Server.
+   When running, try to gracefully shutdown the Server,
+   and finally schedule a reboot on exit.
+*/
+export function restart() {
+  switch(status) {
+  case OFF:
+  case FAILED:
+    start();
+    return;
+  case RUNNING:
+    _shutdown();
+    // Fall Through
+  case KILLING:
+    _status(RESTART);
+    return;
+  case STARTED:
+  case RESTART:
+  default:
+    return;
+  }
+}
+
+// --------------------------------------------------------------------------
+// --- Server Control (Reset)
+// --------------------------------------------------------------------------
+
+/**
+   @summary Acknowledge `FAILED` status.
+   @description
+   When not running, clear the console and reset any error flag.
+   Otherwised, do nothing.
+*/
+export function clear() {
+  switch(status) {
+  case FAILED:
+    _status(OFF);
+    // Fall Through
+  case OFF:
+    buffer.clear();
+    Dome.emit(STATUS);
+    return;
+  default:
+    return;
+  }
+}
+
+// --------------------------------------------------------------------------
+// --- Server Configure
+// --------------------------------------------------------------------------
+
+/**
+   @summary Configure the Server.
+   @param {object} config - Server Configuration
+   @param {object} [config.env] - Process environment variables (default: `undefined`)
+   @param {string} [config.cwd] - Working directory (default: current)
+   @param {string} [config.command] - Server command (default: `frama-c`)
+   @param {Array.<string>} [config.params] - Additional server arguments (default: empty)
+   @param {string} [config.sockaddr] - Server socket (default: `ipc:///.frama-c.<pid>.io`)
+   @param {number} [config.timeout] - Shutdown timeout before server is hard killed, in milliseconds (default: 300ms)
+   @param {number} [config.polling] - Server polling period, in milliseconds (default: 50ms)
+   @param {string} [config.logout] - Process stdout log file (default: `undefined`)
+   @param {string} [config.logerr] - Process stderr log file (default: `undefined`)
+*/
+export function configure( cfg )
+{
+  config = cfg || {} ;
+}
+
+/**
+   @summary Configure the Server.
+   @return {object} server configuration
+   @description
+   See `configure()` method.
+*/
+export function getConfig() {
+  return config;
+}
+
+
+// --------------------------------------------------------------------------
+// --- Low-level Launching
+// --------------------------------------------------------------------------
+
+async function _launch() {
+  _reset();
+  if (!config) throw('Frama-C Server not configured');
+  let { env, cwd, command='frama-c', params=[], sockaddr, logout, logerr } = config;
+
+  buffer.clear();
+  buffer.append('$',command);
+  let size = params.reduce((n,p) => n + p.length , 0);
+  if (size < 40)
+    buffer.append('',...params);
+  else
+    params.forEach((argv) => {
+      if (argv.startsWith('-') || argv.endsWith('.c') || argv.endsWith('.i') || argv.endsWith('.h'))
+        buffer.append('\n    ');
+      buffer.append(' ');
+      buffer.append(argv);
+    });
+  buffer.append('\n');
+
+  if (!cwd) cwd = System.getWorkingDir();
+  if (!sockaddr) {
+    let socketfile = System.join(cwd,'.frama-c.' + System.getPID() + '.io');
+    System.atExit(() => System.remove(socketfile));
+    sockaddr = 'ipc://' + socketfile ;
+  }
+  logout = logout && System.join( cwd, logout );
+  logerr = logerr && System.join( cwd, logerr );
+  params = ['-server-zmq',sockaddr,'-then'].concat(params);
+  let options = {
+    cwd,
+    stdout: { path: logout, pipe: true },
+    stderr: { path: logerr, pipe: true },
+    env
+  };
+  // Launch Process
+  const process = await System.spawn( command, params, options );
+  const kill = () => process.kill() ;
+  const logger = (text) => {
+    buffer.append(text);
+    if (0 <= text.indexOf('\n'))
+      buffer.scroll();
+  };
+  process.stdout.on('data', logger );
+  process.stderr.on('data', logger );
+  process.on('error', (err) => {
+    buffer.append('Error:',err,'\n');
+    _close(err);
+  });
+  process.on('exit', (status,signal) => {
+    signal && buffer.log('Signal:',signal);
+    status && buffer.log('Exit:',status);
+    _close(signal || status);
+  });
+  // Connect to Server
+  socket = new Request();
+  busy = false ;
+  socket.connect(sockaddr);
+}
+
+// --------------------------------------------------------------------------
+// --- Low-level Killing
+// --------------------------------------------------------------------------
+
+function _reset() {
+  rqid = 0;
+  process = undefined;
+  queue_cmd = [];
+  queue_ids = [];
+  _.forEach( pending , ({ reject }) => reject('shutdown') );
+  pending = {};
+  if (flushing) clearImmediate(flushing);
+  flushing = undefined;
+  if (polling) clearTimeout(polling);
+  polling = undefined;
+}
+
+function _kill() {
+  _reset();
+  if (killing) clearTimeout(killing);
+  if (process) process.kill();
+}
+
+function _shutdown() {
+  _reset();
+  queue_cmd.push('SHUTDOWN');
+  _flush();
+  if (!killing) {
+    if (process) {
+      const timeout = (config && config.timeout) || 300 ;
+      killing = setTimeout( () => process.kill() , timeout );
+    }
+  }
+}
+
+function _close(error) {
+  _reset();
+  if (killing) {
+    clearTimeout( killing );
+    killing = undefined ;
+  }
+  if (socket) {
+    socket.close();
+    socket = undefined ;
+    busy = false ;
+  }
+  if (process) {
+    process.kill();
+    process = undefined ;
+  }
+  if (error) {
+    _status(FAILED,error);
+  } else {
+    if (status === RESTART) setImmediate(start);
+    _status(OFF);
+  }
+}
+
+// --------------------------------------------------------------------------
+// --- Request Queue
+// --------------------------------------------------------------------------
+
+/**
+   @summary Send a GET request to the server.
+   @param {string} rq - the request identifier
+   @param {object} params - request parameters
+   @return {Promise<object>} the promised request results
+   @description
+   You may _kill_ the request before its normal termination by
+   invoking `kill()` on the returned promised.
+ */
+export function sendGET( rq, params ) { return _sendRequest( 'GET' , rq , params ); }
+
+/**
+   @summary Send a SET request to the server.
+   @param {string} rq - the request identifier
+   @param {object} params - request parameters
+   @return {Promise<object>} the promised request results
+   @description
+   You may _kill_ the request before its normal termination by
+   invoking `kill()` on the returned promised.
+ */
+export function sendSET( rq, params ) { return _sendRequest( 'SET' , rq , params ); }
+
+/**
+   @summary Send an EXEC request to the server.
+   @param {string} rq - the request identifier
+   @param {object} params - request parameters
+   @return {Promise<object>} the promised request results
+   @description
+   You may _kill_ the request before its normal termination by
+   invoking `kill()` on the returned promised.
+ */
+export function sendEXEC( rq, params ) { return _sendRequest( 'EXEC' , rq , params ); }
+
+// --------------------------------------------------------------------------
+// --- Signal Management
+// --------------------------------------------------------------------------
+
+class Signal {
+
+  constructor(id) {
+    this.id = id ;
+    this.event = SIGNAL + id ;
+    this.active = false ;
+    this.listen = false ;
+    this.sigon = this.sigon.bind(this);
+    this.sigoff = _.debounce( this.sigoff.bind(this) , 1000 );
+  }
+
+  on(callback) {
+    let n = Dome.emitter.listenerCount( this.event );
+    Dome.on( this.event , callback );
+    if (n == 0) {
+      this.active = true ;
+      if (isRunning()) this.sigon();
+    }
+  }
+
+  off(callback) {
+    Dome.off( this.event , callback );
+    let n = Dome.emitter.listenerCount( this.event );
+    if (n == 0) {
+      this.active = false ;
+      if (isRunning()) this.sigoff();
+    }
+  }
+
+  /* Bound to this */
+  sigon() {
+    if (this.active && !this.listen) {
+      Dome.emit( ACTIVITY + this.id, true );
+      this.listen = true ;
+      queue_cmd.push('SIGON',this.id);
+      _flush();
+    }
+  }
+
+  /* Bound to this, Debounced */
+  sigoff() {
+    if (!this.active && this.listen) {
+      Dome.emit( ACTIVITY + this.id, false );
+      if (isRunning()) {
+        this.listen = false ;
+        queue_cmd.push('SIGOFF',this.id);
+        _flush();
+      }
+    }
+  }
+
+}
+
+//--- Memo
+
+const signals = {} ;
+function _signal(id)
+{
+  let s = signals[id];
+  if (!s) s = signals[id] = new Signal(id);
+  return s ;
+}
+
+//--- External API
+
+/**
+   @summary Register a Signal callback.
+   @param {string} id - the signal event to listen to
+   @param {function} callback - the callback to call on received signal
+   @description
+   If the server is not yet listening to this signal, a `SIGON` command is sent.
+ */
+export function onSignal( id , callback )
+{
+  _signal(id).on(callback);
+}
+
+/**
+   @summary Un-register a Signal callback.
+   @param {string} id - the signal event that was listen to
+   @param {function} callback - the callback to remove
+   @description
+   When no more callbacks are listening to this signal for a while,
+   the server will be notified with a `SIGOFF` command.
+ */
+export function offSignal( id , callback )
+{
+  _signal(id).off(callback);
+}
+
+/**
+   @summary Hook on Signal (Custom React Hook).
+   @param {string} id - the signal event to listen to
+   @param {function} callback - the callback to be called on signal
+ */
+export function useSignal( id , callback )
+{
+  React.useEffect( () => {
+    onSignal( id , callback );
+    return () => { offSignal(id,callback); };
+  });
+}
+
+//--- Server Synchro
+
+Dome.on( READY , () => {
+  _.forEach( signals , (s) => s.sigon() );
+});
+
+Dome.on( SHUTDOWN , () => {
+  _.forEach( signals , (s) => s.sigoff.cancel() );
+});
+
+// --------------------------------------------------------------------------
+// --- REQUEST Management
+// --------------------------------------------------------------------------
+
+function _sendRequest( kind, rq , params=null ) {
+  if (!isRunning()) return Promise.reject('Server not running');
+  if (!rq) return Promise.reject('Undefined request');
+  const rid = 'RQ.' + rqid++;
+  const data = JSON.stringify(params);
+  const promise = new Promise((resolve,reject) => {
+    pending[rid] = { resolve, reject };
+  });
+  promise.kill = () => {
+    if (socket && pending[rid]) {
+      queue_cmd.push('KILL',rid);
+      _flush();
+    }
+  };
+  queue_cmd.push(kind,rid,rq,data);
+  queue_ids.push(rid);
+  _flush();
+  return promise;
+}
+
+function _resolve(id,data) {
+  let promise = pending[id];
+  if (promise) {
+    delete pending[id];
+    promise.resolve(JSON.parse(data));
+  }
+}
+
+function _reject(id,error) {
+  let promise = pending[id];
+  if (promise) {
+    delete pending[id];
+    promise.reject(error);
+  }
+}
+
+function _cancel(ids) {
+  ids.forEach((rid) => _reject(rid,'canceled'));
+}
+
+function _waiting() {
+  return _.find( pending , () => true ) !== undefined ;
+}
+
+// --------------------------------------------------------------------------
+// --- Server Command Queue
+// --------------------------------------------------------------------------
+
+function _flush() {
+  if (!flushing) {
+    flushing = setImmediate(() => {
+      flushing = undefined;
+      _send();
+    });
+  }
+}
+
+function _poll() {
+  if (!polling) {
+    let delay = (config && config.polling) || 50 ;
+    polling = setTimeout(() => {
+      polling = undefined;
+      _send();
+    }, delay);
+  }
+}
+
+function _send() {
+  // when busy, will be eventually re-triggered
+  if (!busy) {
+    const cmds = queue_cmd ;
+    if (!cmds.length && _waiting()) cmds.push('POLL');
+    if (cmds.length) {
+      const ids = queue_ids ;
+      queue_cmd = [];
+      queue_ids = [];
+      if (socket) {
+        busy = true ;
+        socket.send( cmds )
+          .then(() => socket.receive().then((resp) => _receive(resp)))
+          .catch(() => _cancel(ids))
+          .finally(() => { busy = false ; Dome.emit(STATUS); });
+      } else
+        _cancel(ids);
+    } else {
+      // No pending command nor pending response
+      rqid = 0 ;
+    }
+    Dome.emit(STATUS);
+  }
+}
+
+function _receive(resp) {
+  try {
+    var rid,data,err,cmd;
+    const shift = () => resp.shift().toString();
+    while( resp.length ) {
+      cmd = shift();
+      switch( cmd ) {
+      case 'NONE':
+        break;
+      case 'DATA':
+        rid = shift();
+        data = shift();
+        _resolve(rid,data);
+        break;
+      case 'KILLED':
+        rid = shift();
+        _reject(rid,'killed');
+        break;
+      case 'ERROR':
+        rid = shift();
+        err = shift();
+        _reject(rid,err);
+        break;
+      case 'REJECTED':
+        rid = shift();
+        _reject(rid,'rejected');
+        break;
+      case 'SIGNAL':
+        rid = shift();
+        Dome.emit( SIGNAL + rid );
+        break;
+      case 'WRONG':
+        err = shift();
+        console.error('[Frama-C Server] ZMQ Protocol Error:',err);
+        break;
+      case 'NONE':
+        break;
+      default:
+        console.error('[Frama-C Server] Unknown Response:',cmd);
+        resp.length = 0;
+        break;
+      }
+    }
+  } finally {
+    if (queue_cmd.length)
+      _flush();
+    else
+      _poll();
+  }
+}
+
+// --------------------------------------------------------------------------
+// --- Exports
+// --------------------------------------------------------------------------
+
+export default {
+  configure, getConfig,
+  getStatus, useStatus, buffer,
+  getError, getPending, isRunning,
+  start, stop, kill, restart, clear,
+  sendGET, sendSET, sendEXEC,
+  onReady, onShutdown, onActivity,
+  onSignal, offSignal, useSignal,
+  STATUS,READY,SHUTDOWN,
+  OFF,STARTED,RUNNING,KILLING,RESTART,FAILED
+};
+
+// --------------------------------------------------------------------------
diff --git a/ivette/src/frama-c/states.js b/ivette/src/frama-c/states.js
new file mode 100644
index 0000000000000000000000000000000000000000..7f1d3f78d05327762123b5b9f88540a75204f4de
--- /dev/null
+++ b/ivette/src/frama-c/states.js
@@ -0,0 +1,467 @@
+// --------------------------------------------------------------------------
+// --- Frama-C States
+// --------------------------------------------------------------------------
+
+/**
+    @module frama-c/states
+    @description
+    Manage the current Frama-C project and projectified state values.
+*/
+
+import _ from 'lodash' ;
+import React from 'react' ;
+import Dome from 'dome' ;
+import Server from './server' ;
+
+/**
+   @event
+   @name 'frama-c.project'
+   @summary Current Project Updates
+   @description
+   Exported as `State.PROJECT` in public API.
+*/
+export const PROJECT = 'frama-c.project' ;
+
+/**
+   @event
+   @name 'frama-c.state.*'
+   @summary State Notification Events.
+   @description
+   Event `'frama-c.state.<id>'` for project `<id>`.
+   The prefix `'frama-c.state.'` is exported as `States.STATE` in public API.
+*/
+export const STATE = 'frama-c.state.' ;
+
+// --------------------------------------------------------------------------
+// --- Synchronized Current Project
+// --------------------------------------------------------------------------
+
+var currentProject = undefined ;
+var states = {};
+var stateDefaults = {};
+
+Server.onReady(() => {
+  Server.sendGET('kernel.project.getCurrent')
+    .then((current) => {
+      currentProject = current.id ;
+      Dome.emit(PROJECT);
+    });
+});
+
+Server.onShutdown(() => {
+  currentProject = undefined ;
+  states = {} ;
+  Dome.emit(PROJECT);
+});
+
+// --------------------------------------------------------------------------
+// --- Project API
+// --------------------------------------------------------------------------
+
+/**
+   @summary Current Project (Custom React Hook).
+   @return {string} the current project identifier, or `undefined`.
+ */
+export function useProject()
+{
+  Dome.useUpdate(PROJECT);
+  return currentProject;
+}
+
+/**
+   @summary Update Current Project.
+   @param {string} project - the project identifier
+   @description
+   Make all states switching to their projectified value.
+   Emits `PROJECT`.
+ */
+export function setProject(project)
+{
+  if (Server.isRunning()) {
+    Server.sendSET( 'kernel.project.setCurrent' , project );
+    currentProject = project ;
+    Dome.emit(PROJECT);
+  }
+}
+
+// --------------------------------------------------------------------------
+// --- Projectified State
+// --------------------------------------------------------------------------
+
+function getValue(id,project) {
+  if (!project) return undefined;
+  return _.get( states, [project,id], stateDefaults[id] );
+}
+
+function setValue(id,project,value) {
+  if (!project) return ;
+  _.set( states, [project,id], value );
+  Dome.emit( STATE + id , value );
+}
+
+/**
+   @summary Define the default state value.
+   @param {string} id - the state identifier (mandatory)
+   @param {any} value - the new default state
+ */
+export function setStateDefault(id,value)
+{
+  stateDefaults[id] = value;
+}
+
+/**
+   @summary Projectified State (Custom React Hook).
+   @param {string} id - the state identifier (mandatory)
+   @return {array} `[state,setState]` for the specified project
+   @description
+   Returns a getter and a setter for the specified state
+   in the specified or current project.
+   The initial value of states is always `undefined`.
+
+   Each state is associated to a specific event `frama-c-state.<id>` which is
+   is used to notify updates. The hook also updates on `PROJECT` notifications.
+ */
+export function useState(id)
+{
+  Dome.useUpdate( PROJECT, STATE + id );
+  const project = currentProject ;
+  const value = getValue(id,project);
+  return [ value , (v) => setValue(id,project,v) ];
+}
+
+// --------------------------------------------------------------------------
+// --- Cached GET Requests
+// --------------------------------------------------------------------------
+
+/**
+   @summary Cached GET request (Custom React Hook).
+   @param {string} rq - GET request name
+   @param {any} [params] - GET request parameter
+   @param {object} [options] - Special values
+   @param {any} [options.offline] - Returned value when off-line
+   @param {any} [options.pending] - Returned value when pending response
+   @param {any} [options.error] - Returned value on request error
+   @return {any} [result] GET request response (when available)
+   @description
+   Sends the specified GET request and returns its result.
+   The request is send asynchronously and cached until any change in
+   `rq`, `params`, current project or server activity.
+
+   Default values for various situations can be defined in the options parameter,
+   which is `undefined` unless specified, or `null` to keep the current value.
+   For instance `{ pending: null }` will return `undefined` when off-line and in case of errors,
+   but will keep the last received value until a new one is actually received.
+ */
+export function useRequest( rq, params, options={} )
+{
+  const project = useProject();
+  const [ value, setValue ] = React.useState( options.offline );
+  React.useEffect( () => {
+    if (project) {
+      const pending = options.prending ;
+      if (pending !== null) setValue(pending);
+      Server.sendGET( rq , params )
+        .then(setValue)
+        .catch(err => {
+          if (Dome.DEVEL) console.warn(`[Server] use request '${rq}':`,err);
+          const error = options.error ;
+          if (error !== null) setValue(error);
+        });
+    } else {
+      const v = options.offline ;
+      if (value !== v) setValue(v);
+    }
+  } , [ project, rq, JSON.stringify(params) ] );
+  return value;
+}
+
+// --------------------------------------------------------------------------
+// --- Dictionaries
+// --------------------------------------------------------------------------
+
+/**
+   @summary Cached GET request (Custom React Hook).
+   @param {string} rq - GET request name
+   @param {any} [params] - GET request parameter (default `'null'`)
+   @param {object} [options] - Dictionary options
+   @param {boolean} [options.key] - The property to index an item (default `'name'`)
+   @param {boolean} [options.offline] - Keep the dictionary when offline (default `true`)
+   @param {boolean} [options.pending] - Keep the dictionary when pending (default `true`)
+   @param {boolean} [options.error] - Keep the dictionary on error (default `false`)
+   @param {function} [options.filter] - Only index items satisfying the filter (default `undefined`)
+   @return {object} [result] GET request response indexed by key
+   @description
+   Sends the specified GET request and returns its returned collection indexed by the provided key.
+   Items in the collection that do have the key are not indexed.
+*/
+export function useDictionary( rq, params=null, options={} )
+{
+  const { offline=true, pending=true, error=false, key='name', filter } = options ;
+  const tags = useRequest( rq, params, {
+    offline: offline ? null : undefined,
+    pending: pending ? null : undefined,
+    error: error ? null : undefined
+  });
+  const dict = React.useMemo( () => {
+    const d = {};
+    _.forEach( tags, tg => {
+      let k = tg[key];
+      if (k && (!filter || filter(tg))) d[k] = tg;
+    });
+    return d;
+  } , [ tags, filter ]);
+  return dict;
+}
+
+// --------------------------------------------------------------------------
+// --- Synchronized States
+// --------------------------------------------------------------------------
+
+// shared for all projects
+class SyncState {
+
+  constructor(id) {
+    this.id = id ;
+    this.UPDATE = STATE + id ;
+    this.signal = id + '.sig' ;
+    this.get_rq = id + '.get' ;
+    this.set_rq = id + '.set' ;
+    this.insync = false ;
+    this.value = undefined ;
+    this.update = this.update.bind(this);
+    this.effect = this.effect.bind(this);
+    this.setValue = this.setValue.bind(this);
+    Dome.on( PROJECT , this.update );
+  }
+
+  value() {
+    if (!this.insync && Server.isRunning())
+      this.update();
+    return this.value ;
+  }
+
+  setValue(v) {
+    this.insync = true ;
+    this.value = v ;
+    Server.sendSET( this.set_rq , v );
+    Dome.emit( this.UPDATE );
+  }
+
+  update() {
+    const project = currentProject ;
+    this.insync = true ;
+    Server.sendGET( this.get_rq ).then((v) => {
+      this.value = v ;
+      Dome.emit( this.UPDATE );
+    });
+  }
+
+}
+
+// --------------------------------------------------------------------------
+// --- Synchronized States Registry
+// --------------------------------------------------------------------------
+
+var syncStates = {} ;
+
+function getSyncState(id) {
+  let s = syncStates[id] ;
+  if (!s) s = syncStates[id] = new SyncState(id);
+  return s ;
+}
+
+Server.onShutdown(() => syncStates = {});
+
+// --------------------------------------------------------------------------
+// --- Synchronized State Hooks
+// --------------------------------------------------------------------------
+
+/**
+   @summary Use Synchronized State (Custom React Hook).
+   @parameter {string} id - name of the server state
+   @return {Array} `[ value , setValue ]` of the synchronized state
+   @description
+   Synchronization with some (projectified) server state:
+   - sends a `<id>.get` request to obtain the current value of the state;
+   - sends a `<id>.set` request to update the value of the state;
+   - listens to `<id>.sig` signal to stay in sync with server updates.
+ */
+export function useSyncState(id)
+{
+  let s = getSyncState(id) ;
+  Dome.useUpdate( PROJECT, s.UPDATE );
+  Server.useSignal( s.signal , s.update );
+  return [ s.value() , s.setValue ];
+}
+
+/**
+   @summary Use Synchronized Value (Custom React Hook).
+   @parameter {string} id - name of the server state
+   @return {any} current `value` of the state
+   @description
+   Synchronization with some (projectified) server value:
+   - sends a `<id>.get` request to obtain the current value of the state;
+   - listens to `<id>.sig` signal to stay in sync with server updates.
+*/
+export function useSyncValue(id)
+{
+  let s = getSyncState(id) ;
+  Dome.useUpdate( s.update );
+  Server.useSignal( s.signal , s.update );
+  return s.value();
+}
+
+// --------------------------------------------------------------------------
+// --- Synchronized Arrays
+// --------------------------------------------------------------------------
+
+// one per project
+class SyncArray
+{
+
+  constructor(id) {
+    this.UPDATE = STATE + id ;
+    this.signal = id + '.sig' ;
+    this.fetch_rq = id + '.fetch' ;
+    this.reload_rq = id + '.reload' ;
+    this.index = {} ;
+    this.insync = false ;
+    this.fetch = this.fetch.bind(this);
+    this.reload = this.reload.bind(this);
+  }
+
+  getItems() {
+    if (!this.insync && Server.isRunning()) this.fetch();
+    return this.index;
+  }
+
+  isEmpty() {
+    return _.find( this.index , () => true ) !== undefined ;
+  }
+
+  fetch() {
+    this.insync = true ;
+    Server.sendGET( this.fetch_rq, 50 )
+      .then(({
+        reload=false, removed=[], updated=[], pending=0
+      }) => {
+        let reloaded = false ;
+        if (reload) {
+          reloaded = this.isEmpty();
+          this.index = {};
+        }
+        removed.forEach((key) => {
+          delete this.index[key];
+        });
+        updated.forEach((item) => {
+          this.index[item.key] = item;
+        });
+        if (reloaded || removed.length || updated.length) {
+          this.index = Object.assign( {}, this.index );
+          Dome.emit( this.UPDATE );
+        }
+        if (pending>0) {
+          this.fetch();
+        }
+      });
+  }
+
+  reload() {
+    Server.sendSET( this.reload_rq );
+    this.index = {};
+    this.insync = false ;
+    Dome.emit( this.UPDATE );
+  }
+
+}
+
+// --------------------------------------------------------------------------
+// --- Synchronized Arrays Registry
+// --------------------------------------------------------------------------
+
+var syncArrays = {} ; // Model by project & id
+
+function getSyncArray(id) {
+  const path = [ currentProject || '' , id ] ;
+  let a = _.get( syncArrays , path );
+  if (!a) {
+    a = new SyncArray(id);
+    _.set( syncArrays , path , a );
+  }
+  return a;
+}
+
+Server.onShutdown(() => syncArrays = {});
+
+// --------------------------------------------------------------------------
+// --- Synchronized Array Hooks
+// --------------------------------------------------------------------------
+
+/**
+   @summary Force a Synchronized Array to Reload.
+   @description
+   Sends the `<id>.reload` request to the server for
+   triggering a complete array reload.
+ */
+export function reloadArray(id)
+{
+  getSyncArray(id).reload();
+}
+
+/**
+   @summary Use Synchronized Array (Custom React Hook).
+   @parameter {string} id - name of the server array
+   @return {object} items indexed by their identifiers
+   @description
+   Synchronization with some (projectified) server array:
+   - sends `<id>.fetch` requests to obtain the updated entries;
+   - listens to `<id>.sig` signal to stay in sync with server updates.
+ */
+export function useSyncArray(id)
+{
+  let a = getSyncArray(id);
+  Dome.useUpdate( PROJECT, a.UPDATE );
+  Server.useSignal( a.signal, a.fetch );
+  return a.getItems() ;
+}
+
+// --------------------------------------------------------------------------
+// --- Selection
+// --------------------------------------------------------------------------
+
+const SELECTION = 'kernel.selection' ;
+
+setStateDefault( SELECTION , {} );
+
+/**
+   @summary Current selection state.
+   @return {array} `[selection,update]` for the current selection
+   @description
+   The selection is an object with many independant fields.
+   You update it by providing only some fields, the other ones being kept unchanged,
+   like the `setState()` behaviour of React components.
+ */
+export function useSelection()
+{
+  const [ state, setState ] = useState( SELECTION );
+  return [ state, (upd) => setState(Object.assign( {}, state, upd )) ];
+}
+
+// --------------------------------------------------------------------------
+
+export default {
+  useProject,
+  setProject,
+  setStateDefault,
+  useState,
+  useSyncState,
+  useSyncValue,
+  useSyncArray,
+  reloadArray,
+  useRequest,
+  useDictionary,
+  useSelection,
+  PROJECT, STATE
+};
+
+// --------------------------------------------------------------------------
diff --git a/ivette/src/main/index.js b/ivette/src/main/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..2624543a9daad843d4d432821eee867a08b836ae
--- /dev/null
+++ b/ivette/src/main/index.js
@@ -0,0 +1,19 @@
+// --------------------------------------------------------------------------
+// --- Electron main-process entry-point for Dome.
+// --------------------------------------------------------------------------
+
+/*
+   Template of ./src/main/index.js
+   Imported from $(DOME)/template/main.js
+
+   The call to Dome.start() will initialize the Dome application
+   and create the main window that will run the renderer process
+   from `src/renderer/index.js`.
+
+   You may add your own code to be run in the Electron main-process
+   before or after the call to `Dome.start()`.
+*/
+
+import Dome from 'dome' ;
+Dome.setName('Ivette');
+Dome.start();
diff --git a/ivette/src/renderer/ASTview.js b/ivette/src/renderer/ASTview.js
new file mode 100644
index 0000000000000000000000000000000000000000..db6830d63c51f8adc52c7607553c7c23de3d213d
--- /dev/null
+++ b/ivette/src/renderer/ASTview.js
@@ -0,0 +1,98 @@
+// --------------------------------------------------------------------------
+// --- AST Source Code
+// --------------------------------------------------------------------------
+
+import _ from 'lodash' ;
+import React from 'react' ;
+import Dome from 'dome' ;
+import Server from 'frama-c/server' ;
+import States from 'frama-c/states' ;
+
+import { Vfill } from 'dome/layout/boxes' ;
+import { Buffer } from 'dome/text/buffers' ;
+import { Text } from 'dome/text/editors' ;
+import { Component } from 'frama-c/labviews' ;
+
+import 'codemirror/mode/clike/clike.js';
+import 'codemirror/theme/ambiance.css' ;
+
+// --------------------------------------------------------------------------
+// --- Rich Text Printer
+// --------------------------------------------------------------------------
+
+const print = (buffer, text) => {
+  if (Array.isArray(text)) {
+    const tag = text.shift();
+    if (tag !== '')
+      buffer.openTextMarker( { id:tag } );
+    text.forEach(txt => print(buffer, txt));
+    if (tag !== '')
+      buffer.closeTextMarker();
+  } else if (typeof(text)==='string')
+    buffer.append(text);
+};
+
+// --------------------------------------------------------------------------
+// --- AST Printer
+// --------------------------------------------------------------------------
+
+const ASTview = () => {
+
+  // Hooks
+  const buffer = React.useMemo( () => new Buffer(), []);
+  const [ select, setSelect ] = States.useSelection();
+  const theFunction = select && select.function ;
+  const theMarker = select && select.marker ;
+
+  // Hook: async loading
+  React.useEffect( () => {
+    buffer.clear();
+    if (theFunction) {
+      buffer.log('// Loading',theFunction,'…');
+      Server
+        .sendGET("kernel.ast.printFunction", theFunction)
+        .then(data => {
+          buffer.clear();
+          if (!data)
+            buffer.log('// No code for function ', theFunction);
+          print(buffer,data);
+          if (theMarker) buffer.scroll( theMarker );
+      });
+    }
+  }, [ theFunction ] );
+
+  // Hook: scrolling
+  React.useEffect( () => {
+    if (theMarker) buffer.scroll( theMarker );
+  }, [ theMarker ] );
+
+  // Callbacks
+  const onSelection = marker => setSelect({ marker });
+
+  // Component
+  return (
+    <Vfill>
+      <Text buffer={buffer}
+            mode='text/x-csrc'
+            theme='ambiance'
+            selection={theMarker}
+            onSelection={onSelection}
+            readOnly />
+    </Vfill>
+  );
+};
+
+// --------------------------------------------------------------------------
+// --- Export Component
+// --------------------------------------------------------------------------
+
+export default () => (
+  <Component id='frama-c.astview'
+             label='AST'
+             title='Normalized source code representation.'
+             >
+    <ASTview/>
+  </Component>
+);
+
+// --------------------------------------------------------------------------
diff --git a/ivette/src/renderer/Application.js b/ivette/src/renderer/Application.js
new file mode 100644
index 0000000000000000000000000000000000000000..684da31a04f7b5f2afef904f438765f5bcceeb8c
--- /dev/null
+++ b/ivette/src/renderer/Application.js
@@ -0,0 +1,75 @@
+// --------------------------------------------------------------------------
+// --- Main React Component rendered by './index.js'
+// --------------------------------------------------------------------------
+
+import React from 'react' ;
+import Dome from 'dome' ;
+import { Catch } from 'dome/errors' ;
+import { Vfill } from 'dome/layout/boxes' ;
+import { Splitter } from 'dome/layout/splitters' ;
+import Toolbar from 'dome/layout/toolbars' ;
+import Sidebar from 'dome/layout/sidebars' ;
+
+import './style.css' ;
+import 'dome/misc/exports' ;
+
+import { LabView, View, Group, Component } from 'frama-c/labviews' ;
+import { GridItem, GridHbox, GridVbox } from 'dome/layout/grids';
+import Controller from './Controller' ;
+import Properties from './Properties' ;
+import ASTview from './ASTview' ;
+
+// --------------------------------------------------------------------------
+// --- Main View
+// --------------------------------------------------------------------------
+
+export default (function() {
+
+  const [sidebar,flipSidebar] = Dome.useSwitch('frama-c.sidebar.unfold',false);
+  const [viewbar,flipViewbar] = Dome.useSwitch('frama-c.viewbar.unfold',false);
+
+  return (
+    <Vfill>
+      <Toolbar.ToolBar>
+        <Toolbar.Button
+          icon='SIDEBAR' title='Show/Hide side bar'
+          selected={sidebar}
+          onClick={flipSidebar}
+          />
+        <Controller.Control/>
+        <Toolbar.Filler/>
+        <Toolbar.Button
+          icon='ITEMS.GRID'
+          title='Customize Main View'
+          selected={viewbar}
+          onClick={flipViewbar}/>
+      </Toolbar.ToolBar>
+      <Splitter dir='LEFT' settings='frame-c.sidebar.position' unfold={sidebar}>
+        <Sidebar.SideBar>
+          <div>(Empty)</div>
+        </Sidebar.SideBar>
+        <LabView
+          customize={viewbar}
+          settings='frama-c.labview'
+          >
+          <View id='dashboard' label='Dashboard' defaultView>
+            <GridItem id='frama-c.console'/>
+          </View>
+          <Group id='frama-c' label='Frama-C' title='Frama-C Kernel Components'>
+            <Controller.Console/>
+            <Properties/>
+            <ASTview/>
+          </Group>
+        </LabView>
+      </Splitter>
+      <Toolbar.ToolBar>
+        <Controller.Status/>
+        <Toolbar.Filler/>
+        <Controller.Stats/>
+      </Toolbar.ToolBar>
+    </Vfill>
+  );
+
+});
+
+// --------------------------------------------------------------------------
diff --git a/ivette/src/renderer/Controller.js b/ivette/src/renderer/Controller.js
new file mode 100644
index 0000000000000000000000000000000000000000..e0f6405d2dec4190ab49019e1e94dc16fdebebe7
--- /dev/null
+++ b/ivette/src/renderer/Controller.js
@@ -0,0 +1,231 @@
+// --------------------------------------------------------------------------
+// --- Server Controller
+// --------------------------------------------------------------------------
+
+import React from 'react' ;
+import Dome from 'dome' ;
+import Server from 'frama-c/server' ;
+import States from 'frama-c/states' ;
+
+import { Vfill } from 'dome/layout/boxes' ;
+import { Component, TitleBar } from 'frama-c/labviews' ;
+import { Button as ToolButton, ButtonGroup, Space } from 'dome/layout/toolbars' ;
+import { LED, IconButton } from 'dome/controls/buttons' ;
+import { Label, Code } from 'dome/controls/labels' ;
+import { Buffer } from 'dome/text/buffers' ;
+import { Text } from 'dome/text/editors' ;
+
+import 'codemirror/theme/ambiance.css' ;
+
+// --------------------------------------------------------------------------
+// --- Configure Server
+// --------------------------------------------------------------------------
+
+var cmdConfig ;
+const cmdLine = new Buffer();
+
+function dumpCmdLine(config={})
+{
+  const { cwd, command, sockaddr, params } = config ;
+  cmdLine.clear();
+  if (cwd) cmdLine.log('--cwd',cwd);
+  if (command) cmdLine.log('--command',command);
+  if (sockaddr) cmdLine.log('--socket',sockaddr);
+  if (params) params.forEach((v,i) => {
+    if (i>0) {
+      if (v.startsWith('-') || v.endsWith('.c') || v.endsWith('.h') || v.endsWith('.i'))
+        cmdLine.append('\n');
+      else
+        cmdLine.append(' ');
+    }
+    cmdLine.append(v);
+  });
+  cmdLine.append('\n');
+};
+
+function configOfParams(argv,cwd) {
+  let params = [];
+  let command ;
+  let sockaddr ;
+  let working = cwd ;
+  for (let k = 0 ; k < argv.length ; k++) {
+    let v = argv[k];
+    switch(v) {
+    case '--cwd':
+      working = argv[++k];
+      break;
+    case '--command':
+      command = argv[++k];
+      break;
+    case '--socket':
+      sockaddr = argv[++k];
+      break;
+    default:
+      params.push(v);
+    }
+  }
+  return { cwd:working, command, sockaddr, params };
+}
+
+Dome.onCommand((argv,cwd) => {
+  cmdConfig = configOfParams(argv,cwd);
+  dumpCmdLine(cmdConfig);
+  Server.configure(cmdConfig);
+  Server.start();
+});
+
+// --------------------------------------------------------------------------
+// --- Server Control
+// --------------------------------------------------------------------------
+
+export const Control = () => {
+  let status = Server.useStatus();
+  let play = { enabled: false } ;
+  let stop = { enabled: false } ;
+  let reload = { enabled: false } ;
+  switch(status) {
+  case 'OFF':
+  case 'FAILED':
+    play = { enabled: true, onClick:Server.start };
+    break;
+  case 'RUNNING':
+    stop = { enabled: true, onClick:Server.stop };
+    reload = { enabled: true, onClick:Server.restart };
+    break;
+  }
+  return (
+    <ButtonGroup>
+      <ToolButton icon='MEDIA.PLAY' {...play}
+              title='Start the server' />
+      <ToolButton icon='RELOAD' {...reload}
+              title='Re-start the server' />
+      <ToolButton icon='MEDIA.STOP' {...stop}
+              title='Shut down the server'/>
+    </ButtonGroup>
+  );
+};
+
+// --------------------------------------------------------------------------
+// --- Server Console
+// --------------------------------------------------------------------------
+
+function getCmdLine() {
+  return cmdLine.getDoc().getValue().trim();
+}
+
+function execCmdLine(cmd) {
+  let argv = cmd.split(/[ \t\n]+/);
+  let cfg = configOfParams(argv);
+  Server.configure(cfg);
+  Server.restart();
+}
+
+const RenderConsole = () => {
+  const [ cmd , switchCmd ] = Dome.useSwitch();
+  const { current, next, prev, index, length, update, insert, clear } = Dome.useHistory( 'frama-c.command.history' );
+  const doExec = () => {
+    let cmd = getCmdLine();
+    if (cmd != current) insert(cmd);
+    execCmdLine(cmd);
+    switchCmd();
+  };
+  const doNext = () => { cmdLine.getDoc().setValue(next() || ''); };
+  const doPrev = () => { cmdLine.getDoc().setValue(prev() || ''); };
+  const doReload = () => { dumpCmdLine( cmdConfig ); };
+  const doDrop = () => {
+    cmdLine.clear();
+    cmdLine.getDoc().setValue(update(undefined) || '');
+  };
+  return (
+    <React.Fragment>
+      <TitleBar label={cmd ? 'Command Line' : 'Console'}>
+        <Label className='dimmed' display={cmd && length>0}>
+          {1+index}/{length}
+        </Label>
+        <Space/>
+        <IconButton icon='TRASH' display={cmd && clear} disabled={!clear} onClick={clear} title='Clear History'/>
+        <IconButton icon='CROSS' display={cmd && clear} disabled={!current} onClick={doDrop} title='Remove Command'/>
+        <Space/>
+        <IconButton icon='MEDIA.PREV' display={cmd} disabled={!prev} onClick={doPrev} title='Previous Command'/>
+        <IconButton icon='RELOAD' display={cmd} onClick={doReload} title='Reset Command Line'/>
+        <IconButton icon='MEDIA.NEXT' display={cmd} disabled={!next} onClick={doNext} title='Previous Command'/>
+        <Space/>
+        <IconButton icon='MEDIA.PLAY' display={cmd} onClick={doExec} title='Execute Command Line'/>
+        <IconButton icon='EDIT' selected={cmd} onClick={switchCmd} title='Edit Command Line'/>
+      </TitleBar>
+      <Text buffer={cmd ? cmdLine : Server.buffer}
+            mode='text'
+            readOnly={!cmd}
+            theme='ambiance' />
+    </React.Fragment>
+  );
+};
+
+export const Console = () => (
+  <Component id='frama-c.console'
+             label='Console'
+             title='Frama-C Server Output & Command Line'>
+    <RenderConsole/>
+  </Component>
+);
+
+// --------------------------------------------------------------------------
+// --- Status
+// --------------------------------------------------------------------------
+
+export const Status = () => {
+  Dome.useUpdate( Server.STATUS );
+  let s = Server.getStatus();
+  let n = Server.getPending();
+  let led, blink, error ;
+  switch(s) {
+  case Server.OFF:
+    led = 'inactive' ;
+    break;
+  case Server.STARTED:
+    led = 'active' ;
+    blink = true ;
+    break;
+  case Server.RUNNING:
+    led = n>0 ? 'positive' : 'active' ;
+    break;
+  case Server.KILLING:
+    led = 'negative' ;
+    blink = true ;
+    break;
+  case Server.RESTART:
+    led = 'warning' ;
+    blink = true ;
+    break;
+  case Server.FAILED:
+    led = 'negative' ;
+    blink = false ;
+    error = Server.getError();
+    break;
+  }
+  return (
+    <React.Fragment>
+      <LED status={led} blink={blink} />
+      <Code label={s}/>
+      { error && <Label icon='WARNING' label={error} /> }
+    </React.Fragment>
+  );
+};
+
+// --------------------------------------------------------------------------
+// --- Server Stats
+// --------------------------------------------------------------------------
+
+export const Stats = () => {
+  Dome.useUpdate( Server.STATUS );
+  let n = Server.getPending();
+  return n > 0 ? <Code>{n} rq.</Code> : null ;
+};
+
+// --------------------------------------------------------------------------
+// --- Controller Exports
+// --------------------------------------------------------------------------
+
+export default { Control, Console, Status, Stats };
+
+// --------------------------------------------------------------------------
diff --git a/ivette/src/renderer/Preferences.js b/ivette/src/renderer/Preferences.js
new file mode 100644
index 0000000000000000000000000000000000000000..542d1d7c97a42463aa3f0f6f528e1262b58a4e07
--- /dev/null
+++ b/ivette/src/renderer/Preferences.js
@@ -0,0 +1,20 @@
+// --------------------------------------------------------------------------
+// --- Main React Component rendered by './index.js'
+// --------------------------------------------------------------------------
+
+/*
+   Template from $(DOME)/template/Settings.js
+
+   This module shall export a React Component that
+   will be rendered (with empty props and children)
+   in the settings window of your application.
+
+*/
+
+import React from 'react' ;
+
+export default (() => (
+  <h1 className='dome-text-title' style={{margin: 24}}>
+    Settings (none)
+  </h1>
+));
diff --git a/ivette/src/renderer/Properties.js b/ivette/src/renderer/Properties.js
new file mode 100644
index 0000000000000000000000000000000000000000..3eb8dec2966e84f83bad3f4c2ff83d5d2759f033
--- /dev/null
+++ b/ivette/src/renderer/Properties.js
@@ -0,0 +1,76 @@
+// --------------------------------------------------------------------------
+// --- Properties
+// --------------------------------------------------------------------------
+
+import _ from 'lodash' ;
+import React from 'react' ;
+import Dome from 'dome' ;
+import States from 'frama-c/states' ;
+import { Label, Code } from 'dome/controls/labels' ;
+import { ArrayModel } from 'dome/table/arrays' ;
+import { Table, Column, DefineColumn } from 'dome/table/views' ;
+import { Component } from 'frama-c/labviews' ;
+
+// --------------------------------------------------------------------------
+// --- Property Columns
+// --------------------------------------------------------------------------
+
+const ColumnCode = DefineColumn({ renderValue: (text) => <Code>{text}</Code> });
+const ColumnTag = DefineColumn({ renderValue: ({ label, descr }) => (
+  <Label label={label} title={descr}/>
+)});
+
+// --------------------------------------------------------------------------
+// --- Properties Table
+// -------------------------------------------------------------------------
+
+const RenderTable = () => {
+
+  // Hooks
+  const model = React.useMemo( () => new ArrayModel() , [] );
+  const items = States.useSyncArray('kernel.properties');
+  const status = States.useDictionary('kernel.dictionary.propstatus');
+  const [select,setSelect] = States.useSelection();
+  React.useEffect( () => {
+    model.setData( _.toArray( items ) );
+  }, [ model, items ]);
+
+  // Callbacks
+  const getStatus = ({status:st}) => status[st] || { label:st } ;
+  const selection = select ? items[ select.marker ] : undefined ;
+  const onSelection = (item) => item && setSelect({
+    marker: item.key,
+    function: item.function
+  });
+
+  // Rendering
+  return (
+    <React.Fragment>
+      <Table model={model}
+             selection={selection}
+             onSelection={onSelection}
+             scrollToItem={selection}
+             >
+        <ColumnCode id='function' label='Function' width={120} />
+        <ColumnCode id='descr' label='Description' fill />
+        <ColumnTag id='status' label='Status'
+                   fixed width={80} align='center'
+                   getValue={getStatus} />
+      </Table>
+    </React.Fragment>
+  );
+};
+
+// --------------------------------------------------------------------------
+// --- Export Component
+// -------------------------------------------------------------------------
+
+export default () => (
+  <Component id='frama-c.properties'
+             label='Properties'
+             title='Registered ACSL properties status' >
+    <RenderTable/>
+  </Component>
+);
+
+// --------------------------------------------------------------------------
diff --git a/ivette/src/renderer/index.js b/ivette/src/renderer/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..1544ac9e11373febc48161e9672b71456c6819f6
--- /dev/null
+++ b/ivette/src/renderer/index.js
@@ -0,0 +1,41 @@
+// --------------------------------------------------------------------------
+// --- Electron renderer-process entry-point for Dome.
+// --------------------------------------------------------------------------
+
+/*
+   Template of ./src/renderer/index.js
+   Imported from $(DOME)/template/renderer.js
+
+   You can modify this template and add your own code before or
+   after rendering the main components.
+
+   However, please note that React hot-reloading is fragile.
+   Component that are not imported by <Application/> or <Preferences/>
+   may suffer from well known limitations of hot-reloading and
+   may lead to state losses or complete application reloads.
+*/
+
+// Enable live-editing in React:
+import 'react-hot-loader/patch' ;
+import React from 'react' ;
+import Dome from 'dome' ;
+
+// You can change the name of the main components,
+// provided you define the makefile variable
+// `DOME_CUSTOM_ENTRIES` to prevent files 'Application.js' and 'Preferences.js'
+// from being generated by the template makefile.
+
+import Application from './Application' ;
+import Preferences from './Preferences' ;
+
+// Define the application main components for each window:
+Dome.setApplicationWindow(Application);
+Dome.setPreferencesWindow(Preferences);
+
+// Mark the main application reloadable and enable live updates:
+module.hot && Dome.isApplicationWindow() &&
+  module.hot.accept('./Application',() => Dome.setApplicationWindow(Application));
+module.hot && Dome.isPreferencesWindow() &&
+  module.hot.accept('./Preferences',() => Dome.setPreferencesWindow(Preferences));
+
+// --------------------------------------------------------------------------
diff --git a/ivette/src/renderer/style.css b/ivette/src/renderer/style.css
new file mode 100644
index 0000000000000000000000000000000000000000..de68fb842c2f4d783486ba20e1ab3b8a65b00792
--- /dev/null
+++ b/ivette/src/renderer/style.css
@@ -0,0 +1,9 @@
+/* -------------------------------------------------------------------------- */
+/* --- Frama-C Styling                                                    --- */
+/* -------------------------------------------------------------------------- */
+
+.dimmed {
+    color: #888 ;
+}
+
+/* -------------------------------------------------------------------------- */
diff --git a/ivette/tests/eva-1.i b/ivette/tests/eva-1.i
new file mode 100644
index 0000000000000000000000000000000000000000..336eb7fa6e6bd6839f28791d682b74889b6116f5
--- /dev/null
+++ b/ivette/tests/eva-1.i
@@ -0,0 +1,12 @@
+// Test for Ivette
+
+int main(int x,int y)
+{
+  int k ;
+  if (0 < x) k = 0;
+  k += x ;
+  k += y ;
+  k += x ;
+  k += y ;
+  return k;
+}
diff --git a/ivette/webpack.main.js b/ivette/webpack.main.js
new file mode 100644
index 0000000000000000000000000000000000000000..608d29372314dcb2e5cd7406dac737c35371daf6
--- /dev/null
+++ b/ivette/webpack.main.js
@@ -0,0 +1,38 @@
+// --------------------------------------------------------------------------
+// --- Webpack extension for electron main-process
+// --------------------------------------------------------------------------
+
+/*
+   Template of ./webpack.main.js from $(DOME)/template/webpack.main.js
+
+   This webpack definitions will be merged into electron-webpack
+   ones thanks to electron-webpack.json configuration file.
+
+   You may extend it with your own additions.
+*/
+
+const path = require('path');
+const DOME = process.env.DOME || path.resolve(__dirname , 'dome');
+const ENV = process.env.DOME_ENV ;
+
+// Do not use electron-devtools-installer in production mode
+function domeDevtools() {
+  switch(ENV) {
+  case 'dev':
+    return 'electron-devtools-installer';
+  default:
+    return path.resolve( DOME , 'src/misc/devtools.js' );
+  }
+}
+
+module.exports = {
+  resolve: {
+    alias: {
+      'dome$':         path.resolve( DOME , 'src/main/dome.js' ),
+      'dome/system$':  path.resolve( DOME , 'src/misc/system.js' ),
+      'dome/devtools': domeDevtools()
+    }
+  }
+} ;
+
+// --------------------------------------------------------------------------
diff --git a/ivette/webpack.plugin.js b/ivette/webpack.plugin.js
new file mode 100644
index 0000000000000000000000000000000000000000..f60bdbf7ab8cda15ad9dcba2c747a5a3d82e57ed
--- /dev/null
+++ b/ivette/webpack.plugin.js
@@ -0,0 +1,41 @@
+// --------------------------------------------------------------------------
+// --- Webpack configuration for packing plugins
+// --------------------------------------------------------------------------
+
+/*
+   Template of ./webpack.plugin.js from $(DOME)/template/webpack.plugin.js
+
+   This webpack definitions will be used to make plugin bundles.
+
+   You may extend it with your own additions.
+*/
+
+const path = require('path');
+const DOME = process.env.DOME || path.resolve( __dirname , 'dome' );
+const PLUGIN = process.env.DOME_PLUGIN ;
+const ENTRY = path.resolve( __dirname , 'src/plugins' , PLUGIN );
+const DIST = path.resolve( __dirname , 'dist/plugins' , PLUGIN );
+
+module.exports = {
+  entry: ENTRY,
+  output: {
+    path: DIST,
+    filename: 'bundle.js',
+    libraryTarget: 'commonjs2'
+  },
+  module: {
+    rules: [
+      { test: /\.css$/, use: [ 'style-loader', 'css-loader' ] },
+      { test: /\.js$/, use: [ 'babel-loader' ], exclude: /(node_modules)/ }
+    ]
+  },
+  externals: [
+    'lodash',
+    'react',
+    /^dome\/.+$/,
+    /^@plugins\/.+$/,
+    /^@\/.+$/
+  ]
+};
+
+// --------------------------------------------------------------------------
diff --git a/ivette/webpack.renderer.js b/ivette/webpack.renderer.js
new file mode 100644
index 0000000000000000000000000000000000000000..523eaa554cde3df6800a34a768dc9eb3948abbdf
--- /dev/null
+++ b/ivette/webpack.renderer.js
@@ -0,0 +1,38 @@
+// --------------------------------------------------------------------------
+// --- Webpack extension for electron main-process
+// --------------------------------------------------------------------------
+
+/*
+   Template from $(DOME)/template/webpack.main.js
+
+   This webpack definitions will be merged into electron-webpack
+   ones thanks to electron-webpack.json configuration file.
+
+   You may extend it with your own additions.
+*/
+
+const path = require('path');
+const DOME = process.env.DOME || path.resolve( __dirname , 'dome' );
+
+// --------------------------------------------------------------------------
+
+module.exports = {
+  module: {
+    rules: [
+      { test: /\.css$/, use: [ 'css-loader' ] }
+    ]
+  },
+  resolve: {
+    alias: {
+      'frama-c':      path.resolve( __dirname , 'src/frama-c' ),
+      '@plugins':     path.resolve( __dirname , 'src/plugins' ),
+      'dome/misc':    path.resolve( DOME , 'src/misc' ),
+      'dome/system':  path.resolve( DOME , 'src/misc/system.js' ),
+      'dome$':        path.resolve( DOME , 'src/renderer/dome.js' ),
+      'dome':         path.resolve( DOME , 'src/renderer' ),
+      'react-dom':    '@hot-loader/react-dom'
+    }
+  }
+} ;
+
+// --------------------------------------------------------------------------
diff --git a/ivette/yarn.lock b/ivette/yarn.lock
new file mode 100644
index 0000000000000000000000000000000000000000..1df871031514506d4b97c672d4df859da2fdc497
--- /dev/null
+++ b/ivette/yarn.lock
@@ -0,0 +1,8215 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"7zip-bin@~5.0.3":
+  version "5.0.3"
+  resolved "https://registry.yarnpkg.com/7zip-bin/-/7zip-bin-5.0.3.tgz#bc5b5532ecafd923a61f2fb097e3b108c0106a3f"
+  integrity sha512-GLyWIFBbGvpKPGo55JyRZAo4lVbnBiD52cKlw/0Vt+wnmKvWJkpZvsjVoaIolyBXDeAQKSicRtqFNPem9w0WYA==
+
+"7zip@0.0.6":
+  version "0.0.6"
+  resolved "https://registry.yarnpkg.com/7zip/-/7zip-0.0.6.tgz#9cafb171af82329490353b4816f03347aa150a30"
+  integrity sha1-nK+xca+CMpSQNTtIFvAzR6oVCjA=
+
+"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.5.5":
+  version "7.5.5"
+  resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.5.5.tgz#bc0782f6d69f7b7d49531219699b988f669a8f9d"
+  integrity sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==
+  dependencies:
+    "@babel/highlight" "^7.0.0"
+
+"@babel/core@^7", "@babel/core@^7.5.0":
+  version "7.7.2"
+  resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.7.2.tgz#ea5b99693bcfc058116f42fa1dd54da412b29d91"
+  integrity sha512-eeD7VEZKfhK1KUXGiyPFettgF3m513f8FoBSWiQ1xTvl1RAopLs42Wp9+Ze911I6H0N9lNqJMDgoZT7gHsipeQ==
+  dependencies:
+    "@babel/code-frame" "^7.5.5"
+    "@babel/generator" "^7.7.2"
+    "@babel/helpers" "^7.7.0"
+    "@babel/parser" "^7.7.2"
+    "@babel/template" "^7.7.0"
+    "@babel/traverse" "^7.7.2"
+    "@babel/types" "^7.7.2"
+    convert-source-map "^1.7.0"
+    debug "^4.1.0"
+    json5 "^2.1.0"
+    lodash "^4.17.13"
+    resolve "^1.3.2"
+    semver "^5.4.1"
+    source-map "^0.5.0"
+
+"@babel/generator@^7.7.2":
+  version "7.7.2"
+  resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.7.2.tgz#2f4852d04131a5e17ea4f6645488b5da66ebf3af"
+  integrity sha512-WthSArvAjYLz4TcbKOi88me+KmDJdKSlfwwN8CnUYn9jBkzhq0ZEPuBfkAWIvjJ3AdEV1Cf/+eSQTnp3IDJKlQ==
+  dependencies:
+    "@babel/types" "^7.7.2"
+    jsesc "^2.5.1"
+    lodash "^4.17.13"
+    source-map "^0.5.0"
+
+"@babel/generator@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.7.4.tgz#db651e2840ca9aa66f327dcec1dc5f5fa9611369"
+  integrity sha512-m5qo2WgdOJeyYngKImbkyQrnUN1mPceaG5BV+G0E3gWsa4l/jCSryWJdM2x8OuGAOyh+3d5pVYfZWCiNFtynxg==
+  dependencies:
+    "@babel/types" "^7.7.4"
+    jsesc "^2.5.1"
+    lodash "^4.17.13"
+    source-map "^0.5.0"
+
+"@babel/helper-annotate-as-pure@^7.0.0", "@babel/helper-annotate-as-pure@^7.7.0":
+  version "7.7.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.7.0.tgz#efc54032d43891fe267679e63f6860aa7dbf4a5e"
+  integrity sha512-k50CQxMlYTYo+GGyUGFwpxKVtxVJi9yh61sXZji3zYHccK9RYliZGSTOgci85T+r+0VFN2nWbGM04PIqwfrpMg==
+  dependencies:
+    "@babel/types" "^7.7.0"
+
+"@babel/helper-annotate-as-pure@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.7.4.tgz#bb3faf1e74b74bd547e867e48f551fa6b098b6ce"
+  integrity sha512-2BQmQgECKzYKFPpiycoF9tlb5HA4lrVyAmLLVK177EcQAqjVLciUb2/R+n1boQ9y5ENV3uz2ZqiNw7QMBBw1Og==
+  dependencies:
+    "@babel/types" "^7.7.4"
+
+"@babel/helper-builder-binary-assignment-operator-visitor@^7.1.0":
+  version "7.7.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.7.0.tgz#32dd9551d6ed3a5fc2edc50d6912852aa18274d9"
+  integrity sha512-Cd8r8zs4RKDwMG/92lpZcnn5WPQ3LAMQbCw42oqUh4s7vsSN5ANUZjMel0OOnxDLq57hoDDbai+ryygYfCTOsw==
+  dependencies:
+    "@babel/helper-explode-assignable-expression" "^7.7.0"
+    "@babel/types" "^7.7.0"
+
+"@babel/helper-builder-binary-assignment-operator-visitor@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.7.4.tgz#5f73f2b28580e224b5b9bd03146a4015d6217f5f"
+  integrity sha512-Biq/d/WtvfftWZ9Uf39hbPBYDUo986m5Bb4zhkeYDGUllF43D+nUe5M6Vuo6/8JDK/0YX/uBdeoQpyaNhNugZQ==
+  dependencies:
+    "@babel/helper-explode-assignable-expression" "^7.7.4"
+    "@babel/types" "^7.7.4"
+
+"@babel/helper-builder-react-jsx@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/helper-builder-react-jsx/-/helper-builder-react-jsx-7.7.4.tgz#da188d247508b65375b2c30cf59de187be6b0c66"
+  integrity sha512-kvbfHJNN9dg4rkEM4xn1s8d1/h6TYNvajy9L1wx4qLn9HFg0IkTsQi4rfBe92nxrPUFcMsHoMV+8rU7MJb3fCA==
+  dependencies:
+    "@babel/types" "^7.7.4"
+    esutils "^2.0.0"
+
+"@babel/helper-call-delegate@^7.4.4":
+  version "7.7.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-call-delegate/-/helper-call-delegate-7.7.0.tgz#df8942452c2c1a217335ca7e393b9afc67f668dc"
+  integrity sha512-Su0Mdq7uSSWGZayGMMQ+z6lnL00mMCnGAbO/R0ZO9odIdB/WNU/VfQKqMQU0fdIsxQYbRjDM4BixIa93SQIpvw==
+  dependencies:
+    "@babel/helper-hoist-variables" "^7.7.0"
+    "@babel/traverse" "^7.7.0"
+    "@babel/types" "^7.7.0"
+
+"@babel/helper-call-delegate@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/helper-call-delegate/-/helper-call-delegate-7.7.4.tgz#621b83e596722b50c0066f9dc37d3232e461b801"
+  integrity sha512-8JH9/B7J7tCYJ2PpWVpw9JhPuEVHztagNVuQAFBVFYluRMlpG7F1CgKEgGeL6KFqcsIa92ZYVj6DSc0XwmN1ZA==
+  dependencies:
+    "@babel/helper-hoist-variables" "^7.7.4"
+    "@babel/traverse" "^7.7.4"
+    "@babel/types" "^7.7.4"
+
+"@babel/helper-create-regexp-features-plugin@^7.7.0":
+  version "7.7.2"
+  resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.7.2.tgz#6f20443778c8fce2af2ff4206284afc0ced65db6"
+  integrity sha512-pAil/ZixjTlrzNpjx+l/C/wJk002Wo7XbbZ8oujH/AoJ3Juv0iN/UTcPUHXKMFLqsfS0Hy6Aow8M31brUYBlQQ==
+  dependencies:
+    "@babel/helper-regex" "^7.4.4"
+    regexpu-core "^4.6.0"
+
+"@babel/helper-create-regexp-features-plugin@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.7.4.tgz#6d5762359fd34f4da1500e4cff9955b5299aaf59"
+  integrity sha512-Mt+jBKaxL0zfOIWrfQpnfYCN7/rS6GKx6CCCfuoqVVd+17R8zNDlzVYmIi9qyb2wOk002NsmSTDymkIygDUH7A==
+  dependencies:
+    "@babel/helper-regex" "^7.4.4"
+    regexpu-core "^4.6.0"
+
+"@babel/helper-define-map@^7.7.0":
+  version "7.7.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.7.0.tgz#60b0e9fd60def9de5054c38afde8c8ee409c7529"
+  integrity sha512-kPKWPb0dMpZi+ov1hJiwse9dWweZsz3V9rP4KdytnX1E7z3cTNmFGglwklzFPuqIcHLIY3bgKSs4vkwXXdflQA==
+  dependencies:
+    "@babel/helper-function-name" "^7.7.0"
+    "@babel/types" "^7.7.0"
+    lodash "^4.17.13"
+
+"@babel/helper-define-map@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.7.4.tgz#2841bf92eb8bd9c906851546fe6b9d45e162f176"
+  integrity sha512-v5LorqOa0nVQUvAUTUF3KPastvUt/HzByXNamKQ6RdJRTV7j8rLL+WB5C/MzzWAwOomxDhYFb1wLLxHqox86lg==
+  dependencies:
+    "@babel/helper-function-name" "^7.7.4"
+    "@babel/types" "^7.7.4"
+    lodash "^4.17.13"
+
+"@babel/helper-explode-assignable-expression@^7.7.0":
+  version "7.7.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.7.0.tgz#db2a6705555ae1f9f33b4b8212a546bc7f9dc3ef"
+  integrity sha512-CDs26w2shdD1urNUAji2RJXyBFCaR+iBEGnFz3l7maizMkQe3saVw9WtjG1tz8CwbjvlFnaSLVhgnu1SWaherg==
+  dependencies:
+    "@babel/traverse" "^7.7.0"
+    "@babel/types" "^7.7.0"
+
+"@babel/helper-explode-assignable-expression@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.7.4.tgz#fa700878e008d85dc51ba43e9fb835cddfe05c84"
+  integrity sha512-2/SicuFrNSXsZNBxe5UGdLr+HZg+raWBLE9vC98bdYOKX/U6PY0mdGlYUJdtTDPSU0Lw0PNbKKDpwYHJLn2jLg==
+  dependencies:
+    "@babel/traverse" "^7.7.4"
+    "@babel/types" "^7.7.4"
+
+"@babel/helper-function-name@^7.7.0":
+  version "7.7.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.7.0.tgz#44a5ad151cfff8ed2599c91682dda2ec2c8430a3"
+  integrity sha512-tDsJgMUAP00Ugv8O2aGEua5I2apkaQO7lBGUq1ocwN3G23JE5Dcq0uh3GvFTChPa4b40AWiAsLvCZOA2rdnQ7Q==
+  dependencies:
+    "@babel/helper-get-function-arity" "^7.7.0"
+    "@babel/template" "^7.7.0"
+    "@babel/types" "^7.7.0"
+
+"@babel/helper-function-name@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.7.4.tgz#ab6e041e7135d436d8f0a3eca15de5b67a341a2e"
+  integrity sha512-AnkGIdiBhEuiwdoMnKm7jfPfqItZhgRaZfMg1XX3bS25INOnLPjPG1Ppnajh8eqgt5kPJnfqrRHqFqmjKDZLzQ==
+  dependencies:
+    "@babel/helper-get-function-arity" "^7.7.4"
+    "@babel/template" "^7.7.4"
+    "@babel/types" "^7.7.4"
+
+"@babel/helper-get-function-arity@^7.0.0", "@babel/helper-get-function-arity@^7.7.0":
+  version "7.7.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.0.tgz#c604886bc97287a1d1398092bc666bc3d7d7aa2d"
+  integrity sha512-tLdojOTz4vWcEnHWHCuPN5P85JLZWbm5Fx5ZsMEMPhF3Uoe3O7awrbM2nQ04bDOUToH/2tH/ezKEOR8zEYzqyw==
+  dependencies:
+    "@babel/types" "^7.7.0"
+
+"@babel/helper-get-function-arity@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.4.tgz#cb46348d2f8808e632f0ab048172130e636005f0"
+  integrity sha512-QTGKEdCkjgzgfJ3bAyRwF4yyT3pg+vDgan8DSivq1eS0gwi+KGKE5x8kRcbeFTb/673mkO5SN1IZfmCfA5o+EA==
+  dependencies:
+    "@babel/types" "^7.7.4"
+
+"@babel/helper-hoist-variables@^7.7.0":
+  version "7.7.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.7.0.tgz#b4552e4cfe5577d7de7b183e193e84e4ec538c81"
+  integrity sha512-LUe/92NqsDAkJjjCEWkNe+/PcpnisvnqdlRe19FahVapa4jndeuJ+FBiTX1rcAKWKcJGE+C3Q3tuEuxkSmCEiQ==
+  dependencies:
+    "@babel/types" "^7.7.0"
+
+"@babel/helper-hoist-variables@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.7.4.tgz#612384e3d823fdfaaf9fce31550fe5d4db0f3d12"
+  integrity sha512-wQC4xyvc1Jo/FnLirL6CEgPgPCa8M74tOdjWpRhQYapz5JC7u3NYU1zCVoVAGCE3EaIP9T1A3iW0WLJ+reZlpQ==
+  dependencies:
+    "@babel/types" "^7.7.4"
+
+"@babel/helper-member-expression-to-functions@^7.7.0":
+  version "7.7.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.7.0.tgz#472b93003a57071f95a541ea6c2b098398bcad8a"
+  integrity sha512-QaCZLO2RtBcmvO/ekOLp8p7R5X2JriKRizeDpm5ChATAFWrrYDcDxPuCIBXKyBjY+i1vYSdcUTMIb8psfxHDPA==
+  dependencies:
+    "@babel/types" "^7.7.0"
+
+"@babel/helper-member-expression-to-functions@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.7.4.tgz#356438e2569df7321a8326644d4b790d2122cb74"
+  integrity sha512-9KcA1X2E3OjXl/ykfMMInBK+uVdfIVakVe7W7Lg3wfXUNyS3Q1HWLFRwZIjhqiCGbslummPDnmb7vIekS0C1vw==
+  dependencies:
+    "@babel/types" "^7.7.4"
+
+"@babel/helper-module-imports@7.0.0-beta.35":
+  version "7.0.0-beta.35"
+  resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.0.0-beta.35.tgz#308e350e731752cdb4d0f058df1d704925c64e0a"
+  integrity sha512-vaC1KyIZSuyWb3Lj277fX0pxivyHwuDU4xZsofqgYAbkDxNieMg2vuhzP5AgMweMY7fCQUMTi+BgPqTLjkxXFg==
+  dependencies:
+    "@babel/types" "7.0.0-beta.35"
+    lodash "^4.2.0"
+
+"@babel/helper-module-imports@^7.7.0":
+  version "7.7.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.7.0.tgz#99c095889466e5f7b6d66d98dffc58baaf42654d"
+  integrity sha512-Dv3hLKIC1jyfTkClvyEkYP2OlkzNvWs5+Q8WgPbxM5LMeorons7iPP91JM+DU7tRbhqA1ZeooPaMFvQrn23RHw==
+  dependencies:
+    "@babel/types" "^7.7.0"
+
+"@babel/helper-module-imports@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.7.4.tgz#e5a92529f8888bf319a6376abfbd1cebc491ad91"
+  integrity sha512-dGcrX6K9l8258WFjyDLJwuVKxR4XZfU0/vTUgOQYWEnRD8mgr+p4d6fCUMq/ys0h4CCt/S5JhbvtyErjWouAUQ==
+  dependencies:
+    "@babel/types" "^7.7.4"
+
+"@babel/helper-module-transforms@^7.1.0", "@babel/helper-module-transforms@^7.7.0":
+  version "7.7.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.7.0.tgz#154a69f0c5b8fd4d39e49750ff7ac4faa3f36786"
+  integrity sha512-rXEefBuheUYQyX4WjV19tuknrJFwyKw0HgzRwbkyTbB+Dshlq7eqkWbyjzToLrMZk/5wKVKdWFluiAsVkHXvuQ==
+  dependencies:
+    "@babel/helper-module-imports" "^7.7.0"
+    "@babel/helper-simple-access" "^7.7.0"
+    "@babel/helper-split-export-declaration" "^7.7.0"
+    "@babel/template" "^7.7.0"
+    "@babel/types" "^7.7.0"
+    lodash "^4.17.13"
+
+"@babel/helper-module-transforms@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.7.4.tgz#8d7cdb1e1f8ea3d8c38b067345924ac4f8e0879a"
+  integrity sha512-ehGBu4mXrhs0FxAqN8tWkzF8GSIGAiEumu4ONZ/hD9M88uHcD+Yu2ttKfOCgwzoesJOJrtQh7trI5YPbRtMmnA==
+  dependencies:
+    "@babel/helper-module-imports" "^7.7.4"
+    "@babel/helper-simple-access" "^7.7.4"
+    "@babel/helper-split-export-declaration" "^7.7.4"
+    "@babel/template" "^7.7.4"
+    "@babel/types" "^7.7.4"
+    lodash "^4.17.13"
+
+"@babel/helper-module-transforms@^7.7.5":
+  version "7.7.5"
+  resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.7.5.tgz#d044da7ffd91ec967db25cd6748f704b6b244835"
+  integrity sha512-A7pSxyJf1gN5qXVcidwLWydjftUN878VkalhXX5iQDuGyiGK3sOrrKKHF4/A4fwHtnsotv/NipwAeLzY4KQPvw==
+  dependencies:
+    "@babel/helper-module-imports" "^7.7.4"
+    "@babel/helper-simple-access" "^7.7.4"
+    "@babel/helper-split-export-declaration" "^7.7.4"
+    "@babel/template" "^7.7.4"
+    "@babel/types" "^7.7.4"
+    lodash "^4.17.13"
+
+"@babel/helper-optimise-call-expression@^7.7.0":
+  version "7.7.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.7.0.tgz#4f66a216116a66164135dc618c5d8b7a959f9365"
+  integrity sha512-48TeqmbazjNU/65niiiJIJRc5JozB8acui1OS7bSd6PgxfuovWsvjfWSzlgx+gPFdVveNzUdpdIg5l56Pl5jqg==
+  dependencies:
+    "@babel/types" "^7.7.0"
+
+"@babel/helper-optimise-call-expression@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.7.4.tgz#034af31370d2995242aa4df402c3b7794b2dcdf2"
+  integrity sha512-VB7gWZ2fDkSuqW6b1AKXkJWO5NyNI3bFL/kK79/30moK57blr6NbH8xcl2XcKCwOmJosftWunZqfO84IGq3ZZg==
+  dependencies:
+    "@babel/types" "^7.7.4"
+
+"@babel/helper-plugin-utils@^7.0.0":
+  version "7.0.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz#bbb3fbee98661c569034237cc03967ba99b4f250"
+  integrity sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA==
+
+"@babel/helper-regex@^7.0.0", "@babel/helper-regex@^7.4.4":
+  version "7.5.5"
+  resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.5.5.tgz#0aa6824f7100a2e0e89c1527c23936c152cab351"
+  integrity sha512-CkCYQLkfkiugbRDO8eZn6lRuR8kzZoGXCg3149iTk5se7g6qykSpy3+hELSwquhu+TgHn8nkLiBwHvNX8Hofcw==
+  dependencies:
+    lodash "^4.17.13"
+
+"@babel/helper-remap-async-to-generator@^7.7.0":
+  version "7.7.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.7.0.tgz#4d69ec653e8bff5bce62f5d33fc1508f223c75a7"
+  integrity sha512-pHx7RN8X0UNHPB/fnuDnRXVZ316ZigkO8y8D835JlZ2SSdFKb6yH9MIYRU4fy/KPe5sPHDFOPvf8QLdbAGGiyw==
+  dependencies:
+    "@babel/helper-annotate-as-pure" "^7.7.0"
+    "@babel/helper-wrap-function" "^7.7.0"
+    "@babel/template" "^7.7.0"
+    "@babel/traverse" "^7.7.0"
+    "@babel/types" "^7.7.0"
+
+"@babel/helper-remap-async-to-generator@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.7.4.tgz#c68c2407350d9af0e061ed6726afb4fff16d0234"
+  integrity sha512-Sk4xmtVdM9sA/jCI80f+KS+Md+ZHIpjuqmYPk1M7F/upHou5e4ReYmExAiu6PVe65BhJPZA2CY9x9k4BqE5klw==
+  dependencies:
+    "@babel/helper-annotate-as-pure" "^7.7.4"
+    "@babel/helper-wrap-function" "^7.7.4"
+    "@babel/template" "^7.7.4"
+    "@babel/traverse" "^7.7.4"
+    "@babel/types" "^7.7.4"
+
+"@babel/helper-replace-supers@^7.5.5", "@babel/helper-replace-supers@^7.7.0":
+  version "7.7.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.7.0.tgz#d5365c8667fe7cbd13b8ddddceb9bd7f2b387512"
+  integrity sha512-5ALYEul5V8xNdxEeWvRsBzLMxQksT7MaStpxjJf9KsnLxpAKBtfw5NeMKZJSYDa0lKdOcy0g+JT/f5mPSulUgg==
+  dependencies:
+    "@babel/helper-member-expression-to-functions" "^7.7.0"
+    "@babel/helper-optimise-call-expression" "^7.7.0"
+    "@babel/traverse" "^7.7.0"
+    "@babel/types" "^7.7.0"
+
+"@babel/helper-replace-supers@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.7.4.tgz#3c881a6a6a7571275a72d82e6107126ec9e2cdd2"
+  integrity sha512-pP0tfgg9hsZWo5ZboYGuBn/bbYT/hdLPVSS4NMmiRJdwWhP0IznPwN9AE1JwyGsjSPLC364I0Qh5p+EPkGPNpg==
+  dependencies:
+    "@babel/helper-member-expression-to-functions" "^7.7.4"
+    "@babel/helper-optimise-call-expression" "^7.7.4"
+    "@babel/traverse" "^7.7.4"
+    "@babel/types" "^7.7.4"
+
+"@babel/helper-simple-access@^7.7.0":
+  version "7.7.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.7.0.tgz#97a8b6c52105d76031b86237dc1852b44837243d"
+  integrity sha512-AJ7IZD7Eem3zZRuj5JtzFAptBw7pMlS3y8Qv09vaBWoFsle0d1kAn5Wq6Q9MyBXITPOKnxwkZKoAm4bopmv26g==
+  dependencies:
+    "@babel/template" "^7.7.0"
+    "@babel/types" "^7.7.0"
+
+"@babel/helper-simple-access@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.7.4.tgz#a169a0adb1b5f418cfc19f22586b2ebf58a9a294"
+  integrity sha512-zK7THeEXfan7UlWsG2A6CI/L9jVnI5+xxKZOdej39Y0YtDYKx9raHk5F2EtK9K8DHRTihYwg20ADt9S36GR78A==
+  dependencies:
+    "@babel/template" "^7.7.4"
+    "@babel/types" "^7.7.4"
+
+"@babel/helper-split-export-declaration@^7.7.0":
+  version "7.7.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.7.0.tgz#1365e74ea6c614deeb56ebffabd71006a0eb2300"
+  integrity sha512-HgYSI8rH08neWlAH3CcdkFg9qX9YsZysZI5GD8LjhQib/mM0jGOZOVkoUiiV2Hu978fRtjtsGsW6w0pKHUWtqA==
+  dependencies:
+    "@babel/types" "^7.7.0"
+
+"@babel/helper-split-export-declaration@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.7.4.tgz#57292af60443c4a3622cf74040ddc28e68336fd8"
+  integrity sha512-guAg1SXFcVr04Guk9eq0S4/rWS++sbmyqosJzVs8+1fH5NI+ZcmkaSkc7dmtAFbHFva6yRJnjW3yAcGxjueDug==
+  dependencies:
+    "@babel/types" "^7.7.4"
+
+"@babel/helper-wrap-function@^7.7.0":
+  version "7.7.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.7.0.tgz#15af3d3e98f8417a60554acbb6c14e75e0b33b74"
+  integrity sha512-sd4QjeMgQqzshSjecZjOp8uKfUtnpmCyQhKQrVJBBgeHAB/0FPi33h3AbVlVp07qQtMD4QgYSzaMI7VwncNK/w==
+  dependencies:
+    "@babel/helper-function-name" "^7.7.0"
+    "@babel/template" "^7.7.0"
+    "@babel/traverse" "^7.7.0"
+    "@babel/types" "^7.7.0"
+
+"@babel/helper-wrap-function@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.7.4.tgz#37ab7fed5150e22d9d7266e830072c0cdd8baace"
+  integrity sha512-VsfzZt6wmsocOaVU0OokwrIytHND55yvyT4BPB9AIIgwr8+x7617hetdJTsuGwygN5RC6mxA9EJztTjuwm2ofg==
+  dependencies:
+    "@babel/helper-function-name" "^7.7.4"
+    "@babel/template" "^7.7.4"
+    "@babel/traverse" "^7.7.4"
+    "@babel/types" "^7.7.4"
+
+"@babel/helpers@^7.7.0":
+  version "7.7.0"
+  resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.7.0.tgz#359bb5ac3b4726f7c1fde0ec75f64b3f4275d60b"
+  integrity sha512-VnNwL4YOhbejHb7x/b5F39Zdg5vIQpUUNzJwx0ww1EcVRt41bbGRZWhAURrfY32T5zTT3qwNOQFWpn+P0i0a2g==
+  dependencies:
+    "@babel/template" "^7.7.0"
+    "@babel/traverse" "^7.7.0"
+    "@babel/types" "^7.7.0"
+
+"@babel/highlight@^7.0.0":
+  version "7.5.0"
+  resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.5.0.tgz#56d11312bd9248fa619591d02472be6e8cb32540"
+  integrity sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==
+  dependencies:
+    chalk "^2.0.0"
+    esutils "^2.0.2"
+    js-tokens "^4.0.0"
+
+"@babel/parser@^7.4.4":
+  version "7.7.3"
+  resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.7.3.tgz#5fad457c2529de476a248f75b0f090b3060af043"
+  integrity sha512-bqv+iCo9i+uLVbI0ILzKkvMorqxouI+GbV13ivcARXn9NNEabi2IEz912IgNpT/60BNXac5dgcfjb94NjsF33A==
+
+"@babel/parser@^7.7.0", "@babel/parser@^7.7.2":
+  version "7.7.2"
+  resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.7.2.tgz#ea8334dc77416bfd9473eb470fd00d8245b3943b"
+  integrity sha512-DDaR5e0g4ZTb9aP7cpSZLkACEBdoLGwJDWgHtBhrGX7Q1RjhdoMOfexICj5cqTAtpowjGQWfcvfnQG7G2kAB5w==
+
+"@babel/parser@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.7.4.tgz#75ab2d7110c2cf2fa949959afb05fa346d2231bb"
+  integrity sha512-jIwvLO0zCL+O/LmEJQjWA75MQTWwx3c3u2JOTDK5D3/9egrWRRA0/0hk9XXywYnXZVVpzrBYeIQTmhwUaePI9g==
+
+"@babel/plugin-proposal-async-generator-functions@^7.7.0":
+  version "7.7.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.7.0.tgz#83ef2d6044496b4c15d8b4904e2219e6dccc6971"
+  integrity sha512-ot/EZVvf3mXtZq0Pd0+tSOfGWMizqmOohXmNZg6LNFjHOV+wOPv7BvVYh8oPR8LhpIP3ye8nNooKL50YRWxpYA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/helper-remap-async-to-generator" "^7.7.0"
+    "@babel/plugin-syntax-async-generators" "^7.2.0"
+
+"@babel/plugin-proposal-async-generator-functions@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.7.4.tgz#0351c5ac0a9e927845fffd5b82af476947b7ce6d"
+  integrity sha512-1ypyZvGRXriY/QP668+s8sFr2mqinhkRDMPSQLNghCQE+GAkFtp+wkHVvg2+Hdki8gwP+NFzJBJ/N1BfzCCDEw==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/helper-remap-async-to-generator" "^7.7.4"
+    "@babel/plugin-syntax-async-generators" "^7.7.4"
+
+"@babel/plugin-proposal-dynamic-import@^7.7.0":
+  version "7.7.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.7.0.tgz#dc02a8bad8d653fb59daf085516fa416edd2aa7f"
+  integrity sha512-7poL3Xi+QFPC7sGAzEIbXUyYzGJwbc2+gSD0AkiC5k52kH2cqHdqxm5hNFfLW3cRSTcx9bN0Fl7/6zWcLLnKAQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/plugin-syntax-dynamic-import" "^7.2.0"
+
+"@babel/plugin-proposal-dynamic-import@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.7.4.tgz#dde64a7f127691758cbfed6cf70de0fa5879d52d"
+  integrity sha512-StH+nGAdO6qDB1l8sZ5UBV8AC3F2VW2I8Vfld73TMKyptMU9DY5YsJAS8U81+vEtxcH3Y/La0wG0btDrhpnhjQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/plugin-syntax-dynamic-import" "^7.7.4"
+
+"@babel/plugin-proposal-json-strings@^7.2.0":
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.2.0.tgz#568ecc446c6148ae6b267f02551130891e29f317"
+  integrity sha512-MAFV1CA/YVmYwZG0fBQyXhmj0BHCB5egZHCKWIFVv/XCxAeVGIHfos3SwDck4LvCllENIAg7xMKOG5kH0dzyUg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/plugin-syntax-json-strings" "^7.2.0"
+
+"@babel/plugin-proposal-json-strings@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.7.4.tgz#7700a6bfda771d8dc81973249eac416c6b4c697d"
+  integrity sha512-wQvt3akcBTfLU/wYoqm/ws7YOAQKu8EVJEvHip/mzkNtjaclQoCCIqKXFP5/eyfnfbQCDV3OLRIK3mIVyXuZlw==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/plugin-syntax-json-strings" "^7.7.4"
+
+"@babel/plugin-proposal-object-rest-spread@^7.6.2":
+  version "7.6.2"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.6.2.tgz#8ffccc8f3a6545e9f78988b6bf4fe881b88e8096"
+  integrity sha512-LDBXlmADCsMZV1Y9OQwMc0MyGZ8Ta/zlD9N67BfQT8uYwkRswiu2hU6nJKrjrt/58aH/vqfQlR/9yId/7A2gWw==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/plugin-syntax-object-rest-spread" "^7.2.0"
+
+"@babel/plugin-proposal-object-rest-spread@^7.7.7":
+  version "7.7.7"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.7.7.tgz#9f27075004ab99be08c5c1bd653a2985813cb370"
+  integrity sha512-3qp9I8lelgzNedI3hrhkvhaEYree6+WHnyA/q4Dza9z7iEIs1eyhWyJnetk3jJ69RT0AT4G0UhEGwyGFJ7GUuQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/plugin-syntax-object-rest-spread" "^7.7.4"
+
+"@babel/plugin-proposal-optional-catch-binding@^7.2.0":
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.2.0.tgz#135d81edb68a081e55e56ec48541ece8065c38f5"
+  integrity sha512-mgYj3jCcxug6KUcX4OBoOJz3CMrwRfQELPQ5560F70YQUBZB7uac9fqaWamKR1iWUzGiK2t0ygzjTScZnVz75g==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/plugin-syntax-optional-catch-binding" "^7.2.0"
+
+"@babel/plugin-proposal-optional-catch-binding@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.7.4.tgz#ec21e8aeb09ec6711bc0a39ca49520abee1de379"
+  integrity sha512-DyM7U2bnsQerCQ+sejcTNZh8KQEUuC3ufzdnVnSiUv/qoGJp2Z3hanKL18KDhsBT5Wj6a7CMT5mdyCNJsEaA9w==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/plugin-syntax-optional-catch-binding" "^7.7.4"
+
+"@babel/plugin-proposal-unicode-property-regex@^7.7.0":
+  version "7.7.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.7.0.tgz#549fe1717a1bd0a2a7e63163841cb37e78179d5d"
+  integrity sha512-mk34H+hp7kRBWJOOAR0ZMGCydgKMD4iN9TpDRp3IIcbunltxEY89XSimc6WbtSLCDrwcdy/EEw7h5CFCzxTchw==
+  dependencies:
+    "@babel/helper-create-regexp-features-plugin" "^7.7.0"
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-proposal-unicode-property-regex@^7.7.7":
+  version "7.7.7"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.7.7.tgz#433fa9dac64f953c12578b29633f456b68831c4e"
+  integrity sha512-80PbkKyORBUVm1fbTLrHpYdJxMThzM1UqFGh0ALEhO9TYbG86Ah9zQYAB/84axz2vcxefDLdZwWwZNlYARlu9w==
+  dependencies:
+    "@babel/helper-create-regexp-features-plugin" "^7.7.4"
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-syntax-async-generators@^7.2.0":
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.2.0.tgz#69e1f0db34c6f5a0cf7e2b3323bf159a76c8cb7f"
+  integrity sha512-1ZrIRBv2t0GSlcwVoQ6VgSLpLgiN/FVQUzt9znxo7v2Ov4jJrs8RY8tv0wvDmFN3qIdMKWrmMMW6yZ0G19MfGg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-syntax-async-generators@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.7.4.tgz#331aaf310a10c80c44a66b238b6e49132bd3c889"
+  integrity sha512-Li4+EjSpBgxcsmeEF8IFcfV/+yJGxHXDirDkEoyFjumuwbmfCVHUt0HuowD/iGM7OhIRyXJH9YXxqiH6N815+g==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-syntax-dynamic-import@^7.2.0":
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.2.0.tgz#69c159ffaf4998122161ad8ebc5e6d1f55df8612"
+  integrity sha512-mVxuJ0YroI/h/tbFTPGZR8cv6ai+STMKNBq0f8hFxsxWjl94qqhsb+wXbpNMDPU3cfR1TIsVFzU3nXyZMqyK4w==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-syntax-dynamic-import@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.7.4.tgz#29ca3b4415abfe4a5ec381e903862ad1a54c3aec"
+  integrity sha512-jHQW0vbRGvwQNgyVxwDh4yuXu4bH1f5/EICJLAhl1SblLs2CDhrsmCk+v5XLdE9wxtAFRyxx+P//Iw+a5L/tTg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-syntax-json-strings@^7.2.0":
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.2.0.tgz#72bd13f6ffe1d25938129d2a186b11fd62951470"
+  integrity sha512-5UGYnMSLRE1dqqZwug+1LISpA403HzlSfsg6P9VXU6TBjcSHeNlw4DxDx7LgpF+iKZoOG/+uzqoRHTdcUpiZNg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-syntax-json-strings@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.7.4.tgz#86e63f7d2e22f9e27129ac4e83ea989a382e86cc"
+  integrity sha512-QpGupahTQW1mHRXddMG5srgpHWqRLwJnJZKXTigB9RPFCCGbDGCgBeM/iC82ICXp414WeYx/tD54w7M2qRqTMg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-syntax-jsx@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.7.4.tgz#dab2b56a36fb6c3c222a1fbc71f7bf97f327a9ec"
+  integrity sha512-wuy6fiMe9y7HeZBWXYCGt2RGxZOj0BImZ9EyXJVnVGBKO/Br592rbR3rtIQn0eQhAk9vqaKP5n8tVqEFBQMfLg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-syntax-object-rest-spread@^7.2.0":
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.2.0.tgz#3b7a3e733510c57e820b9142a6579ac8b0dfad2e"
+  integrity sha512-t0JKGgqk2We+9may3t0xDdmneaXmyxq0xieYcKHxIsrJO64n1OiMWNUtc5gQK1PA0NpdCRrtZp4z+IUaKugrSA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-syntax-object-rest-spread@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.7.4.tgz#47cf220d19d6d0d7b154304701f468fc1cc6ff46"
+  integrity sha512-mObR+r+KZq0XhRVS2BrBKBpr5jqrqzlPvS9C9vuOf5ilSwzloAl7RPWLrgKdWS6IreaVrjHxTjtyqFiOisaCwg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-syntax-optional-catch-binding@^7.2.0":
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.2.0.tgz#a94013d6eda8908dfe6a477e7f9eda85656ecf5c"
+  integrity sha512-bDe4xKNhb0LI7IvZHiA13kff0KEfaGX/Hv4lMA9+7TEc63hMNvfKo6ZFpXhKuEp+II/q35Gc4NoMeDZyaUbj9w==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-syntax-optional-catch-binding@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.7.4.tgz#a3e38f59f4b6233867b4a92dcb0ee05b2c334aa6"
+  integrity sha512-4ZSuzWgFxqHRE31Glu+fEr/MirNZOMYmD/0BhBWyLyOOQz/gTAl7QmWm2hX1QxEIXsr2vkdlwxIzTyiYRC4xcQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-syntax-top-level-await@^7.7.0":
+  version "7.7.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.7.0.tgz#f5699549f50bbe8d12b1843a4e82f0a37bb65f4d"
+  integrity sha512-hi8FUNiFIY1fnUI2n1ViB1DR0R4QeK4iHcTlW6aJkrPoTdb8Rf1EMQ6GT3f67DDkYyWgew9DFoOZ6gOoEsdzTA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-syntax-top-level-await@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.7.4.tgz#bd7d8fa7b9fee793a36e4027fd6dd1aa32f946da"
+  integrity sha512-wdsOw0MvkL1UIgiQ/IFr3ETcfv1xb8RMM0H9wbiDyLaJFyiDg5oZvDLCXosIXmFeIlweML5iOBXAkqddkYNizg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-arrow-functions@^7.2.0":
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.2.0.tgz#9aeafbe4d6ffc6563bf8f8372091628f00779550"
+  integrity sha512-ER77Cax1+8/8jCB9fo4Ud161OZzWN5qawi4GusDuRLcDbDG+bIGYY20zb2dfAFdTRGzrfq2xZPvF0R64EHnimg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-arrow-functions@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.7.4.tgz#76309bd578addd8aee3b379d809c802305a98a12"
+  integrity sha512-zUXy3e8jBNPiffmqkHRNDdZM2r8DWhCB7HhcoyZjiK1TxYEluLHAvQuYnTT+ARqRpabWqy/NHkO6e3MsYB5YfA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-async-to-generator@^7.7.0":
+  version "7.7.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.7.0.tgz#e2b84f11952cf5913fe3438b7d2585042772f492"
+  integrity sha512-vLI2EFLVvRBL3d8roAMqtVY0Bm9C1QzLkdS57hiKrjUBSqsQYrBsMCeOg/0KK7B0eK9V71J5mWcha9yyoI2tZw==
+  dependencies:
+    "@babel/helper-module-imports" "^7.7.0"
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/helper-remap-async-to-generator" "^7.7.0"
+
+"@babel/plugin-transform-async-to-generator@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.7.4.tgz#694cbeae6d613a34ef0292713fa42fb45c4470ba"
+  integrity sha512-zpUTZphp5nHokuy8yLlyafxCJ0rSlFoSHypTUWgpdwoDXWQcseaect7cJ8Ppk6nunOM6+5rPMkod4OYKPR5MUg==
+  dependencies:
+    "@babel/helper-module-imports" "^7.7.4"
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/helper-remap-async-to-generator" "^7.7.4"
+
+"@babel/plugin-transform-block-scoped-functions@^7.2.0":
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.2.0.tgz#5d3cc11e8d5ddd752aa64c9148d0db6cb79fd190"
+  integrity sha512-ntQPR6q1/NKuphly49+QiQiTN0O63uOwjdD6dhIjSWBI5xlrbUFh720TIpzBhpnrLfv2tNH/BXvLIab1+BAI0w==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-block-scoped-functions@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.7.4.tgz#d0d9d5c269c78eaea76227ace214b8d01e4d837b"
+  integrity sha512-kqtQzwtKcpPclHYjLK//3lH8OFsCDuDJBaFhVwf8kqdnF6MN4l618UDlcA7TfRs3FayrHj+svYnSX8MC9zmUyQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-block-scoping@^7.6.3":
+  version "7.6.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.6.3.tgz#6e854e51fbbaa84351b15d4ddafe342f3a5d542a"
+  integrity sha512-7hvrg75dubcO3ZI2rjYTzUrEuh1E9IyDEhhB6qfcooxhDA33xx2MasuLVgdxzcP6R/lipAC6n9ub9maNW6RKdw==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+    lodash "^4.17.13"
+
+"@babel/plugin-transform-block-scoping@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.7.4.tgz#200aad0dcd6bb80372f94d9e628ea062c58bf224"
+  integrity sha512-2VBe9u0G+fDt9B5OV5DQH4KBf5DoiNkwFKOz0TCvBWvdAN2rOykCTkrL+jTLxfCAm76l9Qo5OqL7HBOx2dWggg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+    lodash "^4.17.13"
+
+"@babel/plugin-transform-classes@^7.7.0":
+  version "7.7.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.7.0.tgz#b411ecc1b8822d24b81e5d184f24149136eddd4a"
+  integrity sha512-/b3cKIZwGeUesZheU9jNYcwrEA7f/Bo4IdPmvp7oHgvks2majB5BoT5byAql44fiNQYOPzhk2w8DbgfuafkMoA==
+  dependencies:
+    "@babel/helper-annotate-as-pure" "^7.7.0"
+    "@babel/helper-define-map" "^7.7.0"
+    "@babel/helper-function-name" "^7.7.0"
+    "@babel/helper-optimise-call-expression" "^7.7.0"
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/helper-replace-supers" "^7.7.0"
+    "@babel/helper-split-export-declaration" "^7.7.0"
+    globals "^11.1.0"
+
+"@babel/plugin-transform-classes@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.7.4.tgz#c92c14be0a1399e15df72667067a8f510c9400ec"
+  integrity sha512-sK1mjWat7K+buWRuImEzjNf68qrKcrddtpQo3swi9j7dUcG6y6R6+Di039QN2bD1dykeswlagupEmpOatFHHUg==
+  dependencies:
+    "@babel/helper-annotate-as-pure" "^7.7.4"
+    "@babel/helper-define-map" "^7.7.4"
+    "@babel/helper-function-name" "^7.7.4"
+    "@babel/helper-optimise-call-expression" "^7.7.4"
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/helper-replace-supers" "^7.7.4"
+    "@babel/helper-split-export-declaration" "^7.7.4"
+    globals "^11.1.0"
+
+"@babel/plugin-transform-computed-properties@^7.2.0":
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.2.0.tgz#83a7df6a658865b1c8f641d510c6f3af220216da"
+  integrity sha512-kP/drqTxY6Xt3NNpKiMomfgkNn4o7+vKxK2DDKcBG9sHj51vHqMBGy8wbDS/J4lMxnqs153/T3+DmCEAkC5cpA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-computed-properties@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.7.4.tgz#e856c1628d3238ffe12d668eb42559f79a81910d"
+  integrity sha512-bSNsOsZnlpLLyQew35rl4Fma3yKWqK3ImWMSC/Nc+6nGjC9s5NFWAer1YQ899/6s9HxO2zQC1WoFNfkOqRkqRQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-destructuring@^7.6.0":
+  version "7.6.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.6.0.tgz#44bbe08b57f4480094d57d9ffbcd96d309075ba6"
+  integrity sha512-2bGIS5P1v4+sWTCnKNDZDxbGvEqi0ijeqM/YqHtVGrvG2y0ySgnEEhXErvE9dA0bnIzY9bIzdFK0jFA46ASIIQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-destructuring@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.7.4.tgz#2b713729e5054a1135097b6a67da1b6fe8789267"
+  integrity sha512-4jFMXI1Cu2aXbcXXl8Lr6YubCn6Oc7k9lLsu8v61TZh+1jny2BWmdtvY9zSUlLdGUvcy9DMAWyZEOqjsbeg/wA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-dotall-regex@^7.7.0":
+  version "7.7.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.7.0.tgz#c5c9ecacab3a5e0c11db6981610f0c32fd698b3b"
+  integrity sha512-3QQlF7hSBnSuM1hQ0pS3pmAbWLax/uGNCbPBND9y+oJ4Y776jsyujG2k0Sn2Aj2a0QwVOiOFL5QVPA7spjvzSA==
+  dependencies:
+    "@babel/helper-create-regexp-features-plugin" "^7.7.0"
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-dotall-regex@^7.7.7":
+  version "7.7.7"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.7.7.tgz#3e9713f1b69f339e87fa796b097d73ded16b937b"
+  integrity sha512-b4in+YlTeE/QmTgrllnb3bHA0HntYvjz8O3Mcbx75UBPJA2xhb5A8nle498VhxSXJHQefjtQxpnLPehDJ4TRlg==
+  dependencies:
+    "@babel/helper-create-regexp-features-plugin" "^7.7.4"
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-duplicate-keys@^7.5.0":
+  version "7.5.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.5.0.tgz#c5dbf5106bf84cdf691222c0974c12b1df931853"
+  integrity sha512-igcziksHizyQPlX9gfSjHkE2wmoCH3evvD2qR5w29/Dk0SMKE/eOI7f1HhBdNhR/zxJDqrgpoDTq5YSLH/XMsQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-duplicate-keys@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.7.4.tgz#3d21731a42e3f598a73835299dd0169c3b90ac91"
+  integrity sha512-g1y4/G6xGWMD85Tlft5XedGaZBCIVN+/P0bs6eabmcPP9egFleMAo65OOjlhcz1njpwagyY3t0nsQC9oTFegJA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-exponentiation-operator@^7.2.0":
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.2.0.tgz#a63868289e5b4007f7054d46491af51435766008"
+  integrity sha512-umh4hR6N7mu4Elq9GG8TOu9M0bakvlsREEC+ialrQN6ABS4oDQ69qJv1VtR3uxlKMCQMCvzk7vr17RHKcjx68A==
+  dependencies:
+    "@babel/helper-builder-binary-assignment-operator-visitor" "^7.1.0"
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-exponentiation-operator@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.7.4.tgz#dd30c0191e3a1ba19bcc7e389bdfddc0729d5db9"
+  integrity sha512-MCqiLfCKm6KEA1dglf6Uqq1ElDIZwFuzz1WH5mTf8k2uQSxEJMbOIEh7IZv7uichr7PMfi5YVSrr1vz+ipp7AQ==
+  dependencies:
+    "@babel/helper-builder-binary-assignment-operator-visitor" "^7.7.4"
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-for-of@^7.4.4":
+  version "7.4.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.4.4.tgz#0267fc735e24c808ba173866c6c4d1440fc3c556"
+  integrity sha512-9T/5Dlr14Z9TIEXLXkt8T1DU7F24cbhwhMNUziN3hB1AXoZcdzPcTiKGRn/6iOymDqtTKWnr/BtRKN9JwbKtdQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-for-of@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.7.4.tgz#248800e3a5e507b1f103d8b4ca998e77c63932bc"
+  integrity sha512-zZ1fD1B8keYtEcKF+M1TROfeHTKnijcVQm0yO/Yu1f7qoDoxEIc/+GX6Go430Bg84eM/xwPFp0+h4EbZg7epAA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-function-name@^7.7.0":
+  version "7.7.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.7.0.tgz#0fa786f1eef52e3b7d4fc02e54b2129de8a04c2a"
+  integrity sha512-P5HKu0d9+CzZxP5jcrWdpe7ZlFDe24bmqP6a6X8BHEBl/eizAsY8K6LX8LASZL0Jxdjm5eEfzp+FIrxCm/p8bA==
+  dependencies:
+    "@babel/helper-function-name" "^7.7.0"
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-function-name@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.7.4.tgz#75a6d3303d50db638ff8b5385d12451c865025b1"
+  integrity sha512-E/x09TvjHNhsULs2IusN+aJNRV5zKwxu1cpirZyRPw+FyyIKEHPXTsadj48bVpc1R5Qq1B5ZkzumuFLytnbT6g==
+  dependencies:
+    "@babel/helper-function-name" "^7.7.4"
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-literals@^7.2.0":
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.2.0.tgz#690353e81f9267dad4fd8cfd77eafa86aba53ea1"
+  integrity sha512-2ThDhm4lI4oV7fVQ6pNNK+sx+c/GM5/SaML0w/r4ZB7sAneD/piDJtwdKlNckXeyGK7wlwg2E2w33C/Hh+VFCg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-literals@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.7.4.tgz#27fe87d2b5017a2a5a34d1c41a6b9f6a6262643e"
+  integrity sha512-X2MSV7LfJFm4aZfxd0yLVFrEXAgPqYoDG53Br/tCKiKYfX0MjVjQeWPIhPHHsCqzwQANq+FLN786fF5rgLS+gw==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-member-expression-literals@^7.2.0":
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.2.0.tgz#fa10aa5c58a2cb6afcf2c9ffa8cb4d8b3d489a2d"
+  integrity sha512-HiU3zKkSU6scTidmnFJ0bMX8hz5ixC93b4MHMiYebmk2lUVNGOboPsqQvx5LzooihijUoLR/v7Nc1rbBtnc7FA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-member-expression-literals@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.7.4.tgz#aee127f2f3339fc34ce5e3055d7ffbf7aa26f19a"
+  integrity sha512-9VMwMO7i69LHTesL0RdGy93JU6a+qOPuvB4F4d0kR0zyVjJRVJRaoaGjhtki6SzQUu8yen/vxPKN6CWnCUw6bA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-modules-amd@^7.5.0":
+  version "7.5.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.5.0.tgz#ef00435d46da0a5961aa728a1d2ecff063e4fb91"
+  integrity sha512-n20UsQMKnWrltocZZm24cRURxQnWIvsABPJlw/fvoy9c6AgHZzoelAIzajDHAQrDpuKFFPPcFGd7ChsYuIUMpg==
+  dependencies:
+    "@babel/helper-module-transforms" "^7.1.0"
+    "@babel/helper-plugin-utils" "^7.0.0"
+    babel-plugin-dynamic-import-node "^2.3.0"
+
+"@babel/plugin-transform-modules-amd@^7.7.5":
+  version "7.7.5"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.7.5.tgz#39e0fb717224b59475b306402bb8eedab01e729c"
+  integrity sha512-CT57FG4A2ZUNU1v+HdvDSDrjNWBrtCmSH6YbbgN3Lrf0Di/q/lWRxZrE72p3+HCCz9UjfZOEBdphgC0nzOS6DQ==
+  dependencies:
+    "@babel/helper-module-transforms" "^7.7.5"
+    "@babel/helper-plugin-utils" "^7.0.0"
+    babel-plugin-dynamic-import-node "^2.3.0"
+
+"@babel/plugin-transform-modules-commonjs@^7.7.0":
+  version "7.7.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.7.0.tgz#3e5ffb4fd8c947feede69cbe24c9554ab4113fe3"
+  integrity sha512-KEMyWNNWnjOom8vR/1+d+Ocz/mILZG/eyHHO06OuBQ2aNhxT62fr4y6fGOplRx+CxCSp3IFwesL8WdINfY/3kg==
+  dependencies:
+    "@babel/helper-module-transforms" "^7.7.0"
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/helper-simple-access" "^7.7.0"
+    babel-plugin-dynamic-import-node "^2.3.0"
+
+"@babel/plugin-transform-modules-commonjs@^7.7.5":
+  version "7.7.5"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.7.5.tgz#1d27f5eb0bcf7543e774950e5b2fa782e637b345"
+  integrity sha512-9Cq4zTFExwFhQI6MT1aFxgqhIsMWQWDVwOgLzl7PTWJHsNaqFvklAU+Oz6AQLAS0dJKTwZSOCo20INwktxpi3Q==
+  dependencies:
+    "@babel/helper-module-transforms" "^7.7.5"
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/helper-simple-access" "^7.7.4"
+    babel-plugin-dynamic-import-node "^2.3.0"
+
+"@babel/plugin-transform-modules-systemjs@^7.7.0":
+  version "7.7.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.7.0.tgz#9baf471213af9761c1617bb12fd278e629041417"
+  integrity sha512-ZAuFgYjJzDNv77AjXRqzQGlQl4HdUM6j296ee4fwKVZfhDR9LAGxfvXjBkb06gNETPnN0sLqRm9Gxg4wZH6dXg==
+  dependencies:
+    "@babel/helper-hoist-variables" "^7.7.0"
+    "@babel/helper-plugin-utils" "^7.0.0"
+    babel-plugin-dynamic-import-node "^2.3.0"
+
+"@babel/plugin-transform-modules-systemjs@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.7.4.tgz#cd98152339d3e763dfe838b7d4273edaf520bb30"
+  integrity sha512-y2c96hmcsUi6LrMqvmNDPBBiGCiQu0aYqpHatVVu6kD4mFEXKjyNxd/drc18XXAf9dv7UXjrZwBVmTTGaGP8iw==
+  dependencies:
+    "@babel/helper-hoist-variables" "^7.7.4"
+    "@babel/helper-plugin-utils" "^7.0.0"
+    babel-plugin-dynamic-import-node "^2.3.0"
+
+"@babel/plugin-transform-modules-umd@^7.7.0":
+  version "7.7.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.7.0.tgz#d62c7da16670908e1d8c68ca0b5d4c0097b69966"
+  integrity sha512-u7eBA03zmUswQ9LQ7Qw0/ieC1pcAkbp5OQatbWUzY1PaBccvuJXUkYzoN1g7cqp7dbTu6Dp9bXyalBvD04AANA==
+  dependencies:
+    "@babel/helper-module-transforms" "^7.7.0"
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-modules-umd@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.7.4.tgz#1027c355a118de0aae9fee00ad7813c584d9061f"
+  integrity sha512-u2B8TIi0qZI4j8q4C51ktfO7E3cQ0qnaXFI1/OXITordD40tt17g/sXqgNNCcMTcBFKrUPcGDx+TBJuZxLx7tw==
+  dependencies:
+    "@babel/helper-module-transforms" "^7.7.4"
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-named-capturing-groups-regex@^7.7.0":
+  version "7.7.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.7.0.tgz#358e6fd869b9a4d8f5cbc79e4ed4fc340e60dcaf"
+  integrity sha512-+SicSJoKouPctL+j1pqktRVCgy+xAch1hWWTMy13j0IflnyNjaoskj+DwRQFimHbLqO3sq2oN2CXMvXq3Bgapg==
+  dependencies:
+    "@babel/helper-create-regexp-features-plugin" "^7.7.0"
+
+"@babel/plugin-transform-named-capturing-groups-regex@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.7.4.tgz#fb3bcc4ee4198e7385805007373d6b6f42c98220"
+  integrity sha512-jBUkiqLKvUWpv9GLSuHUFYdmHg0ujC1JEYoZUfeOOfNydZXp1sXObgyPatpcwjWgsdBGsagWW0cdJpX/DO2jMw==
+  dependencies:
+    "@babel/helper-create-regexp-features-plugin" "^7.7.4"
+
+"@babel/plugin-transform-new-target@^7.4.4":
+  version "7.4.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.4.4.tgz#18d120438b0cc9ee95a47f2c72bc9768fbed60a5"
+  integrity sha512-r1z3T2DNGQwwe2vPGZMBNjioT2scgWzK9BCnDEh+46z8EEwXBq24uRzd65I7pjtugzPSj921aM15RpESgzsSuA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-new-target@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.7.4.tgz#4a0753d2d60639437be07b592a9e58ee00720167"
+  integrity sha512-CnPRiNtOG1vRodnsyGX37bHQleHE14B9dnnlgSeEs3ek3fHN1A1SScglTCg1sfbe7sRQ2BUcpgpTpWSfMKz3gg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-object-super@^7.5.5":
+  version "7.5.5"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.5.5.tgz#c70021df834073c65eb613b8679cc4a381d1a9f9"
+  integrity sha512-un1zJQAhSosGFBduPgN/YFNvWVpRuHKU7IHBglLoLZsGmruJPOo6pbInneflUdmq7YvSVqhpPs5zdBvLnteltQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/helper-replace-supers" "^7.5.5"
+
+"@babel/plugin-transform-object-super@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.7.4.tgz#48488937a2d586c0148451bf51af9d7dda567262"
+  integrity sha512-ho+dAEhC2aRnff2JCA0SAK7V2R62zJd/7dmtoe7MHcso4C2mS+vZjn1Pb1pCVZvJs1mgsvv5+7sT+m3Bysb6eg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/helper-replace-supers" "^7.7.4"
+
+"@babel/plugin-transform-parameters@^7.4.4":
+  version "7.4.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.4.4.tgz#7556cf03f318bd2719fe4c922d2d808be5571e16"
+  integrity sha512-oMh5DUO1V63nZcu/ZVLQFqiihBGo4OpxJxR1otF50GMeCLiRx5nUdtokd+u9SuVJrvvuIh9OosRFPP4pIPnwmw==
+  dependencies:
+    "@babel/helper-call-delegate" "^7.4.4"
+    "@babel/helper-get-function-arity" "^7.0.0"
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-parameters@^7.7.7":
+  version "7.7.7"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.7.7.tgz#7a884b2460164dc5f194f668332736584c760007"
+  integrity sha512-OhGSrf9ZBrr1fw84oFXj5hgi8Nmg+E2w5L7NhnG0lPvpDtqd7dbyilM2/vR8CKbJ907RyxPh2kj6sBCSSfI9Ew==
+  dependencies:
+    "@babel/helper-call-delegate" "^7.7.4"
+    "@babel/helper-get-function-arity" "^7.7.4"
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-property-literals@^7.2.0":
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.2.0.tgz#03e33f653f5b25c4eb572c98b9485055b389e905"
+  integrity sha512-9q7Dbk4RhgcLp8ebduOpCbtjh7C0itoLYHXd9ueASKAG/is5PQtMR5VJGka9NKqGhYEGn5ITahd4h9QeBMylWQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-property-literals@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.7.4.tgz#2388d6505ef89b266103f450f9167e6bd73f98c2"
+  integrity sha512-MatJhlC4iHsIskWYyawl53KuHrt+kALSADLQQ/HkhTjX954fkxIEh4q5slL4oRAnsm/eDoZ4q0CIZpcqBuxhJQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-react-display-name@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.7.4.tgz#9f2b80b14ebc97eef4a9b29b612c58ed9c0d10dd"
+  integrity sha512-sBbIvqYkthai0X0vkD2xsAwluBp+LtNHH+/V4a5ydifmTtb8KOVOlrMIk/MYmIc4uTYDnjZUHQildYNo36SRJw==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-react-jsx-self@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.7.4.tgz#81b8fbfd14b2215e8f1c2c3adfba266127b0231c"
+  integrity sha512-PWYjSfqrO273mc1pKCRTIJXyqfc9vWYBax88yIhQb+bpw3XChVC7VWS4VwRVs63wFHKxizvGSd00XEr+YB9Q2A==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/plugin-syntax-jsx" "^7.7.4"
+
+"@babel/plugin-transform-react-jsx-source@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.7.4.tgz#8994b1bf6014b133f5a46d3b7d1ee5f5e3e72c10"
+  integrity sha512-5ZU9FnPhqtHsOXxutRtXZAzoEJwDaP32QcobbMP1/qt7NYcsCNK8XgzJcJfoEr/ZnzVvUNInNjIW22Z6I8p9mg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/plugin-syntax-jsx" "^7.7.4"
+
+"@babel/plugin-transform-react-jsx@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.7.4.tgz#d91205717fae4e2f84d020cd3057ec02a10f11da"
+  integrity sha512-LixU4BS95ZTEAZdPaIuyg/k8FiiqN9laQ0dMHB4MlpydHY53uQdWCUrwjLr5o6ilS6fAgZey4Q14XBjl5tL6xw==
+  dependencies:
+    "@babel/helper-builder-react-jsx" "^7.7.4"
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/plugin-syntax-jsx" "^7.7.4"
+
+"@babel/plugin-transform-regenerator@^7.7.0":
+  version "7.7.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.7.0.tgz#f1b20b535e7716b622c99e989259d7dd942dd9cc"
+  integrity sha512-AXmvnC+0wuj/cFkkS/HFHIojxH3ffSXE+ttulrqWjZZRaUOonfJc60e1wSNT4rV8tIunvu/R3wCp71/tLAa9xg==
+  dependencies:
+    regenerator-transform "^0.14.0"
+
+"@babel/plugin-transform-regenerator@^7.7.5":
+  version "7.7.5"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.7.5.tgz#3a8757ee1a2780f390e89f246065ecf59c26fce9"
+  integrity sha512-/8I8tPvX2FkuEyWbjRCt4qTAgZK0DVy8QRguhA524UH48RfGJy94On2ri+dCuwOpcerPRl9O4ebQkRcVzIaGBw==
+  dependencies:
+    regenerator-transform "^0.14.0"
+
+"@babel/plugin-transform-reserved-words@^7.2.0":
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.2.0.tgz#4792af87c998a49367597d07fedf02636d2e1634"
+  integrity sha512-fz43fqW8E1tAB3DKF19/vxbpib1fuyCwSPE418ge5ZxILnBhWyhtPgz8eh1RCGGJlwvksHkyxMxh0eenFi+kFw==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-reserved-words@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.7.4.tgz#6a7cf123ad175bb5c69aec8f6f0770387ed3f1eb"
+  integrity sha512-OrPiUB5s5XvkCO1lS7D8ZtHcswIC57j62acAnJZKqGGnHP+TIc/ljQSrgdX/QyOTdEK5COAhuc820Hi1q2UgLQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-shorthand-properties@^7.2.0":
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.2.0.tgz#6333aee2f8d6ee7e28615457298934a3b46198f0"
+  integrity sha512-QP4eUM83ha9zmYtpbnyjTLAGKQritA5XW/iG9cjtuOI8s1RuL/3V6a3DeSHfKutJQ+ayUfeZJPcnCYEQzaPQqg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-shorthand-properties@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.7.4.tgz#74a0a9b2f6d67a684c6fbfd5f0458eb7ba99891e"
+  integrity sha512-q+suddWRfIcnyG5YiDP58sT65AJDZSUhXQDZE3r04AuqD6d/XLaQPPXSBzP2zGerkgBivqtQm9XKGLuHqBID6Q==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-spread@^7.6.2":
+  version "7.6.2"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.6.2.tgz#fc77cf798b24b10c46e1b51b1b88c2bf661bb8dd"
+  integrity sha512-DpSvPFryKdK1x+EDJYCy28nmAaIMdxmhot62jAXF/o99iA33Zj2Lmcp3vDmz+MUh0LNYVPvfj5iC3feb3/+PFg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-spread@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.7.4.tgz#aa673b356fe6b7e70d69b6e33a17fef641008578"
+  integrity sha512-8OSs0FLe5/80cndziPlg4R0K6HcWSM0zyNhHhLsmw/Nc5MaA49cAsnoJ/t/YZf8qkG7fD+UjTRaApVDB526d7Q==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-sticky-regex@^7.2.0":
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.2.0.tgz#a1e454b5995560a9c1e0d537dfc15061fd2687e1"
+  integrity sha512-KKYCoGaRAf+ckH8gEL3JHUaFVyNHKe3ASNsZ+AlktgHevvxGigoIttrEJb8iKN03Q7Eazlv1s6cx2B2cQ3Jabw==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/helper-regex" "^7.0.0"
+
+"@babel/plugin-transform-sticky-regex@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.7.4.tgz#ffb68c05090c30732076b1285dc1401b404a123c"
+  integrity sha512-Ls2NASyL6qtVe1H1hXts9yuEeONV2TJZmplLONkMPUG158CtmnrzW5Q5teibM5UVOFjG0D3IC5mzXR6pPpUY7A==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/helper-regex" "^7.0.0"
+
+"@babel/plugin-transform-template-literals@^7.4.4":
+  version "7.4.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.4.4.tgz#9d28fea7bbce637fb7612a0750989d8321d4bcb0"
+  integrity sha512-mQrEC4TWkhLN0z8ygIvEL9ZEToPhG5K7KDW3pzGqOfIGZ28Jb0POUkeWcoz8HnHvhFy6dwAT1j8OzqN8s804+g==
+  dependencies:
+    "@babel/helper-annotate-as-pure" "^7.0.0"
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-template-literals@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.7.4.tgz#1eb6411736dd3fe87dbd20cc6668e5121c17d604"
+  integrity sha512-sA+KxLwF3QwGj5abMHkHgshp9+rRz+oY9uoRil4CyLtgEuE/88dpkeWgNk5qKVsJE9iSfly3nvHapdRiIS2wnQ==
+  dependencies:
+    "@babel/helper-annotate-as-pure" "^7.7.4"
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-typeof-symbol@^7.2.0":
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.2.0.tgz#117d2bcec2fbf64b4b59d1f9819894682d29f2b2"
+  integrity sha512-2LNhETWYxiYysBtrBTqL8+La0jIoQQnIScUJc74OYvUGRmkskNY4EzLCnjHBzdmb38wqtTaixpo1NctEcvMDZw==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-typeof-symbol@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.7.4.tgz#3174626214f2d6de322882e498a38e8371b2140e"
+  integrity sha512-KQPUQ/7mqe2m0B8VecdyaW5XcQYaePyl9R7IsKd+irzj6jvbhoGnRE+M0aNkyAzI07VfUQ9266L5xMARitV3wg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-unicode-regex@^7.7.0":
+  version "7.7.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.7.0.tgz#743d9bcc44080e3cc7d49259a066efa30f9187a3"
+  integrity sha512-RrThb0gdrNwFAqEAAx9OWgtx6ICK69x7i9tCnMdVrxQwSDp/Abu9DXFU5Hh16VP33Rmxh04+NGW28NsIkFvFKA==
+  dependencies:
+    "@babel/helper-create-regexp-features-plugin" "^7.7.0"
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-unicode-regex@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.7.4.tgz#a3c0f65b117c4c81c5b6484f2a5e7b95346b83ae"
+  integrity sha512-N77UUIV+WCvE+5yHw+oks3m18/umd7y392Zv7mYTpFqHtkpcc+QUz+gLJNTWVlWROIWeLqY0f3OjZxV5TcXnRw==
+  dependencies:
+    "@babel/helper-create-regexp-features-plugin" "^7.7.4"
+    "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/preset-env@^7.5.3":
+  version "7.7.1"
+  resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.7.1.tgz#04a2ff53552c5885cf1083e291c8dd5490f744bb"
+  integrity sha512-/93SWhi3PxcVTDpSqC+Dp4YxUu3qZ4m7I76k0w73wYfn7bGVuRIO4QUz95aJksbS+AD1/mT1Ie7rbkT0wSplaA==
+  dependencies:
+    "@babel/helper-module-imports" "^7.7.0"
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/plugin-proposal-async-generator-functions" "^7.7.0"
+    "@babel/plugin-proposal-dynamic-import" "^7.7.0"
+    "@babel/plugin-proposal-json-strings" "^7.2.0"
+    "@babel/plugin-proposal-object-rest-spread" "^7.6.2"
+    "@babel/plugin-proposal-optional-catch-binding" "^7.2.0"
+    "@babel/plugin-proposal-unicode-property-regex" "^7.7.0"
+    "@babel/plugin-syntax-async-generators" "^7.2.0"
+    "@babel/plugin-syntax-dynamic-import" "^7.2.0"
+    "@babel/plugin-syntax-json-strings" "^7.2.0"
+    "@babel/plugin-syntax-object-rest-spread" "^7.2.0"
+    "@babel/plugin-syntax-optional-catch-binding" "^7.2.0"
+    "@babel/plugin-syntax-top-level-await" "^7.7.0"
+    "@babel/plugin-transform-arrow-functions" "^7.2.0"
+    "@babel/plugin-transform-async-to-generator" "^7.7.0"
+    "@babel/plugin-transform-block-scoped-functions" "^7.2.0"
+    "@babel/plugin-transform-block-scoping" "^7.6.3"
+    "@babel/plugin-transform-classes" "^7.7.0"
+    "@babel/plugin-transform-computed-properties" "^7.2.0"
+    "@babel/plugin-transform-destructuring" "^7.6.0"
+    "@babel/plugin-transform-dotall-regex" "^7.7.0"
+    "@babel/plugin-transform-duplicate-keys" "^7.5.0"
+    "@babel/plugin-transform-exponentiation-operator" "^7.2.0"
+    "@babel/plugin-transform-for-of" "^7.4.4"
+    "@babel/plugin-transform-function-name" "^7.7.0"
+    "@babel/plugin-transform-literals" "^7.2.0"
+    "@babel/plugin-transform-member-expression-literals" "^7.2.0"
+    "@babel/plugin-transform-modules-amd" "^7.5.0"
+    "@babel/plugin-transform-modules-commonjs" "^7.7.0"
+    "@babel/plugin-transform-modules-systemjs" "^7.7.0"
+    "@babel/plugin-transform-modules-umd" "^7.7.0"
+    "@babel/plugin-transform-named-capturing-groups-regex" "^7.7.0"
+    "@babel/plugin-transform-new-target" "^7.4.4"
+    "@babel/plugin-transform-object-super" "^7.5.5"
+    "@babel/plugin-transform-parameters" "^7.4.4"
+    "@babel/plugin-transform-property-literals" "^7.2.0"
+    "@babel/plugin-transform-regenerator" "^7.7.0"
+    "@babel/plugin-transform-reserved-words" "^7.2.0"
+    "@babel/plugin-transform-shorthand-properties" "^7.2.0"
+    "@babel/plugin-transform-spread" "^7.6.2"
+    "@babel/plugin-transform-sticky-regex" "^7.2.0"
+    "@babel/plugin-transform-template-literals" "^7.4.4"
+    "@babel/plugin-transform-typeof-symbol" "^7.2.0"
+    "@babel/plugin-transform-unicode-regex" "^7.7.0"
+    "@babel/types" "^7.7.1"
+    browserslist "^4.6.0"
+    core-js-compat "^3.1.1"
+    invariant "^2.2.2"
+    js-levenshtein "^1.1.3"
+    semver "^5.5.0"
+
+"@babel/preset-env@^7.7.7":
+  version "7.7.7"
+  resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.7.7.tgz#c294167b91e53e7e36d820e943ece8d0c7fe46ac"
+  integrity sha512-pCu0hrSSDVI7kCVUOdcMNQEbOPJ52E+LrQ14sN8uL2ALfSqePZQlKrOy+tM4uhEdYlCHi4imr8Zz2cZe9oSdIg==
+  dependencies:
+    "@babel/helper-module-imports" "^7.7.4"
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/plugin-proposal-async-generator-functions" "^7.7.4"
+    "@babel/plugin-proposal-dynamic-import" "^7.7.4"
+    "@babel/plugin-proposal-json-strings" "^7.7.4"
+    "@babel/plugin-proposal-object-rest-spread" "^7.7.7"
+    "@babel/plugin-proposal-optional-catch-binding" "^7.7.4"
+    "@babel/plugin-proposal-unicode-property-regex" "^7.7.7"
+    "@babel/plugin-syntax-async-generators" "^7.7.4"
+    "@babel/plugin-syntax-dynamic-import" "^7.7.4"
+    "@babel/plugin-syntax-json-strings" "^7.7.4"
+    "@babel/plugin-syntax-object-rest-spread" "^7.7.4"
+    "@babel/plugin-syntax-optional-catch-binding" "^7.7.4"
+    "@babel/plugin-syntax-top-level-await" "^7.7.4"
+    "@babel/plugin-transform-arrow-functions" "^7.7.4"
+    "@babel/plugin-transform-async-to-generator" "^7.7.4"
+    "@babel/plugin-transform-block-scoped-functions" "^7.7.4"
+    "@babel/plugin-transform-block-scoping" "^7.7.4"
+    "@babel/plugin-transform-classes" "^7.7.4"
+    "@babel/plugin-transform-computed-properties" "^7.7.4"
+    "@babel/plugin-transform-destructuring" "^7.7.4"
+    "@babel/plugin-transform-dotall-regex" "^7.7.7"
+    "@babel/plugin-transform-duplicate-keys" "^7.7.4"
+    "@babel/plugin-transform-exponentiation-operator" "^7.7.4"
+    "@babel/plugin-transform-for-of" "^7.7.4"
+    "@babel/plugin-transform-function-name" "^7.7.4"
+    "@babel/plugin-transform-literals" "^7.7.4"
+    "@babel/plugin-transform-member-expression-literals" "^7.7.4"
+    "@babel/plugin-transform-modules-amd" "^7.7.5"
+    "@babel/plugin-transform-modules-commonjs" "^7.7.5"
+    "@babel/plugin-transform-modules-systemjs" "^7.7.4"
+    "@babel/plugin-transform-modules-umd" "^7.7.4"
+    "@babel/plugin-transform-named-capturing-groups-regex" "^7.7.4"
+    "@babel/plugin-transform-new-target" "^7.7.4"
+    "@babel/plugin-transform-object-super" "^7.7.4"
+    "@babel/plugin-transform-parameters" "^7.7.7"
+    "@babel/plugin-transform-property-literals" "^7.7.4"
+    "@babel/plugin-transform-regenerator" "^7.7.5"
+    "@babel/plugin-transform-reserved-words" "^7.7.4"
+    "@babel/plugin-transform-shorthand-properties" "^7.7.4"
+    "@babel/plugin-transform-spread" "^7.7.4"
+    "@babel/plugin-transform-sticky-regex" "^7.7.4"
+    "@babel/plugin-transform-template-literals" "^7.7.4"
+    "@babel/plugin-transform-typeof-symbol" "^7.7.4"
+    "@babel/plugin-transform-unicode-regex" "^7.7.4"
+    "@babel/types" "^7.7.4"
+    browserslist "^4.6.0"
+    core-js-compat "^3.6.0"
+    invariant "^2.2.2"
+    js-levenshtein "^1.1.3"
+    semver "^5.5.0"
+
+"@babel/preset-react@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.7.4.tgz#3fe2ea698d8fb536d8e7881a592c3c1ee8bf5707"
+  integrity sha512-j+vZtg0/8pQr1H8wKoaJyGL2IEk3rG/GIvua7Sec7meXVIvGycihlGMx5xcU00kqCJbwzHs18xTu3YfREOqQ+g==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/plugin-transform-react-display-name" "^7.7.4"
+    "@babel/plugin-transform-react-jsx" "^7.7.4"
+    "@babel/plugin-transform-react-jsx-self" "^7.7.4"
+    "@babel/plugin-transform-react-jsx-source" "^7.7.4"
+
+"@babel/runtime@^7.6.3":
+  version "7.7.2"
+  resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.7.2.tgz#111a78002a5c25fc8e3361bedc9529c696b85a6a"
+  integrity sha512-JONRbXbTXc9WQE2mAZd1p0Z3DZ/6vaQIkgYMSTP3KjRCyd7rCZCcfhCyX+YjwcKxcZ82UrxbRD358bpExNgrjw==
+  dependencies:
+    regenerator-runtime "^0.13.2"
+
+"@babel/template@^7.7.0":
+  version "7.7.0"
+  resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.7.0.tgz#4fadc1b8e734d97f56de39c77de76f2562e597d0"
+  integrity sha512-OKcwSYOW1mhWbnTBgQY5lvg1Fxg+VyfQGjcBduZFljfc044J5iDlnDSfhQ867O17XHiSCxYHUxHg2b7ryitbUQ==
+  dependencies:
+    "@babel/code-frame" "^7.0.0"
+    "@babel/parser" "^7.7.0"
+    "@babel/types" "^7.7.0"
+
+"@babel/template@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.7.4.tgz#428a7d9eecffe27deac0a98e23bf8e3675d2a77b"
+  integrity sha512-qUzihgVPguAzXCK7WXw8pqs6cEwi54s3E+HrejlkuWO6ivMKx9hZl3Y2fSXp9i5HgyWmj7RKP+ulaYnKM4yYxw==
+  dependencies:
+    "@babel/code-frame" "^7.0.0"
+    "@babel/parser" "^7.7.4"
+    "@babel/types" "^7.7.4"
+
+"@babel/traverse@^7.7.0", "@babel/traverse@^7.7.2":
+  version "7.7.2"
+  resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.7.2.tgz#ef0a65e07a2f3c550967366b3d9b62a2dcbeae09"
+  integrity sha512-TM01cXib2+rgIZrGJOLaHV/iZUAxf4A0dt5auY6KNZ+cm6aschuJGqKJM3ROTt3raPUdIDk9siAufIFEleRwtw==
+  dependencies:
+    "@babel/code-frame" "^7.5.5"
+    "@babel/generator" "^7.7.2"
+    "@babel/helper-function-name" "^7.7.0"
+    "@babel/helper-split-export-declaration" "^7.7.0"
+    "@babel/parser" "^7.7.2"
+    "@babel/types" "^7.7.2"
+    debug "^4.1.0"
+    globals "^11.1.0"
+    lodash "^4.17.13"
+
+"@babel/traverse@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.7.4.tgz#9c1e7c60fb679fe4fcfaa42500833333c2058558"
+  integrity sha512-P1L58hQyupn8+ezVA2z5KBm4/Zr4lCC8dwKCMYzsa5jFMDMQAzaBNy9W5VjB+KAmBjb40U7a/H6ao+Xo+9saIw==
+  dependencies:
+    "@babel/code-frame" "^7.5.5"
+    "@babel/generator" "^7.7.4"
+    "@babel/helper-function-name" "^7.7.4"
+    "@babel/helper-split-export-declaration" "^7.7.4"
+    "@babel/parser" "^7.7.4"
+    "@babel/types" "^7.7.4"
+    debug "^4.1.0"
+    globals "^11.1.0"
+    lodash "^4.17.13"
+
+"@babel/types@7.0.0-beta.35":
+  version "7.0.0-beta.35"
+  resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.0.0-beta.35.tgz#cf933a9a9a38484ca724b335b88d83726d5ab960"
+  integrity sha512-y9XT11CozHDgjWcTdxmhSj13rJVXpa5ZXwjjOiTedjaM0ba5ItqdS02t31EhPl7HtOWxsZkYCCUNrSfrOisA6w==
+  dependencies:
+    esutils "^2.0.2"
+    lodash "^4.2.0"
+    to-fast-properties "^2.0.0"
+
+"@babel/types@^7.7.0", "@babel/types@^7.7.1", "@babel/types@^7.7.2":
+  version "7.7.2"
+  resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.7.2.tgz#550b82e5571dcd174af576e23f0adba7ffc683f7"
+  integrity sha512-YTf6PXoh3+eZgRCBzzP25Bugd2ngmpQVrk7kXX0i5N9BO7TFBtIgZYs7WtxtOGs8e6A4ZI7ECkbBCEHeXocvOA==
+  dependencies:
+    esutils "^2.0.2"
+    lodash "^4.17.13"
+    to-fast-properties "^2.0.0"
+
+"@babel/types@^7.7.4":
+  version "7.7.4"
+  resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.7.4.tgz#516570d539e44ddf308c07569c258ff94fde9193"
+  integrity sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==
+  dependencies:
+    esutils "^2.0.2"
+    lodash "^4.17.13"
+    to-fast-properties "^2.0.0"
+
+"@develar/schema-utils@~2.1.0":
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/@develar/schema-utils/-/schema-utils-2.1.0.tgz#eceb1695bfbed6f6bb84666d5d3abe5e1fd54e17"
+  integrity sha512-qjCqB4ctMig9Gz5bd6lkdFr3bO6arOdQqptdBSpF1ZpCnjofieCciEzkoS9ujY9cMGyllYSCSmBJ3x9OKHXzoA==
+  dependencies:
+    ajv "^6.1.0"
+    ajv-keywords "^3.1.0"
+
+"@electron/get@^1.0.1":
+  version "1.6.0"
+  resolved "https://registry.yarnpkg.com/@electron/get/-/get-1.6.0.tgz#3e1437fa0f436ce9d4e0c27a4c330835ae6d74ed"
+  integrity sha512-xuvAzbN9iBApfAMvW0hKUpxHR5wPVbG9RaoSTbpu/WaHISDu0MVfMWYhfeU0X730CpBV0G2RkLgwAs9WDan3GA==
+  dependencies:
+    debug "^4.1.1"
+    env-paths "^2.2.0"
+    fs-extra "^8.1.0"
+    got "^9.6.0"
+    sanitize-filename "^1.6.2"
+    sumchecker "^3.0.0"
+  optionalDependencies:
+    global-agent "^2.0.2"
+    global-tunnel-ng "^2.7.1"
+
+"@hot-loader/react-dom@^16.11.0":
+  version "16.11.0"
+  resolved "https://registry.yarnpkg.com/@hot-loader/react-dom/-/react-dom-16.11.0.tgz#c0b483923b289db5431516f56ee2a69448ebf9bd"
+  integrity sha512-cIOVB8YgT4EVCNiXK+gGuYl6adW/TKlW3N7GvgY5QgpL2NTWagF/oJxVscHSdR3O7NjJsoxdseB5spqwCHNIhA==
+  dependencies:
+    loose-envify "^1.1.0"
+    object-assign "^4.1.1"
+    prop-types "^15.6.2"
+    scheduler "^0.17.0"
+
+"@posthtml/esm@^1.0.0":
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/@posthtml/esm/-/esm-1.0.0.tgz#09bcb28a02438dcee22ad1970ca1d85a000ae0cf"
+  integrity sha512-dEVG+ITnvqKGa4v040tP+n8LOKOqr94qjLva7bE5pnfm2KHJwsKz69J4KMxgWLznbpBJzy8vQfCayEk3vLZnZQ==
+
+"@sindresorhus/is@^0.14.0":
+  version "0.14.0"
+  resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea"
+  integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==
+
+"@szmarczak/http-timer@^1.1.2":
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421"
+  integrity sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==
+  dependencies:
+    defer-to-connect "^1.0.1"
+
+"@types/debug@^4.1.4":
+  version "4.1.5"
+  resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.5.tgz#b14efa8852b7768d898906613c23f688713e02cd"
+  integrity sha512-Q1y515GcOdTHgagaVFhHnIFQ38ygs/kmxdNpvpou+raI9UO3YZcHDngBSYKQklcKlvA7iuQlmIKbzvmxcOE9CQ==
+
+"@types/events@*":
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7"
+  integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==
+
+"@types/glob@^7.1.1":
+  version "7.1.1"
+  resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.1.tgz#aa59a1c6e3fbc421e07ccd31a944c30eba521575"
+  integrity sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==
+  dependencies:
+    "@types/events" "*"
+    "@types/minimatch" "*"
+    "@types/node" "*"
+
+"@types/minimatch@*":
+  version "3.0.3"
+  resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d"
+  integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==
+
+"@types/node@*", "@types/node@^12.0.12":
+  version "12.12.6"
+  resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.6.tgz#a47240c10d86a9a57bb0c633f0b2e0aea9ce9253"
+  integrity sha512-FjsYUPzEJdGXjwKqSpE0/9QEh6kzhTAeObA54rn6j3rR4C/mzpI9L0KNfoeASSPMMdxIsoJuCLDWcM/rVjIsSA==
+
+"@types/prop-types@*":
+  version "15.7.3"
+  resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7"
+  integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==
+
+"@types/q@^1.5.1":
+  version "1.5.2"
+  resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.2.tgz#690a1475b84f2a884fd07cd797c00f5f31356ea8"
+  integrity sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw==
+
+"@types/react@^16.9.17":
+  version "16.9.17"
+  resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.17.tgz#58f0cc0e9ec2425d1441dd7b623421a867aa253e"
+  integrity sha512-UP27In4fp4sWF5JgyV6pwVPAQM83Fj76JOcg02X5BZcpSu5Wx+fP9RMqc2v0ssBoQIFvD5JdKY41gjJJKmw6Bg==
+  dependencies:
+    "@types/prop-types" "*"
+    csstype "^2.2.0"
+
+"@types/webpack-env@^1.13.9":
+  version "1.14.1"
+  resolved "https://registry.yarnpkg.com/@types/webpack-env/-/webpack-env-1.14.1.tgz#0d8a53f308f017c53a5ddc3d07f4d6fa76b790d7"
+  integrity sha512-0Ki9jAAhKDSuLDXOIMADg54Hu60SuBTEsWaJGGy5cV+SSUQ63J2a+RrYYGrErzz39fXzTibhKrAQJAb8M7PNcA==
+
+"@webassemblyjs/ast@1.8.5":
+  version "1.8.5"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.8.5.tgz#51b1c5fe6576a34953bf4b253df9f0d490d9e359"
+  integrity sha512-aJMfngIZ65+t71C3y2nBBg5FFG0Okt9m0XEgWZ7Ywgn1oMAT8cNwx00Uv1cQyHtidq0Xn94R4TAywO+LCQ+ZAQ==
+  dependencies:
+    "@webassemblyjs/helper-module-context" "1.8.5"
+    "@webassemblyjs/helper-wasm-bytecode" "1.8.5"
+    "@webassemblyjs/wast-parser" "1.8.5"
+
+"@webassemblyjs/floating-point-hex-parser@1.8.5":
+  version "1.8.5"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.8.5.tgz#1ba926a2923613edce496fd5b02e8ce8a5f49721"
+  integrity sha512-9p+79WHru1oqBh9ewP9zW95E3XAo+90oth7S5Re3eQnECGq59ly1Ri5tsIipKGpiStHsUYmY3zMLqtk3gTcOtQ==
+
+"@webassemblyjs/helper-api-error@1.8.5":
+  version "1.8.5"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.8.5.tgz#c49dad22f645227c5edb610bdb9697f1aab721f7"
+  integrity sha512-Za/tnzsvnqdaSPOUXHyKJ2XI7PDX64kWtURyGiJJZKVEdFOsdKUCPTNEVFZq3zJ2R0G5wc2PZ5gvdTRFgm81zA==
+
+"@webassemblyjs/helper-buffer@1.8.5":
+  version "1.8.5"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.8.5.tgz#fea93e429863dd5e4338555f42292385a653f204"
+  integrity sha512-Ri2R8nOS0U6G49Q86goFIPNgjyl6+oE1abW1pS84BuhP1Qcr5JqMwRFT3Ah3ADDDYGEgGs1iyb1DGX+kAi/c/Q==
+
+"@webassemblyjs/helper-code-frame@1.8.5":
+  version "1.8.5"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.8.5.tgz#9a740ff48e3faa3022b1dff54423df9aa293c25e"
+  integrity sha512-VQAadSubZIhNpH46IR3yWO4kZZjMxN1opDrzePLdVKAZ+DFjkGD/rf4v1jap744uPVU6yjL/smZbRIIJTOUnKQ==
+  dependencies:
+    "@webassemblyjs/wast-printer" "1.8.5"
+
+"@webassemblyjs/helper-fsm@1.8.5":
+  version "1.8.5"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.8.5.tgz#ba0b7d3b3f7e4733da6059c9332275d860702452"
+  integrity sha512-kRuX/saORcg8se/ft6Q2UbRpZwP4y7YrWsLXPbbmtepKr22i8Z4O3V5QE9DbZK908dh5Xya4Un57SDIKwB9eow==
+
+"@webassemblyjs/helper-module-context@1.8.5":
+  version "1.8.5"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.8.5.tgz#def4b9927b0101dc8cbbd8d1edb5b7b9c82eb245"
+  integrity sha512-/O1B236mN7UNEU4t9X7Pj38i4VoU8CcMHyy3l2cV/kIF4U5KoHXDVqcDuOs1ltkac90IM4vZdHc52t1x8Yfs3g==
+  dependencies:
+    "@webassemblyjs/ast" "1.8.5"
+    mamacro "^0.0.3"
+
+"@webassemblyjs/helper-wasm-bytecode@1.8.5":
+  version "1.8.5"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.8.5.tgz#537a750eddf5c1e932f3744206551c91c1b93e61"
+  integrity sha512-Cu4YMYG3Ddl72CbmpjU/wbP6SACcOPVbHN1dI4VJNJVgFwaKf1ppeFJrwydOG3NDHxVGuCfPlLZNyEdIYlQ6QQ==
+
+"@webassemblyjs/helper-wasm-section@1.8.5":
+  version "1.8.5"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.8.5.tgz#74ca6a6bcbe19e50a3b6b462847e69503e6bfcbf"
+  integrity sha512-VV083zwR+VTrIWWtgIUpqfvVdK4ff38loRmrdDBgBT8ADXYsEZ5mPQ4Nde90N3UYatHdYoDIFb7oHzMncI02tA==
+  dependencies:
+    "@webassemblyjs/ast" "1.8.5"
+    "@webassemblyjs/helper-buffer" "1.8.5"
+    "@webassemblyjs/helper-wasm-bytecode" "1.8.5"
+    "@webassemblyjs/wasm-gen" "1.8.5"
+
+"@webassemblyjs/ieee754@1.8.5":
+  version "1.8.5"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.8.5.tgz#712329dbef240f36bf57bd2f7b8fb9bf4154421e"
+  integrity sha512-aaCvQYrvKbY/n6wKHb/ylAJr27GglahUO89CcGXMItrOBqRarUMxWLJgxm9PJNuKULwN5n1csT9bYoMeZOGF3g==
+  dependencies:
+    "@xtuc/ieee754" "^1.2.0"
+
+"@webassemblyjs/leb128@1.8.5":
+  version "1.8.5"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.8.5.tgz#044edeb34ea679f3e04cd4fd9824d5e35767ae10"
+  integrity sha512-plYUuUwleLIziknvlP8VpTgO4kqNaH57Y3JnNa6DLpu/sGcP6hbVdfdX5aHAV716pQBKrfuU26BJK29qY37J7A==
+  dependencies:
+    "@xtuc/long" "4.2.2"
+
+"@webassemblyjs/utf8@1.8.5":
+  version "1.8.5"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.8.5.tgz#a8bf3b5d8ffe986c7c1e373ccbdc2a0915f0cedc"
+  integrity sha512-U7zgftmQriw37tfD934UNInokz6yTmn29inT2cAetAsaU9YeVCveWEwhKL1Mg4yS7q//NGdzy79nlXh3bT8Kjw==
+
+"@webassemblyjs/wasm-edit@1.8.5":
+  version "1.8.5"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.8.5.tgz#962da12aa5acc1c131c81c4232991c82ce56e01a"
+  integrity sha512-A41EMy8MWw5yvqj7MQzkDjU29K7UJq1VrX2vWLzfpRHt3ISftOXqrtojn7nlPsZ9Ijhp5NwuODuycSvfAO/26Q==
+  dependencies:
+    "@webassemblyjs/ast" "1.8.5"
+    "@webassemblyjs/helper-buffer" "1.8.5"
+    "@webassemblyjs/helper-wasm-bytecode" "1.8.5"
+    "@webassemblyjs/helper-wasm-section" "1.8.5"
+    "@webassemblyjs/wasm-gen" "1.8.5"
+    "@webassemblyjs/wasm-opt" "1.8.5"
+    "@webassemblyjs/wasm-parser" "1.8.5"
+    "@webassemblyjs/wast-printer" "1.8.5"
+
+"@webassemblyjs/wasm-gen@1.8.5":
+  version "1.8.5"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.8.5.tgz#54840766c2c1002eb64ed1abe720aded714f98bc"
+  integrity sha512-BCZBT0LURC0CXDzj5FXSc2FPTsxwp3nWcqXQdOZE4U7h7i8FqtFK5Egia6f9raQLpEKT1VL7zr4r3+QX6zArWg==
+  dependencies:
+    "@webassemblyjs/ast" "1.8.5"
+    "@webassemblyjs/helper-wasm-bytecode" "1.8.5"
+    "@webassemblyjs/ieee754" "1.8.5"
+    "@webassemblyjs/leb128" "1.8.5"
+    "@webassemblyjs/utf8" "1.8.5"
+
+"@webassemblyjs/wasm-opt@1.8.5":
+  version "1.8.5"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.8.5.tgz#b24d9f6ba50394af1349f510afa8ffcb8a63d264"
+  integrity sha512-HKo2mO/Uh9A6ojzu7cjslGaHaUU14LdLbGEKqTR7PBKwT6LdPtLLh9fPY33rmr5wcOMrsWDbbdCHq4hQUdd37Q==
+  dependencies:
+    "@webassemblyjs/ast" "1.8.5"
+    "@webassemblyjs/helper-buffer" "1.8.5"
+    "@webassemblyjs/wasm-gen" "1.8.5"
+    "@webassemblyjs/wasm-parser" "1.8.5"
+
+"@webassemblyjs/wasm-parser@1.8.5":
+  version "1.8.5"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.8.5.tgz#21576f0ec88b91427357b8536383668ef7c66b8d"
+  integrity sha512-pi0SYE9T6tfcMkthwcgCpL0cM9nRYr6/6fjgDtL6q/ZqKHdMWvxitRi5JcZ7RI4SNJJYnYNaWy5UUrHQy998lw==
+  dependencies:
+    "@webassemblyjs/ast" "1.8.5"
+    "@webassemblyjs/helper-api-error" "1.8.5"
+    "@webassemblyjs/helper-wasm-bytecode" "1.8.5"
+    "@webassemblyjs/ieee754" "1.8.5"
+    "@webassemblyjs/leb128" "1.8.5"
+    "@webassemblyjs/utf8" "1.8.5"
+
+"@webassemblyjs/wast-parser@1.8.5":
+  version "1.8.5"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.8.5.tgz#e10eecd542d0e7bd394f6827c49f3df6d4eefb8c"
+  integrity sha512-daXC1FyKWHF1i11obK086QRlsMsY4+tIOKgBqI1lxAnkp9xe9YMcgOxm9kLe+ttjs5aWV2KKE1TWJCN57/Btsg==
+  dependencies:
+    "@webassemblyjs/ast" "1.8.5"
+    "@webassemblyjs/floating-point-hex-parser" "1.8.5"
+    "@webassemblyjs/helper-api-error" "1.8.5"
+    "@webassemblyjs/helper-code-frame" "1.8.5"
+    "@webassemblyjs/helper-fsm" "1.8.5"
+    "@xtuc/long" "4.2.2"
+
+"@webassemblyjs/wast-printer@1.8.5":
+  version "1.8.5"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.8.5.tgz#114bbc481fd10ca0e23b3560fa812748b0bae5bc"
+  integrity sha512-w0U0pD4EhlnvRyeJzBqaVSJAo9w/ce7/WPogeXLzGkO6hzhr4GnQIZ4W4uUt5b9ooAaXPtnXlj0gzsXEOUNYMg==
+  dependencies:
+    "@webassemblyjs/ast" "1.8.5"
+    "@webassemblyjs/wast-parser" "1.8.5"
+    "@xtuc/long" "4.2.2"
+
+"@xtuc/ieee754@^1.2.0":
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790"
+  integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==
+
+"@xtuc/long@4.2.2":
+  version "4.2.2"
+  resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d"
+  integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==
+
+abbrev@1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
+  integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==
+
+accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.7:
+  version "1.3.7"
+  resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd"
+  integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==
+  dependencies:
+    mime-types "~2.1.24"
+    negotiator "0.6.2"
+
+acorn@^6.2.1:
+  version "6.3.0"
+  resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.3.0.tgz#0087509119ffa4fc0a0041d1e93a417e68cb856e"
+  integrity sha512-/czfa8BwS88b9gWQVhc8eknunSA2DoJpJyTQkhheIf5E48u1N0R4q/YxxsAeqRrmK9TQ/uYfgLDfZo91UlANIA==
+
+ajv-errors@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d"
+  integrity sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==
+
+ajv-keywords@^3.1.0, ajv-keywords@^3.4.1:
+  version "3.4.1"
+  resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.4.1.tgz#ef916e271c64ac12171fd8384eaae6b2345854da"
+  integrity sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==
+
+ajv@^6.1.0, ajv@^6.10.1, ajv@^6.10.2:
+  version "6.10.2"
+  resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.2.tgz#d3cea04d6b017b2894ad69040fec8b623eb4bd52"
+  integrity sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==
+  dependencies:
+    fast-deep-equal "^2.0.1"
+    fast-json-stable-stringify "^2.0.0"
+    json-schema-traverse "^0.4.1"
+    uri-js "^4.2.2"
+
+alphanum-sort@^1.0.1, alphanum-sort@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3"
+  integrity sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=
+
+ansi-align@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.0.tgz#b536b371cf687caaef236c18d3e21fe3797467cb"
+  integrity sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw==
+  dependencies:
+    string-width "^3.0.0"
+
+ansi-colors@^3.0.0:
+  version "3.2.4"
+  resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.4.tgz#e3a3da4bfbae6c86a9c285625de124a234026fbf"
+  integrity sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==
+
+ansi-html@0.0.7:
+  version "0.0.7"
+  resolved "https://registry.yarnpkg.com/ansi-html/-/ansi-html-0.0.7.tgz#813584021962a9e9e6fd039f940d12f56ca7859e"
+  integrity sha1-gTWEAhliqenm/QOflA0S9WynhZ4=
+
+ansi-regex@^2.0.0:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
+  integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8=
+
+ansi-regex@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998"
+  integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=
+
+ansi-regex@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997"
+  integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==
+
+ansi-styles@^2.2.1:
+  version "2.2.1"
+  resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe"
+  integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=
+
+ansi-styles@^3.2.0, ansi-styles@^3.2.1:
+  version "3.2.1"
+  resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
+  integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==
+  dependencies:
+    color-convert "^1.9.0"
+
+anymatch@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb"
+  integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==
+  dependencies:
+    micromatch "^3.1.4"
+    normalize-path "^2.1.1"
+
+app-builder-bin@3.4.3:
+  version "3.4.3"
+  resolved "https://registry.yarnpkg.com/app-builder-bin/-/app-builder-bin-3.4.3.tgz#58a74193eb882f029be6b7f0cd3f0c6805927a6b"
+  integrity sha512-qMhayIwi3juerQEVJMQ76trObEbfQT0nhUdxZz9a26/3NLT3pE6awmQ8S1cEnrGugaaM5gYqR8OElcDezfmEsg==
+
+app-builder-lib@21.2.0, app-builder-lib@~21.2.0:
+  version "21.2.0"
+  resolved "https://registry.yarnpkg.com/app-builder-lib/-/app-builder-lib-21.2.0.tgz#fa1d1604601431e2c3476857e9b9b61d33ad26cc"
+  integrity sha512-aOX/nv77/Bti6NymJDg7p9T067xD8m1ipIEJR7B4Mm1GsJWpMm9PZdXtCRiMNRjHtQS5KIljT0g17781y6qn5A==
+  dependencies:
+    "7zip-bin" "~5.0.3"
+    "@develar/schema-utils" "~2.1.0"
+    async-exit-hook "^2.0.1"
+    bluebird-lst "^1.0.9"
+    builder-util "21.2.0"
+    builder-util-runtime "8.3.0"
+    chromium-pickle-js "^0.2.0"
+    debug "^4.1.1"
+    ejs "^2.6.2"
+    electron-publish "21.2.0"
+    fs-extra "^8.1.0"
+    hosted-git-info "^2.7.1"
+    is-ci "^2.0.0"
+    isbinaryfile "^4.0.2"
+    js-yaml "^3.13.1"
+    lazy-val "^1.0.4"
+    minimatch "^3.0.4"
+    normalize-package-data "^2.5.0"
+    read-config-file "5.0.0"
+    sanitize-filename "^1.6.2"
+    semver "^6.3.0"
+    temp-file "^3.3.4"
+
+aproba@^1.0.3, aproba@^1.1.1:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a"
+  integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==
+
+are-we-there-yet@~1.1.2:
+  version "1.1.5"
+  resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21"
+  integrity sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==
+  dependencies:
+    delegates "^1.0.0"
+    readable-stream "^2.0.6"
+
+argparse@^1.0.7:
+  version "1.0.10"
+  resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
+  integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==
+  dependencies:
+    sprintf-js "~1.0.2"
+
+arr-diff@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520"
+  integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=
+
+arr-flatten@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1"
+  integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==
+
+arr-union@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4"
+  integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=
+
+array-flatten@1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2"
+  integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=
+
+array-flatten@^2.1.0:
+  version "2.1.2"
+  resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.2.tgz#24ef80a28c1a893617e2149b0c6d0d788293b099"
+  integrity sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==
+
+array-union@^1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39"
+  integrity sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=
+  dependencies:
+    array-uniq "^1.0.1"
+
+array-uniq@^1.0.1:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6"
+  integrity sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=
+
+array-unique@^0.3.2:
+  version "0.3.2"
+  resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428"
+  integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=
+
+asn1.js@^4.0.0:
+  version "4.10.1"
+  resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0"
+  integrity sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==
+  dependencies:
+    bn.js "^4.0.0"
+    inherits "^2.0.1"
+    minimalistic-assert "^1.0.0"
+
+assert@^1.1.1:
+  version "1.5.0"
+  resolved "https://registry.yarnpkg.com/assert/-/assert-1.5.0.tgz#55c109aaf6e0aefdb3dc4b71240c70bf574b18eb"
+  integrity sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==
+  dependencies:
+    object-assign "^4.1.1"
+    util "0.10.3"
+
+assign-symbols@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367"
+  integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=
+
+async-each@^1.0.1:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf"
+  integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==
+
+async-exit-hook@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/async-exit-hook/-/async-exit-hook-2.0.1.tgz#8bd8b024b0ec9b1c01cccb9af9db29bd717dfaf3"
+  integrity sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==
+
+async-limiter@~1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd"
+  integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==
+
+async@^2.6.2:
+  version "2.6.3"
+  resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff"
+  integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==
+  dependencies:
+    lodash "^4.17.14"
+
+atob@^2.1.1:
+  version "2.1.2"
+  resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
+  integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
+
+autoprefixer@^6.3.1:
+  version "6.7.7"
+  resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-6.7.7.tgz#1dbd1c835658e35ce3f9984099db00585c782014"
+  integrity sha1-Hb0cg1ZY41zj+ZhAmdsAWFx4IBQ=
+  dependencies:
+    browserslist "^1.7.6"
+    caniuse-db "^1.0.30000634"
+    normalize-range "^0.1.2"
+    num2fraction "^1.2.2"
+    postcss "^5.2.16"
+    postcss-value-parser "^3.2.3"
+
+babel-loader@^8.0.6:
+  version "8.0.6"
+  resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.0.6.tgz#e33bdb6f362b03f4bb141a0c21ab87c501b70dfb"
+  integrity sha512-4BmWKtBOBm13uoUwd08UwjZlaw3O9GWf456R9j+5YykFZ6LUIjIKLc0zEZf+hauxPOJs96C8k6FvYD09vWzhYw==
+  dependencies:
+    find-cache-dir "^2.0.0"
+    loader-utils "^1.0.2"
+    mkdirp "^0.5.1"
+    pify "^4.0.1"
+
+babel-plugin-component@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-component/-/babel-plugin-component-1.1.1.tgz#9b023a23ff5c9aae0fd56c5a18b9cab8c4d45eea"
+  integrity sha512-WUw887kJf2GH80Ng/ZMctKZ511iamHNqPhd9uKo14yzisvV7Wt1EckIrb8oq/uCz3B3PpAW7Xfl7AkTLDYT6ag==
+  dependencies:
+    "@babel/helper-module-imports" "7.0.0-beta.35"
+
+babel-plugin-dynamic-import-node@^2.3.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz#f00f507bdaa3c3e3ff6e7e5e98d90a7acab96f7f"
+  integrity sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ==
+  dependencies:
+    object.assign "^4.1.0"
+
+babel-runtime@^6.26.0:
+  version "6.26.0"
+  resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe"
+  integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4=
+  dependencies:
+    core-js "^2.4.0"
+    regenerator-runtime "^0.11.0"
+
+balanced-match@^0.4.2:
+  version "0.4.2"
+  resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838"
+  integrity sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=
+
+balanced-match@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
+  integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
+
+base64-js@^1.0.2:
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1"
+  integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==
+
+base@^0.11.1:
+  version "0.11.2"
+  resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f"
+  integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==
+  dependencies:
+    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"
+
+batch@0.6.1:
+  version "0.6.1"
+  resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16"
+  integrity sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=
+
+big.js@^3.1.3:
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/big.js/-/big.js-3.2.0.tgz#a5fc298b81b9e0dca2e458824784b65c52ba588e"
+  integrity sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==
+
+big.js@^5.2.2:
+  version "5.2.2"
+  resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328"
+  integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==
+
+binary-extensions@^1.0.0:
+  version "1.13.1"
+  resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65"
+  integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==
+
+bluebird-lst@^1.0.9:
+  version "1.0.9"
+  resolved "https://registry.yarnpkg.com/bluebird-lst/-/bluebird-lst-1.0.9.tgz#a64a0e4365658b9ab5fe875eb9dfb694189bb41c"
+  integrity sha512-7B1Rtx82hjnSD4PGLAjVWeYH3tHAcVUmChh85a3lltKQm6FresXh9ErQo6oAv6CqxttczC3/kEg8SY5NluPuUw==
+  dependencies:
+    bluebird "^3.5.5"
+
+bluebird@^3.5.4, bluebird@^3.5.5:
+  version "3.7.1"
+  resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.1.tgz#df70e302b471d7473489acf26a93d63b53f874de"
+  integrity sha512-DdmyoGCleJnkbp3nkbxTLJ18rjDsE4yCggEwKNXkeV123sPNfOCYeDoeuOY+F2FrSjO1YXcTU+dsy96KMy+gcg==
+
+bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0:
+  version "4.11.8"
+  resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f"
+  integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==
+
+body-parser@1.19.0:
+  version "1.19.0"
+  resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a"
+  integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==
+  dependencies:
+    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"
+
+bonjour@^3.5.0:
+  version "3.5.0"
+  resolved "https://registry.yarnpkg.com/bonjour/-/bonjour-3.5.0.tgz#8e890a183d8ee9a2393b3844c691a42bcf7bc9f5"
+  integrity sha1-jokKGD2O6aI5OzhExpGkK897yfU=
+  dependencies:
+    array-flatten "^2.1.0"
+    deep-equal "^1.0.1"
+    dns-equal "^1.0.0"
+    dns-txt "^2.0.2"
+    multicast-dns "^6.0.1"
+    multicast-dns-service-types "^1.1.0"
+
+boolbase@^1.0.0, boolbase@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
+  integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24=
+
+boolean@^2.0.2, boolean@^2.0.3:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/boolean/-/boolean-2.0.3.tgz#da4a5c5a85231d64dd4f6022a0fa5a66adc76653"
+  integrity sha512-iHzXeFCXWrpjYE7DToXGCBPGZf0eVISqzL+4sgrOSYEKXnb59WHPFvGTTyCj6zJ/MuuLAxEn8zPkrTHHzlt3IA==
+
+boxen@^3.0.0:
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/boxen/-/boxen-3.2.0.tgz#fbdff0de93636ab4450886b6ff45b92d098f45eb"
+  integrity sha512-cU4J/+NodM3IHdSL2yN8bqYqnmlBTidDR4RC7nJs61ZmtGz8VZzM3HLQX0zY5mrSmPtR3xWwsq2jOUQqFZN8+A==
+  dependencies:
+    ansi-align "^3.0.0"
+    camelcase "^5.3.1"
+    chalk "^2.4.2"
+    cli-boxes "^2.2.0"
+    string-width "^3.0.0"
+    term-size "^1.2.0"
+    type-fest "^0.3.0"
+    widest-line "^2.0.0"
+
+brace-expansion@^1.1.7:
+  version "1.1.11"
+  resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
+  integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
+  dependencies:
+    balanced-match "^1.0.0"
+    concat-map "0.0.1"
+
+braces@^2.3.1, braces@^2.3.2:
+  version "2.3.2"
+  resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729"
+  integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==
+  dependencies:
+    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"
+
+brorand@^1.0.1:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f"
+  integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=
+
+browserify-aes@^1.0.0, browserify-aes@^1.0.4:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48"
+  integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==
+  dependencies:
+    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@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0"
+  integrity sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==
+  dependencies:
+    browserify-aes "^1.0.4"
+    browserify-des "^1.0.0"
+    evp_bytestokey "^1.0.0"
+
+browserify-des@^1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.2.tgz#3af4f1f59839403572f1c66204375f7a7f703e9c"
+  integrity sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==
+  dependencies:
+    cipher-base "^1.0.1"
+    des.js "^1.0.0"
+    inherits "^2.0.1"
+    safe-buffer "^5.1.2"
+
+browserify-rsa@^4.0.0:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.0.1.tgz#21e0abfaf6f2029cf2fafb133567a701d4135524"
+  integrity sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=
+  dependencies:
+    bn.js "^4.1.0"
+    randombytes "^2.0.1"
+
+browserify-sign@^4.0.0:
+  version "4.0.4"
+  resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.0.4.tgz#aa4eb68e5d7b658baa6bf6a57e630cbd7a93d298"
+  integrity sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=
+  dependencies:
+    bn.js "^4.1.1"
+    browserify-rsa "^4.0.0"
+    create-hash "^1.1.0"
+    create-hmac "^1.1.2"
+    elliptic "^6.0.0"
+    inherits "^2.0.1"
+    parse-asn1 "^5.0.0"
+
+browserify-zlib@^0.2.0:
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz#2869459d9aa3be245fe8fe2ca1f46e2e7f54d73f"
+  integrity sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==
+  dependencies:
+    pako "~1.0.5"
+
+browserslist@^1.3.6, browserslist@^1.5.2, browserslist@^1.7.6:
+  version "1.7.7"
+  resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-1.7.7.tgz#0bd76704258be829b2398bb50e4b62d1a166b0b9"
+  integrity sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=
+  dependencies:
+    caniuse-db "^1.0.30000639"
+    electron-to-chromium "^1.2.7"
+
+browserslist@^4.6.0, browserslist@^4.7.2:
+  version "4.7.2"
+  resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.7.2.tgz#1bb984531a476b5d389cedecb195b2cd69fb1348"
+  integrity sha512-uZavT/gZXJd2UTi9Ov7/Z340WOSQ3+m1iBVRUknf+okKxonL9P83S3ctiBDtuRmRu8PiCHjqyueqQ9HYlJhxiw==
+  dependencies:
+    caniuse-lite "^1.0.30001004"
+    electron-to-chromium "^1.3.295"
+    node-releases "^1.1.38"
+
+browserslist@^4.8.2:
+  version "4.8.2"
+  resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.8.2.tgz#b45720ad5fbc8713b7253c20766f701c9a694289"
+  integrity sha512-+M4oeaTplPm/f1pXDw84YohEv7B1i/2Aisei8s4s6k3QsoSHa7i5sz8u/cGQkkatCPxMASKxPualR4wwYgVboA==
+  dependencies:
+    caniuse-lite "^1.0.30001015"
+    electron-to-chromium "^1.3.322"
+    node-releases "^1.1.42"
+
+buffer-from@^1.0.0:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
+  integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==
+
+buffer-indexof@^1.0.0:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/buffer-indexof/-/buffer-indexof-1.1.1.tgz#52fabcc6a606d1a00302802648ef68f639da268c"
+  integrity sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==
+
+buffer-xor@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9"
+  integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=
+
+buffer@^4.3.0:
+  version "4.9.1"
+  resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.1.tgz#6d1bb601b07a4efced97094132093027c95bc298"
+  integrity sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=
+  dependencies:
+    base64-js "^1.0.2"
+    ieee754 "^1.1.4"
+    isarray "^1.0.0"
+
+builder-util-runtime@8.3.0:
+  version "8.3.0"
+  resolved "https://registry.yarnpkg.com/builder-util-runtime/-/builder-util-runtime-8.3.0.tgz#f5fac9139af6facf42a21fbe4d3aebed88fda33e"
+  integrity sha512-CSOdsYqf4RXIHh1HANPbrZHlZ9JQJXSuDDloblZPcWQVN62inyYoTQuSmY3KrgefME2Sv3Kn2MxHvbGQHRf8Iw==
+  dependencies:
+    debug "^4.1.1"
+    sax "^1.2.4"
+
+builder-util@21.2.0, builder-util@~21.2.0:
+  version "21.2.0"
+  resolved "https://registry.yarnpkg.com/builder-util/-/builder-util-21.2.0.tgz#aba721190e4e841009d9fb4b88f1130ed616522f"
+  integrity sha512-Nd6CUb6YgDY8EXAXEIegx+1kzKqyFQ5ZM5BoYkeunAlwz/zDJoH1UCyULjoS5wQe5czNClFQy07zz2bzYD0Z4A==
+  dependencies:
+    "7zip-bin" "~5.0.3"
+    "@types/debug" "^4.1.4"
+    app-builder-bin "3.4.3"
+    bluebird-lst "^1.0.9"
+    builder-util-runtime "8.3.0"
+    chalk "^2.4.2"
+    debug "^4.1.1"
+    fs-extra "^8.1.0"
+    is-ci "^2.0.0"
+    js-yaml "^3.13.1"
+    source-map-support "^0.5.13"
+    stat-mode "^0.3.0"
+    temp-file "^3.3.4"
+
+builtin-status-codes@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8"
+  integrity sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=
+
+bytes@3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048"
+  integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=
+
+bytes@3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6"
+  integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==
+
+cacache@^12.0.2:
+  version "12.0.3"
+  resolved "https://registry.yarnpkg.com/cacache/-/cacache-12.0.3.tgz#be99abba4e1bf5df461cd5a2c1071fc432573390"
+  integrity sha512-kqdmfXEGFepesTuROHMs3MpFLWrPkSSpRqOw80RCflZXy/khxaArvFrQ7uJxSUduzAufc6G0g1VUCOZXxWavPw==
+  dependencies:
+    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"
+
+cache-base@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2"
+  integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==
+  dependencies:
+    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"
+
+cacheable-request@^6.0.0:
+  version "6.1.0"
+  resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912"
+  integrity sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==
+  dependencies:
+    clone-response "^1.0.2"
+    get-stream "^5.1.0"
+    http-cache-semantics "^4.0.0"
+    keyv "^3.0.0"
+    lowercase-keys "^2.0.0"
+    normalize-url "^4.1.0"
+    responselike "^1.0.2"
+
+camel-case@3.0.x:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-3.0.0.tgz#ca3c3688a4e9cf3a4cda777dc4dcbc713249cf73"
+  integrity sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=
+  dependencies:
+    no-case "^2.2.0"
+    upper-case "^1.1.1"
+
+camelcase@^5.0.0, camelcase@^5.3.1:
+  version "5.3.1"
+  resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
+  integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
+
+caniuse-api@^1.5.2:
+  version "1.6.1"
+  resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-1.6.1.tgz#b534e7c734c4f81ec5fbe8aca2ad24354b962c6c"
+  integrity sha1-tTTnxzTE+B7F++isoq0kNUuWLGw=
+  dependencies:
+    browserslist "^1.3.6"
+    caniuse-db "^1.0.30000529"
+    lodash.memoize "^4.1.2"
+    lodash.uniq "^4.5.0"
+
+caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639:
+  version "1.0.30001008"
+  resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30001008.tgz#1691f28db0e08bf6abb5e472d8aaea392ec4a995"
+  integrity sha512-Fog+uREPKb/RDc0puoAqqBZB05I8wYff+TIMDkYw9Lweq7+hUEN5fNLpIaBX6AJxq4sndqPct8fYLrDUV6u4xw==
+
+caniuse-lite@^1.0.30001004:
+  version "1.0.30001008"
+  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001008.tgz#b8841b1df78a9f5ed9702537ef592f1f8772c0d9"
+  integrity sha512-b8DJyb+VVXZGRgJUa30cbk8gKHZ3LOZTBLaUEEVr2P4xpmFigOCc62CO4uzquW641Ouq1Rm9N+rWLWdSYDaDIw==
+
+caniuse-lite@^1.0.30001015:
+  version "1.0.30001015"
+  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001015.tgz#15a7ddf66aba786a71d99626bc8f2b91c6f0f5f0"
+  integrity sha512-/xL2AbW/XWHNu1gnIrO8UitBGoFthcsDgU9VLK1/dpsoxbaD5LscHozKze05R6WLsBvLhqv78dAPozMFQBYLbQ==
+
+catharsis@^0.8.11:
+  version "0.8.11"
+  resolved "https://registry.yarnpkg.com/catharsis/-/catharsis-0.8.11.tgz#d0eb3d2b82b7da7a3ce2efb1a7b00becc6643468"
+  integrity sha512-a+xUyMV7hD1BrDQA/3iPV7oc+6W26BgVJO05PGEoatMyIuPScQKsde6i3YorWX1qs+AZjnJ18NqdKoCtKiNh1g==
+  dependencies:
+    lodash "^4.17.14"
+
+chalk@2.4.2, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.4.1, chalk@^2.4.2:
+  version "2.4.2"
+  resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
+  integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
+  dependencies:
+    ansi-styles "^3.2.1"
+    escape-string-regexp "^1.0.5"
+    supports-color "^5.3.0"
+
+chalk@^1.1.3:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
+  integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=
+  dependencies:
+    ansi-styles "^2.2.1"
+    escape-string-regexp "^1.0.2"
+    has-ansi "^2.0.0"
+    strip-ansi "^3.0.0"
+    supports-color "^2.0.0"
+
+chokidar@^2.0.2, chokidar@^2.1.8:
+  version "2.1.8"
+  resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917"
+  integrity sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==
+  dependencies:
+    anymatch "^2.0.0"
+    async-each "^1.0.1"
+    braces "^2.3.2"
+    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"
+  optionalDependencies:
+    fsevents "^1.2.7"
+
+chownr@^1.1.1:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.3.tgz#42d837d5239688d55f303003a508230fa6727142"
+  integrity sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw==
+
+chrome-trace-event@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz#234090ee97c7d4ad1a2c4beae27505deffc608a4"
+  integrity sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ==
+  dependencies:
+    tslib "^1.9.0"
+
+chromium-pickle-js@^0.2.0:
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/chromium-pickle-js/-/chromium-pickle-js-0.2.0.tgz#04a106672c18b085ab774d983dfa3ea138f22205"
+  integrity sha1-BKEGZywYsIWrd02YPfo+oTjyIgU=
+
+ci-info@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46"
+  integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==
+
+cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de"
+  integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==
+  dependencies:
+    inherits "^2.0.1"
+    safe-buffer "^5.0.1"
+
+clap@^1.0.9:
+  version "1.2.3"
+  resolved "https://registry.yarnpkg.com/clap/-/clap-1.2.3.tgz#4f36745b32008492557f46412d66d50cb99bce51"
+  integrity sha512-4CoL/A3hf90V3VIEjeuhSvlGFEHKzOz+Wfc2IVZc+FaUgU0ZQafJTP49fvnULipOPcAfqhyI2duwQyns6xqjYA==
+  dependencies:
+    chalk "^1.1.3"
+
+class-utils@^0.3.5:
+  version "0.3.6"
+  resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463"
+  integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==
+  dependencies:
+    arr-union "^3.1.0"
+    define-property "^0.2.5"
+    isobject "^3.0.0"
+    static-extend "^0.1.1"
+
+classnames@^2.2.5:
+  version "2.2.6"
+  resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce"
+  integrity sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==
+
+clean-css@4.2.x:
+  version "4.2.1"
+  resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.1.tgz#2d411ef76b8569b6d0c84068dabe85b0aa5e5c17"
+  integrity sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g==
+  dependencies:
+    source-map "~0.6.0"
+
+cli-boxes@^2.2.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.0.tgz#538ecae8f9c6ca508e3c3c95b453fe93cb4c168d"
+  integrity sha512-gpaBrMAizVEANOpfZp/EEUixTXDyGt7DFzdK5hU+UbWt/J0lB0w20ncZj59Z9a93xHb9u12zF5BS6i9RKbtg4w==
+
+cliui@^4.0.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/cliui/-/cliui-4.1.0.tgz#348422dbe82d800b3022eef4f6ac10bf2e4d1b49"
+  integrity sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==
+  dependencies:
+    string-width "^2.1.1"
+    strip-ansi "^4.0.0"
+    wrap-ansi "^2.0.0"
+
+cliui@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5"
+  integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==
+  dependencies:
+    string-width "^3.1.0"
+    strip-ansi "^5.2.0"
+    wrap-ansi "^5.1.0"
+
+clone-response@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b"
+  integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=
+  dependencies:
+    mimic-response "^1.0.0"
+
+clone@^1.0.2:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e"
+  integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4=
+
+clsx@^1.0.1:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.0.4.tgz#0c0171f6d5cb2fe83848463c15fcc26b4df8c2ec"
+  integrity sha512-1mQ557MIZTrL/140j+JVdRM6e31/OA4vTYxXgqIIZlndyfjHpyawKZia1Im05Vp9BWmImkcNrNtFYQMyFcgJDg==
+
+coa@^2.0.2:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/coa/-/coa-2.0.2.tgz#43f6c21151b4ef2bf57187db0d73de229e3e7ec3"
+  integrity sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==
+  dependencies:
+    "@types/q" "^1.5.1"
+    chalk "^2.4.1"
+    q "^1.1.2"
+
+coa@~1.0.1:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/coa/-/coa-1.0.4.tgz#a9ef153660d6a86a8bdec0289a5c684d217432fd"
+  integrity sha1-qe8VNmDWqGqL3sAomlxoTSF0Mv0=
+  dependencies:
+    q "^1.1.2"
+
+code-point-at@^1.0.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
+  integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=
+
+codemirror@^5.49.2:
+  version "5.49.2"
+  resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.49.2.tgz#c84fdaf11b19803f828b0c67060c7bc6d154ccad"
+  integrity sha512-dwJ2HRPHm8w51WB5YTF9J7m6Z5dtkqbU9ntMZ1dqXyFB9IpjoUFDj80ahRVEoVanfIp6pfASJbOlbWdEf8FOzQ==
+
+collection-visit@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0"
+  integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=
+  dependencies:
+    map-visit "^1.0.0"
+    object-visit "^1.0.0"
+
+color-convert@^1.3.0, color-convert@^1.9.0:
+  version "1.9.3"
+  resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
+  integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
+  dependencies:
+    color-name "1.1.3"
+
+color-name@1.1.3:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
+  integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
+
+color-name@^1.0.0:
+  version "1.1.4"
+  resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
+  integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
+
+color-string@^0.3.0:
+  version "0.3.0"
+  resolved "https://registry.yarnpkg.com/color-string/-/color-string-0.3.0.tgz#27d46fb67025c5c2fa25993bfbf579e47841b991"
+  integrity sha1-J9RvtnAlxcL6JZk7+/V55HhBuZE=
+  dependencies:
+    color-name "^1.0.0"
+
+color@^0.11.0:
+  version "0.11.4"
+  resolved "https://registry.yarnpkg.com/color/-/color-0.11.4.tgz#6d7b5c74fb65e841cd48792ad1ed5e07b904d764"
+  integrity sha1-bXtcdPtl6EHNSHkq0e1eB7kE12Q=
+  dependencies:
+    clone "^1.0.2"
+    color-convert "^1.3.0"
+    color-string "^0.3.0"
+
+colormin@^1.0.5:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/colormin/-/colormin-1.1.2.tgz#ea2f7420a72b96881a38aae59ec124a6f7298133"
+  integrity sha1-6i90IKcrlogaOKrlnsEkpvcpgTM=
+  dependencies:
+    color "^0.11.0"
+    css-color-names "0.0.4"
+    has "^1.0.1"
+
+colors@~1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63"
+  integrity sha1-FopHAXVran9RoSzgyXv6KMCE7WM=
+
+commander@2.17.x:
+  version "2.17.1"
+  resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf"
+  integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==
+
+commander@^2.19.0, commander@^2.20.0:
+  version "2.20.3"
+  resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
+  integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
+
+commander@~2.19.0:
+  version "2.19.0"
+  resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a"
+  integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==
+
+commondir@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
+  integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=
+
+component-emitter@^1.2.1:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0"
+  integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==
+
+compressible@~2.0.16:
+  version "2.0.17"
+  resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.17.tgz#6e8c108a16ad58384a977f3a482ca20bff2f38c1"
+  integrity sha512-BGHeLCK1GV7j1bSmQQAi26X+GgWcTjLr/0tzSvMCl3LH1w1IJ4PFSPoV5316b30cneTziC+B1a+3OjoSUcQYmw==
+  dependencies:
+    mime-db ">= 1.40.0 < 2"
+
+compression@^1.7.4:
+  version "1.7.4"
+  resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f"
+  integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==
+  dependencies:
+    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"
+
+concat-map@0.0.1:
+  version "0.0.1"
+  resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
+  integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
+
+concat-stream@1.6.2, concat-stream@^1.5.0:
+  version "1.6.2"
+  resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34"
+  integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==
+  dependencies:
+    buffer-from "^1.0.0"
+    inherits "^2.0.3"
+    readable-stream "^2.2.2"
+    typedarray "^0.0.6"
+
+config-chain@^1.1.11:
+  version "1.1.12"
+  resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.12.tgz#0fde8d091200eb5e808caf25fe618c02f48e4efa"
+  integrity sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA==
+  dependencies:
+    ini "^1.3.4"
+    proto-list "~1.2.1"
+
+configstore@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/configstore/-/configstore-4.0.0.tgz#5933311e95d3687efb592c528b922d9262d227e7"
+  integrity sha512-CmquAXFBocrzaSM8mtGPMM/HiWmyIpr4CcJl/rgY2uCObZ/S7cKU0silxslqJejl+t/T9HS8E0PUNQD81JGUEQ==
+  dependencies:
+    dot-prop "^4.1.0"
+    graceful-fs "^4.1.2"
+    make-dir "^1.0.0"
+    unique-string "^1.0.0"
+    write-file-atomic "^2.0.0"
+    xdg-basedir "^3.0.0"
+
+connect-history-api-fallback@^1.6.0:
+  version "1.6.0"
+  resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz#8b32089359308d111115d81cad3fceab888f97bc"
+  integrity sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==
+
+console-browserify@^1.1.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336"
+  integrity sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==
+
+console-control-strings@^1.0.0, console-control-strings@~1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
+  integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=
+
+constants-browserify@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75"
+  integrity sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=
+
+content-disposition@0.5.3:
+  version "0.5.3"
+  resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd"
+  integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==
+  dependencies:
+    safe-buffer "5.1.2"
+
+content-type@~1.0.4:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
+  integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==
+
+convert-source-map@^1.7.0:
+  version "1.7.0"
+  resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442"
+  integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==
+  dependencies:
+    safe-buffer "~5.1.1"
+
+cookie-signature@1.0.6:
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
+  integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw=
+
+cookie@0.4.0:
+  version "0.4.0"
+  resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba"
+  integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==
+
+copy-concurrently@^1.0.0:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz#92297398cae34937fcafd6ec8139c18051f0b5e0"
+  integrity sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==
+  dependencies:
+    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"
+
+copy-descriptor@^0.1.0:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d"
+  integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=
+
+core-js-compat@^3.1.1:
+  version "3.4.0"
+  resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.4.0.tgz#2a47c51d3dc026d290018cacd987495f68a47c75"
+  integrity sha512-pgQUcgT2+v9/yxHgMynYjNj7nmxLRXv3UC39rjCjDwpe63ev2rioQTju1PKLYUBbPCQQvZNWvQC8tBJd65q11g==
+  dependencies:
+    browserslist "^4.7.2"
+    semver "^6.3.0"
+
+core-js-compat@^3.6.0:
+  version "3.6.0"
+  resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.6.0.tgz#4eb6cb69d03d99159ed7c860cd5fcf7d23a62ea9"
+  integrity sha512-Z3eCNjGgoYluH89Jt4wVkfYsc/VdLrA2/woX5lm0isO/pCT+P+Y+o65bOuEnjDJLthdwTBxbCVzptTXtc18fJg==
+  dependencies:
+    browserslist "^4.8.2"
+    semver "7.0.0"
+
+core-js@^2.4.0:
+  version "2.6.10"
+  resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.10.tgz#8a5b8391f8cc7013da703411ce5b585706300d7f"
+  integrity sha512-I39t74+4t+zau64EN1fE5v2W31Adtc/REhzWN+gWRRXg6WH5qAsZm62DHpQ1+Yhe4047T55jvzz7MUqF/dBBlA==
+
+core-js@^3.3.3:
+  version "3.4.0"
+  resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.4.0.tgz#29ea478601789c72f2978e9bb98f43546f89d3aa"
+  integrity sha512-lQxb4HScV71YugF/X28LtePZj9AB7WqOpcB+YztYxusvhrgZiQXPmCYfPC5LHsw/+ScEtDbXU3xbqH3CjBRmYA==
+
+core-util-is@~1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
+  integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
+
+create-ecdh@^4.0.0:
+  version "4.0.3"
+  resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.3.tgz#c9111b6f33045c4697f144787f9254cdc77c45ff"
+  integrity sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==
+  dependencies:
+    bn.js "^4.1.0"
+    elliptic "^6.0.0"
+
+create-hash@^1.1.0, create-hash@^1.1.2:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196"
+  integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==
+  dependencies:
+    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@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4:
+  version "1.1.7"
+  resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff"
+  integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==
+  dependencies:
+    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"
+
+crocket@^0.9.11:
+  version "0.9.11"
+  resolved "https://registry.yarnpkg.com/crocket/-/crocket-0.9.11.tgz#288fca11ef0d3dd239b62c488265f30c8edfb0c5"
+  integrity sha1-KI/KEe8NPdI5tixIgmXzDI7fsMU=
+  dependencies:
+    xpipe "*"
+
+cross-spawn@6.0.5, cross-spawn@^6.0.0:
+  version "6.0.5"
+  resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
+  integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==
+  dependencies:
+    nice-try "^1.0.4"
+    path-key "^2.0.1"
+    semver "^5.5.0"
+    shebang-command "^1.2.0"
+    which "^1.2.9"
+
+cross-spawn@^5.0.1:
+  version "5.1.0"
+  resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449"
+  integrity sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=
+  dependencies:
+    lru-cache "^4.0.1"
+    shebang-command "^1.2.0"
+    which "^1.2.9"
+
+cross-unzip@0.0.2:
+  version "0.0.2"
+  resolved "https://registry.yarnpkg.com/cross-unzip/-/cross-unzip-0.0.2.tgz#5183bc47a09559befcf98cc4657964999359372f"
+  integrity sha1-UYO8R6CVWb78+YzEZXlkmZNZNy8=
+
+crypto-browserify@^3.11.0:
+  version "3.12.0"
+  resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec"
+  integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==
+  dependencies:
+    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-random-string@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-1.0.0.tgz#a230f64f568310e1498009940790ec99545bca7e"
+  integrity sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=
+
+css-color-names@0.0.4:
+  version "0.0.4"
+  resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0"
+  integrity sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=
+
+css-hot-loader@^1.4.4:
+  version "1.4.4"
+  resolved "https://registry.yarnpkg.com/css-hot-loader/-/css-hot-loader-1.4.4.tgz#ae784932cd8b7d092f7f15702af08b3ec9436052"
+  integrity sha512-J/qXHz+r7FOT92qMIJfxUk0LC9fecQNZVr0MswQ4FOpKLyOCBjofVMfc6R268bh/5ktkTShrweMr0wWqerC92g==
+  dependencies:
+    loader-utils "^1.1.0"
+    lodash "^4.17.5"
+    normalize-url "^1.9.1"
+
+css-loader@^3.0.0:
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-3.2.0.tgz#bb570d89c194f763627fcf1f80059c6832d009b2"
+  integrity sha512-QTF3Ud5H7DaZotgdcJjGMvyDj5F3Pn1j/sC6VBEOVp94cbwqyIBdcs/quzj4MC1BKQSrTpQznegH/5giYbhnCQ==
+  dependencies:
+    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.17"
+    postcss-modules-extract-imports "^2.0.0"
+    postcss-modules-local-by-default "^3.0.2"
+    postcss-modules-scope "^2.1.0"
+    postcss-modules-values "^3.0.0"
+    postcss-value-parser "^4.0.0"
+    schema-utils "^2.0.0"
+
+css-loader@^3.4.0:
+  version "3.4.0"
+  resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-3.4.0.tgz#9fb263436783117a41d014e45e8eaeba54dd6670"
+  integrity sha512-JornYo4RAXl1Mzt0lOSVPmArzAMV3rGY2VuwtaDc732WTWjdwTaeS19nCGWMcSCf305Q396lhhDAJEWWM0SgPQ==
+  dependencies:
+    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.23"
+    postcss-modules-extract-imports "^2.0.0"
+    postcss-modules-local-by-default "^3.0.2"
+    postcss-modules-scope "^2.1.1"
+    postcss-modules-values "^3.0.0"
+    postcss-value-parser "^4.0.2"
+    schema-utils "^2.6.0"
+
+css-select-base-adapter@^0.1.1:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz#3b2ff4972cc362ab88561507a95408a1432135d7"
+  integrity sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==
+
+css-select@^1.1.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858"
+  integrity sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=
+  dependencies:
+    boolbase "~1.0.0"
+    css-what "2.1"
+    domutils "1.5.1"
+    nth-check "~1.0.1"
+
+css-select@^2.0.0:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/css-select/-/css-select-2.0.2.tgz#ab4386cec9e1f668855564b17c3733b43b2a5ede"
+  integrity sha512-dSpYaDVoWaELjvZ3mS6IKZM/y2PMPa/XYoEfYNZePL4U/XgyxZNroHEHReDx/d+VgXh9VbCTtFqLkFbmeqeaRQ==
+  dependencies:
+    boolbase "^1.0.0"
+    css-what "^2.1.2"
+    domutils "^1.7.0"
+    nth-check "^1.0.2"
+
+css-tree@1.0.0-alpha.37:
+  version "1.0.0-alpha.37"
+  resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.37.tgz#98bebd62c4c1d9f960ec340cf9f7522e30709a22"
+  integrity sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==
+  dependencies:
+    mdn-data "2.0.4"
+    source-map "^0.6.1"
+
+css-what@2.1, css-what@^2.1.2:
+  version "2.1.3"
+  resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2"
+  integrity sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==
+
+cssesc@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
+  integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==
+
+cssnano@^3.4.0:
+  version "3.10.0"
+  resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-3.10.0.tgz#4f38f6cea2b9b17fa01490f23f1dc68ea65c1c38"
+  integrity sha1-Tzj2zqK5sX+gFJDyPx3GjqZcHDg=
+  dependencies:
+    autoprefixer "^6.3.1"
+    decamelize "^1.1.2"
+    defined "^1.0.0"
+    has "^1.0.1"
+    object-assign "^4.0.1"
+    postcss "^5.0.14"
+    postcss-calc "^5.2.0"
+    postcss-colormin "^2.1.8"
+    postcss-convert-values "^2.3.4"
+    postcss-discard-comments "^2.0.4"
+    postcss-discard-duplicates "^2.0.1"
+    postcss-discard-empty "^2.0.1"
+    postcss-discard-overridden "^0.1.1"
+    postcss-discard-unused "^2.2.1"
+    postcss-filter-plugins "^2.0.0"
+    postcss-merge-idents "^2.1.5"
+    postcss-merge-longhand "^2.0.1"
+    postcss-merge-rules "^2.0.3"
+    postcss-minify-font-values "^1.0.2"
+    postcss-minify-gradients "^1.0.1"
+    postcss-minify-params "^1.0.4"
+    postcss-minify-selectors "^2.0.4"
+    postcss-normalize-charset "^1.1.0"
+    postcss-normalize-url "^3.0.7"
+    postcss-ordered-values "^2.1.0"
+    postcss-reduce-idents "^2.2.2"
+    postcss-reduce-initial "^1.0.0"
+    postcss-reduce-transforms "^1.0.3"
+    postcss-svgo "^2.1.1"
+    postcss-unique-selectors "^2.0.2"
+    postcss-value-parser "^3.2.3"
+    postcss-zindex "^2.0.1"
+
+csso@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/csso/-/csso-4.0.2.tgz#e5f81ab3a56b8eefb7f0092ce7279329f454de3d"
+  integrity sha512-kS7/oeNVXkHWxby5tHVxlhjizRCSv8QdU7hB2FpdAibDU8FjTAolhNjKNTiLzXtUrKT6HwClE81yXwEk1309wg==
+  dependencies:
+    css-tree "1.0.0-alpha.37"
+
+csso@~2.3.1:
+  version "2.3.2"
+  resolved "https://registry.yarnpkg.com/csso/-/csso-2.3.2.tgz#ddd52c587033f49e94b71fc55569f252e8ff5f85"
+  integrity sha1-3dUsWHAz9J6Utx/FVWnyUuj/X4U=
+  dependencies:
+    clap "^1.0.9"
+    source-map "^0.5.3"
+
+csstype@^2.2.0, csstype@^2.6.7:
+  version "2.6.7"
+  resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.7.tgz#20b0024c20b6718f4eda3853a1f5a1cce7f5e4a5"
+  integrity sha512-9Mcn9sFbGBAdmimWb2gLVDtFJzeKtDGIr76TUqmjZrw9LFXBMSU70lcs+C0/7fyCd6iBDqmksUcCOUIkisPHsQ==
+
+cyclist@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9"
+  integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=
+
+debug@2.6.9, debug@^2.2.0, debug@^2.3.3:
+  version "2.6.9"
+  resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
+  integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
+  dependencies:
+    ms "2.0.0"
+
+debug@^3.0.0, debug@^3.1.1, debug@^3.2.5, debug@^3.2.6:
+  version "3.2.6"
+  resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
+  integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==
+  dependencies:
+    ms "^2.1.1"
+
+debug@^4.1.0, debug@^4.1.1:
+  version "4.1.1"
+  resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791"
+  integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==
+  dependencies:
+    ms "^2.1.1"
+
+decamelize@^1.1.2, decamelize@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
+  integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=
+
+decode-uri-component@^0.2.0:
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
+  integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=
+
+decompress-response@^3.3.0:
+  version "3.3.0"
+  resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3"
+  integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=
+  dependencies:
+    mimic-response "^1.0.0"
+
+deep-equal@^1.0.1:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.0.tgz#3103cdf8ab6d32cf4a8df7865458f2b8d33f3745"
+  integrity sha512-ZbfWJq/wN1Z273o7mUSjILYqehAktR2NVoSrOukDkU9kg2v/Uv89yU4Cvz8seJeAmtN5oqiefKq8FPuXOboqLw==
+  dependencies:
+    is-arguments "^1.0.4"
+    is-date-object "^1.0.1"
+    is-regex "^1.0.4"
+    object-is "^1.0.1"
+    object-keys "^1.1.1"
+    regexp.prototype.flags "^1.2.0"
+
+deep-extend@^0.6.0:
+  version "0.6.0"
+  resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac"
+  integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==
+
+default-gateway@^4.2.0:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-4.2.0.tgz#167104c7500c2115f6dd69b0a536bb8ed720552b"
+  integrity sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA==
+  dependencies:
+    execa "^1.0.0"
+    ip-regex "^2.1.0"
+
+defer-to-connect@^1.0.1:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.0.tgz#b41bd7efa8508cef13f8456975f7a278c72833fd"
+  integrity sha512-WE2sZoctWm/v4smfCAdjYbrfS55JiMRdlY9ZubFhsYbteCK9+BvAx4YV7nPjYM6ZnX5BcoVKwfmyx9sIFTgQMQ==
+
+define-properties@^1.1.2, define-properties@^1.1.3:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1"
+  integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==
+  dependencies:
+    object-keys "^1.0.12"
+
+define-property@^0.2.5:
+  version "0.2.5"
+  resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116"
+  integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=
+  dependencies:
+    is-descriptor "^0.1.0"
+
+define-property@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6"
+  integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY=
+  dependencies:
+    is-descriptor "^1.0.0"
+
+define-property@^2.0.2:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d"
+  integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==
+  dependencies:
+    is-descriptor "^1.0.2"
+    isobject "^3.0.1"
+
+defined@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693"
+  integrity sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=
+
+del@^4.1.1:
+  version "4.1.1"
+  resolved "https://registry.yarnpkg.com/del/-/del-4.1.1.tgz#9e8f117222ea44a31ff3a156c049b99052a9f0b4"
+  integrity sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==
+  dependencies:
+    "@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"
+
+delegates@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
+  integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=
+
+depd@~1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
+  integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=
+
+des.js@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.0.tgz#c074d2e2aa6a8a9a07dbd61f9a15c2cd83ec8ecc"
+  integrity sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=
+  dependencies:
+    inherits "^2.0.1"
+    minimalistic-assert "^1.0.0"
+
+destroy@~1.0.4:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80"
+  integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=
+
+detect-file@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7"
+  integrity sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=
+
+detect-libc@^1.0.2:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b"
+  integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=
+
+detect-node@^2.0.4:
+  version "2.0.4"
+  resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.4.tgz#014ee8f8f669c5c58023da64b8179c083a28c46c"
+  integrity sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==
+
+diffie-hellman@^5.0.0:
+  version "5.0.3"
+  resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875"
+  integrity sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==
+  dependencies:
+    bn.js "^4.1.0"
+    miller-rabin "^4.0.0"
+    randombytes "^2.0.0"
+
+dmg-builder@21.2.0:
+  version "21.2.0"
+  resolved "https://registry.yarnpkg.com/dmg-builder/-/dmg-builder-21.2.0.tgz#a9c883557cacb9abdb66c7133b30fe921c1a3ba7"
+  integrity sha512-9cJEclnGy7EyKFCoHDYDf54pub/t92CQapyiUxU0w9Bj2vUvfoDagP1PMiX4XD5rPp96141h9A+QN0OB4VgvQg==
+  dependencies:
+    app-builder-lib "~21.2.0"
+    bluebird-lst "^1.0.9"
+    builder-util "~21.2.0"
+    fs-extra "^8.1.0"
+    iconv-lite "^0.5.0"
+    js-yaml "^3.13.1"
+    sanitize-filename "^1.6.2"
+
+dns-equal@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d"
+  integrity sha1-s55/HabrCnW6nBcySzR1PEfgZU0=
+
+dns-packet@^1.3.1:
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-1.3.1.tgz#12aa426981075be500b910eedcd0b47dd7deda5a"
+  integrity sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==
+  dependencies:
+    ip "^1.1.0"
+    safe-buffer "^5.0.1"
+
+dns-txt@^2.0.2:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/dns-txt/-/dns-txt-2.0.2.tgz#b91d806f5d27188e4ab3e7d107d881a1cc4642b6"
+  integrity sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=
+  dependencies:
+    buffer-indexof "^1.0.0"
+
+dom-converter@^0.2:
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.2.0.tgz#6721a9daee2e293682955b6afe416771627bb768"
+  integrity sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==
+  dependencies:
+    utila "~0.4"
+
+dom-helpers@^5.0.0:
+  version "5.1.3"
+  resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.1.3.tgz#7233248eb3a2d1f74aafca31e52c5299cc8ce821"
+  integrity sha512-nZD1OtwfWGRBWlpANxacBEZrEuLa16o1nh7YopFWeoF68Zt8GGEmzHu6Xv4F3XaFIC+YXtTLrzgqKxFgLEe4jw==
+  dependencies:
+    "@babel/runtime" "^7.6.3"
+    csstype "^2.6.7"
+
+dom-serializer@0:
+  version "0.2.1"
+  resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.1.tgz#13650c850daffea35d8b626a4cfc4d3a17643fdb"
+  integrity sha512-sK3ujri04WyjwQXVoK4PU3y8ula1stq10GJZpqHIUgoGZdsGzAGu65BnU3d08aTVSvO7mGPZUc0wTEDL+qGE0Q==
+  dependencies:
+    domelementtype "^2.0.1"
+    entities "^2.0.0"
+
+dom-walk@^0.1.0:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.1.tgz#672226dc74c8f799ad35307df936aba11acd6018"
+  integrity sha1-ZyIm3HTI95mtNTB9+TaroRrNYBg=
+
+domain-browser@^1.1.1:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda"
+  integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==
+
+domelementtype@1, domelementtype@^1.3.1:
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f"
+  integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==
+
+domelementtype@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.0.1.tgz#1f8bdfe91f5a78063274e803b4bdcedf6e94f94d"
+  integrity sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ==
+
+domhandler@^2.3.0:
+  version "2.4.2"
+  resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.2.tgz#8805097e933d65e85546f726d60f5eb88b44f803"
+  integrity sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==
+  dependencies:
+    domelementtype "1"
+
+domutils@1.5.1:
+  version "1.5.1"
+  resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf"
+  integrity sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=
+  dependencies:
+    dom-serializer "0"
+    domelementtype "1"
+
+domutils@^1.5.1, domutils@^1.7.0:
+  version "1.7.0"
+  resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a"
+  integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==
+  dependencies:
+    dom-serializer "0"
+    domelementtype "1"
+
+dot-prop@^4.1.0:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.2.0.tgz#1f19e0c2e1aa0e32797c49799f2837ac6af69c57"
+  integrity sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==
+  dependencies:
+    is-obj "^1.0.0"
+
+dotenv-expand@^5.1.0:
+  version "5.1.0"
+  resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-5.1.0.tgz#3fbaf020bfd794884072ea26b1e9791d45a629f0"
+  integrity sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==
+
+dotenv@^8.0.0:
+  version "8.2.0"
+  resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a"
+  integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==
+
+duplexer3@^0.1.4:
+  version "0.1.4"
+  resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2"
+  integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=
+
+duplexify@^3.4.2, duplexify@^3.6.0:
+  version "3.7.1"
+  resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309"
+  integrity sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==
+  dependencies:
+    end-of-stream "^1.0.0"
+    inherits "^2.0.1"
+    readable-stream "^2.0.0"
+    stream-shift "^1.0.0"
+
+ee-first@1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
+  integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=
+
+ejs@^2.6.2:
+  version "2.7.1"
+  resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.7.1.tgz#5b5ab57f718b79d4aca9254457afecd36fa80228"
+  integrity sha512-kS/gEPzZs3Y1rRsbGX4UOSjtP/CeJP0CxSNZHYxGfVM/VgLcv0ZqM7C45YyTj2DI2g7+P9Dd24C+IMIg6D0nYQ==
+
+electron-builder@^21.2.0:
+  version "21.2.0"
+  resolved "https://registry.yarnpkg.com/electron-builder/-/electron-builder-21.2.0.tgz#b68ec4def713fc0b8602654ce842f972432f50c5"
+  integrity sha512-x8EXrqFbAb2L3N22YlGar3dGh8vwptbB3ovo3OF6K7NTpcsmM2zEoJv7GhFyX73rNzSG2HaWpXwGAtOp2JWiEw==
+  dependencies:
+    app-builder-lib "21.2.0"
+    bluebird-lst "^1.0.9"
+    builder-util "21.2.0"
+    builder-util-runtime "8.3.0"
+    chalk "^2.4.2"
+    dmg-builder "21.2.0"
+    fs-extra "^8.1.0"
+    is-ci "^2.0.0"
+    lazy-val "^1.0.4"
+    read-config-file "5.0.0"
+    sanitize-filename "^1.6.2"
+    update-notifier "^3.0.1"
+    yargs "^13.3.0"
+
+electron-devtools-installer@^2.2.4:
+  version "2.2.4"
+  resolved "https://registry.yarnpkg.com/electron-devtools-installer/-/electron-devtools-installer-2.2.4.tgz#261a50337e37121d338b966f07922eb4939a8763"
+  integrity sha512-b5kcM3hmUqn64+RUcHjjr8ZMpHS2WJ5YO0pnG9+P/RTdx46of/JrEjuciHWux6pE+On6ynWhHJF53j/EDJN0PA==
+  dependencies:
+    "7zip" "0.0.6"
+    cross-unzip "0.0.2"
+    rimraf "^2.5.2"
+    semver "^5.3.0"
+
+electron-publish@21.2.0:
+  version "21.2.0"
+  resolved "https://registry.yarnpkg.com/electron-publish/-/electron-publish-21.2.0.tgz#cc225cb46aa62e74b899f2f7299b396c9802387d"
+  integrity sha512-mWavuoWJe87iaeKd0I24dNWIaR+0yRzshjNVqGyK019H766fsPWl3caQJnVKFaEyrZRP397v4JZVG0e7s16AxA==
+  dependencies:
+    bluebird-lst "^1.0.9"
+    builder-util "~21.2.0"
+    builder-util-runtime "8.3.0"
+    chalk "^2.4.2"
+    fs-extra "^8.1.0"
+    lazy-val "^1.0.4"
+    mime "^2.4.4"
+
+electron-to-chromium@^1.2.7, electron-to-chromium@^1.3.295:
+  version "1.3.306"
+  resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.306.tgz#e8265301d053d5f74e36cb876486830261fbe946"
+  integrity sha512-frDqXvrIROoYvikSKTIKbHbzO6M3/qC6kCIt/1FOa9kALe++c4VAJnwjSFvf1tYLEUsP2n9XZ4XSCyqc3l7A/A==
+
+electron-to-chromium@^1.3.322:
+  version "1.3.322"
+  resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.322.tgz#a6f7e1c79025c2b05838e8e344f6e89eb83213a8"
+  integrity sha512-Tc8JQEfGQ1MzfSzI/bTlSr7btJv/FFO7Yh6tanqVmIWOuNCu6/D1MilIEgLtmWqIrsv+o4IjpLAhgMBr/ncNAA==
+
+electron-webpack-js@~2.3.2:
+  version "2.3.4"
+  resolved "https://registry.yarnpkg.com/electron-webpack-js/-/electron-webpack-js-2.3.4.tgz#f1ffb076530559e48dd5e1b961e8e5be299a5b6c"
+  integrity sha512-lHP/hqNFkf7WXuo5ABfcpbm920cQdymNI2DnTHH1ZqizJMZE8dPCAn6VVZiY95Z8l5bN9qFz8idKCfuQMN5NCQ==
+  dependencies:
+    "@babel/core" "^7.5.0"
+    "@babel/plugin-syntax-dynamic-import" "^7.2.0"
+    "@babel/preset-env" "^7.5.3"
+    babel-loader "^8.0.6"
+    babel-plugin-component "^1.1.1"
+
+electron-webpack@^2.7.4:
+  version "2.7.4"
+  resolved "https://registry.yarnpkg.com/electron-webpack/-/electron-webpack-2.7.4.tgz#f1deed156375fbd846ee718bfc9ccf93a2e27207"
+  integrity sha512-0278nUbG4bANuvrWpLq/CYKJhGwR1gzhAI5IInasbAV8FVA3PyAvu/KMZCZLuVbTxGmWStPg2pdLf1a9alfLCQ==
+  dependencies:
+    "@types/webpack-env" "^1.13.9"
+    async-exit-hook "^2.0.1"
+    bluebird "^3.5.5"
+    chalk "^2.4.2"
+    crocket "^0.9.11"
+    css-hot-loader "^1.4.4"
+    css-loader "^3.0.0"
+    debug "^4.1.1"
+    dotenv "^8.0.0"
+    dotenv-expand "^5.1.0"
+    electron-devtools-installer "^2.2.4"
+    electron-webpack-js "~2.3.2"
+    file-loader "^4.0.0"
+    fs-extra "^8.1.0"
+    html-loader "^1.0.0-alpha.0"
+    html-webpack-plugin "^3.2.0"
+    lazy-val "^1.0.4"
+    mini-css-extract-plugin "^0.7.0"
+    node-loader "^0.6.0"
+    read-config-file "^4.0.1"
+    semver "^6.2.0"
+    source-map-support "^0.5.12"
+    style-loader "^0.23.1"
+    terser-webpack-plugin "^1.3.0"
+    url-loader "^2.0.1"
+    webpack-cli "^3.3.5"
+    webpack-dev-server "^3.7.2"
+    webpack-merge "^4.2.1"
+    yargs "^13.2.4"
+
+electron@^7:
+  version "7.1.1"
+  resolved "https://registry.yarnpkg.com/electron/-/electron-7.1.1.tgz#98be17d850e64689a09ab6dd1e437b36307f2fcf"
+  integrity sha512-NJPv4SuMJlRUtXBd/Ey9XKSLOZ4+hxsOrHHPXwrBQNNdeZesoSrTMgPymee/FwMRtrSt0Pz8NccEZUu/pxmbhQ==
+  dependencies:
+    "@electron/get" "^1.0.1"
+    "@types/node" "^12.0.12"
+    extract-zip "^1.0.3"
+
+elliptic@^6.0.0:
+  version "6.5.1"
+  resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.1.tgz#c380f5f909bf1b9b4428d028cd18d3b0efd6b52b"
+  integrity sha512-xvJINNLbTeWQjrl6X+7eQCrIy/YPv5XCpKW6kB5mKvtnGILoLDcySuwomfdzt0BMdLNVnuRNTuzKNHj0bva1Cg==
+  dependencies:
+    bn.js "^4.4.0"
+    brorand "^1.0.1"
+    hash.js "^1.0.0"
+    hmac-drbg "^1.0.0"
+    inherits "^2.0.1"
+    minimalistic-assert "^1.0.0"
+    minimalistic-crypto-utils "^1.0.0"
+
+emoji-regex@^7.0.1:
+  version "7.0.3"
+  resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156"
+  integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==
+
+emojis-list@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389"
+  integrity sha1-TapNnbAPmBmIDHn6RXrlsJof04k=
+
+encodeurl@^1.0.2, encodeurl@~1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
+  integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=
+
+end-of-stream@^1.0.0, end-of-stream@^1.1.0:
+  version "1.4.4"
+  resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
+  integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==
+  dependencies:
+    once "^1.4.0"
+
+enhanced-resolve@4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz#41c7e0bfdfe74ac1ffe1e57ad6a5c6c9f3742a7f"
+  integrity sha512-F/7vkyTtyc/llOIn8oWclcB25KdRaiPBpZYDgJHgh/UHtpgT2p2eldQgtQnLtUvfMKPKxbRaQM/hHkvLHt1Vng==
+  dependencies:
+    graceful-fs "^4.1.2"
+    memory-fs "^0.4.0"
+    tapable "^1.0.0"
+
+enhanced-resolve@^4.1.0:
+  version "4.1.1"
+  resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.1.1.tgz#2937e2b8066cd0fe7ce0990a98f0d71a35189f66"
+  integrity sha512-98p2zE+rL7/g/DzMHMTF4zZlCgeVdJ7yr6xzEpJRYwFYrGi9ANdn5DnJURg6RpBkyk60XYDnWIv51VfIhfNGuA==
+  dependencies:
+    graceful-fs "^4.1.2"
+    memory-fs "^0.5.0"
+    tapable "^1.0.0"
+
+entities@^1.1.1, entities@~1.1.1:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56"
+  integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==
+
+entities@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.0.tgz#68d6084cab1b079767540d80e56a39b423e4abf4"
+  integrity sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw==
+
+env-paths@^2.2.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.0.tgz#cdca557dc009152917d6166e2febe1f039685e43"
+  integrity sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA==
+
+errno@^0.1.3, errno@~0.1.7:
+  version "0.1.7"
+  resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618"
+  integrity sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==
+  dependencies:
+    prr "~1.0.1"
+
+es-abstract@^1.12.0, es-abstract@^1.5.1:
+  version "1.16.0"
+  resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.16.0.tgz#d3a26dc9c3283ac9750dca569586e976d9dcc06d"
+  integrity sha512-xdQnfykZ9JMEiasTAJZJdMWCQ1Vm00NBw79/AWi7ELfZuuPCSOMDZbT9mkOfSctVtfhb+sAAzrm+j//GjjLHLg==
+  dependencies:
+    es-to-primitive "^1.2.0"
+    function-bind "^1.1.1"
+    has "^1.0.3"
+    has-symbols "^1.0.0"
+    is-callable "^1.1.4"
+    is-regex "^1.0.4"
+    object-inspect "^1.6.0"
+    object-keys "^1.1.1"
+    string.prototype.trimleft "^2.1.0"
+    string.prototype.trimright "^2.1.0"
+
+es-to-primitive@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.0.tgz#edf72478033456e8dda8ef09e00ad9650707f377"
+  integrity sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==
+  dependencies:
+    is-callable "^1.1.4"
+    is-date-object "^1.0.1"
+    is-symbol "^1.0.2"
+
+es6-error@^4.1.1:
+  version "4.1.1"
+  resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d"
+  integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==
+
+escape-html@~1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
+  integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=
+
+escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
+  integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
+
+escape-string-regexp@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344"
+  integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==
+
+eslint-scope@^4.0.3:
+  version "4.0.3"
+  resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848"
+  integrity sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==
+  dependencies:
+    esrecurse "^4.1.0"
+    estraverse "^4.1.1"
+
+esprima@^2.6.0:
+  version "2.7.3"
+  resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581"
+  integrity sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=
+
+esprima@^4.0.0:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
+  integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
+
+esrecurse@^4.1.0:
+  version "4.2.1"
+  resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf"
+  integrity sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==
+  dependencies:
+    estraverse "^4.1.0"
+
+estraverse@^4.1.0, estraverse@^4.1.1:
+  version "4.3.0"
+  resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d"
+  integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==
+
+esutils@^2.0.0, esutils@^2.0.2:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
+  integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
+
+etag@~1.8.1:
+  version "1.8.1"
+  resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
+  integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=
+
+eventemitter3@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.0.tgz#d65176163887ee59f386d64c82610b696a4a74eb"
+  integrity sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg==
+
+events@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/events/-/events-3.0.0.tgz#9a0a0dfaf62893d92b875b8f2698ca4114973e88"
+  integrity sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA==
+
+eventsource@^1.0.7:
+  version "1.0.7"
+  resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-1.0.7.tgz#8fbc72c93fcd34088090bc0a4e64f4b5cee6d8d0"
+  integrity sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ==
+  dependencies:
+    original "^1.0.0"
+
+evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02"
+  integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==
+  dependencies:
+    md5.js "^1.3.4"
+    safe-buffer "^5.1.1"
+
+execa@^0.7.0:
+  version "0.7.0"
+  resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777"
+  integrity sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=
+  dependencies:
+    cross-spawn "^5.0.1"
+    get-stream "^3.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"
+
+execa@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8"
+  integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==
+  dependencies:
+    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"
+
+expand-brackets@^2.1.4:
+  version "2.1.4"
+  resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622"
+  integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI=
+  dependencies:
+    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"
+
+expand-tilde@^2.0.0, expand-tilde@^2.0.2:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/expand-tilde/-/expand-tilde-2.0.2.tgz#97e801aa052df02454de46b02bf621642cdc8502"
+  integrity sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=
+  dependencies:
+    homedir-polyfill "^1.0.1"
+
+express@^4.17.1:
+  version "4.17.1"
+  resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134"
+  integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==
+  dependencies:
+    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"
+
+extend-shallow@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f"
+  integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=
+  dependencies:
+    is-extendable "^0.1.0"
+
+extend-shallow@^3.0.0, extend-shallow@^3.0.2:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8"
+  integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=
+  dependencies:
+    assign-symbols "^1.0.0"
+    is-extendable "^1.0.1"
+
+extglob@^2.0.4:
+  version "2.0.4"
+  resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543"
+  integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==
+  dependencies:
+    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"
+
+extract-zip@^1.0.3:
+  version "1.6.7"
+  resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.6.7.tgz#a840b4b8af6403264c8db57f4f1a74333ef81fe9"
+  integrity sha1-qEC0uK9kAyZMjbV/Txp0Mz74H+k=
+  dependencies:
+    concat-stream "1.6.2"
+    debug "2.6.9"
+    mkdirp "0.5.1"
+    yauzl "2.4.1"
+
+fast-deep-equal@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49"
+  integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=
+
+fast-json-stable-stringify@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2"
+  integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I=
+
+fast-levenshtein@^2.0.6:
+  version "2.0.6"
+  resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
+  integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=
+
+faye-websocket@^0.10.0:
+  version "0.10.0"
+  resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4"
+  integrity sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=
+  dependencies:
+    websocket-driver ">=0.5.1"
+
+faye-websocket@~0.11.1:
+  version "0.11.3"
+  resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.3.tgz#5c0e9a8968e8912c286639fde977a8b209f2508e"
+  integrity sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA==
+  dependencies:
+    websocket-driver ">=0.5.1"
+
+fd-slicer@~1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.0.1.tgz#8b5bcbd9ec327c5041bf9ab023fd6750f1177e65"
+  integrity sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=
+  dependencies:
+    pend "~1.2.0"
+
+figgy-pudding@^3.5.1:
+  version "3.5.1"
+  resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790"
+  integrity sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==
+
+file-loader@^4.0.0:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-4.2.0.tgz#5fb124d2369d7075d70a9a5abecd12e60a95215e"
+  integrity sha512-+xZnaK5R8kBJrHK0/6HRlrKNamvVS5rjyuju+rnyxRGuwUJwpAMsVzUl5dz6rK8brkzjV6JpcFNjp6NqV0g1OQ==
+  dependencies:
+    loader-utils "^1.2.3"
+    schema-utils "^2.0.0"
+
+fill-range@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7"
+  integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=
+  dependencies:
+    extend-shallow "^2.0.1"
+    is-number "^3.0.0"
+    repeat-string "^1.6.1"
+    to-regex-range "^2.1.0"
+
+finalhandler@~1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d"
+  integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==
+  dependencies:
+    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"
+
+find-cache-dir@^2.0.0, find-cache-dir@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7"
+  integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==
+  dependencies:
+    commondir "^1.0.1"
+    make-dir "^2.0.0"
+    pkg-dir "^3.0.0"
+
+find-up@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73"
+  integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==
+  dependencies:
+    locate-path "^3.0.0"
+
+findup-sync@3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-3.0.0.tgz#17b108f9ee512dfb7a5c7f3c8b27ea9e1a9c08d1"
+  integrity sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==
+  dependencies:
+    detect-file "^1.0.0"
+    is-glob "^4.0.0"
+    micromatch "^3.0.4"
+    resolve-dir "^1.0.1"
+
+flatten@^1.0.2:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.3.tgz#c1283ac9f27b368abc1e36d1ff7b04501a30356b"
+  integrity sha512-dVsPA/UwQ8+2uoFe5GHtiBMu48dWLTdsuEd7CKGlZlD78r1TTWBvDuFaFGKCo/ZfEr95Uk56vZoX86OsHkUeIg==
+
+flush-write-stream@^1.0.0:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8"
+  integrity sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==
+  dependencies:
+    inherits "^2.0.3"
+    readable-stream "^2.3.6"
+
+follow-redirects@^1.0.0:
+  version "1.9.0"
+  resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.9.0.tgz#8d5bcdc65b7108fe1508649c79c12d732dcedb4f"
+  integrity sha512-CRcPzsSIbXyVDl0QI01muNDu69S8trU4jArW9LpOt2WtC6LyUJetcIrmfHsRBx7/Jb6GHJUiuqyYxPooFfNt6A==
+  dependencies:
+    debug "^3.0.0"
+
+for-in@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
+  integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=
+
+forwarded@~0.1.2:
+  version "0.1.2"
+  resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84"
+  integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=
+
+fragment-cache@^0.2.1:
+  version "0.2.1"
+  resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19"
+  integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=
+  dependencies:
+    map-cache "^0.2.2"
+
+fresh@0.5.2:
+  version "0.5.2"
+  resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
+  integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=
+
+from2@^2.1.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af"
+  integrity sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=
+  dependencies:
+    inherits "^2.0.1"
+    readable-stream "^2.0.0"
+
+fs-extra@^8.1.0:
+  version "8.1.0"
+  resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0"
+  integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==
+  dependencies:
+    graceful-fs "^4.2.0"
+    jsonfile "^4.0.0"
+    universalify "^0.1.0"
+
+fs-minipass@^1.2.5:
+  version "1.2.7"
+  resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7"
+  integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==
+  dependencies:
+    minipass "^2.6.0"
+
+fs-write-stream-atomic@^1.0.8:
+  version "1.0.10"
+  resolved "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz#b47df53493ef911df75731e70a9ded0189db40c9"
+  integrity sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=
+  dependencies:
+    graceful-fs "^4.1.2"
+    iferr "^0.1.5"
+    imurmurhash "^0.1.4"
+    readable-stream "1 || 2"
+
+fs.realpath@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
+  integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
+
+fsevents@^1.2.7:
+  version "1.2.9"
+  resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.9.tgz#3f5ed66583ccd6f400b5a00db6f7e861363e388f"
+  integrity sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==
+  dependencies:
+    nan "^2.12.1"
+    node-pre-gyp "^0.12.0"
+
+function-bind@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
+  integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
+
+gauge@~2.7.3:
+  version "2.7.4"
+  resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7"
+  integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=
+  dependencies:
+    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"
+
+get-caller-file@^1.0.1:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a"
+  integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==
+
+get-caller-file@^2.0.1:
+  version "2.0.5"
+  resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
+  integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
+
+get-stream@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14"
+  integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=
+
+get-stream@^4.0.0, get-stream@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5"
+  integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==
+  dependencies:
+    pump "^3.0.0"
+
+get-stream@^5.1.0:
+  version "5.1.0"
+  resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.1.0.tgz#01203cdc92597f9b909067c3e656cc1f4d3c4dc9"
+  integrity sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==
+  dependencies:
+    pump "^3.0.0"
+
+get-value@^2.0.3, get-value@^2.0.6:
+  version "2.0.6"
+  resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28"
+  integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=
+
+glob-parent@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae"
+  integrity sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=
+  dependencies:
+    is-glob "^3.1.0"
+    path-dirname "^1.0.0"
+
+glob@^7.0.3, glob@^7.1.3, glob@^7.1.4:
+  version "7.1.6"
+  resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
+  integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
+  dependencies:
+    fs.realpath "^1.0.0"
+    inflight "^1.0.4"
+    inherits "2"
+    minimatch "^3.0.4"
+    once "^1.3.0"
+    path-is-absolute "^1.0.0"
+
+global-agent@^2.0.2:
+  version "2.1.5"
+  resolved "https://registry.yarnpkg.com/global-agent/-/global-agent-2.1.5.tgz#0e9b8367f7068bb6fa360a8f920499e1d873762f"
+  integrity sha512-pYJjCxxNBzYxo6iNO62JZn8iCFVbvpiM0zE4w/G5hBNIvLjnvzIeCVQPMKc3aK8ju5L7Q8NNI/oBSosU0eeSYw==
+  dependencies:
+    boolean "^2.0.2"
+    core-js "^3.3.3"
+    es6-error "^4.1.1"
+    matcher "^2.0.0"
+    roarr "^2.14.2"
+    semver "^6.3.0"
+    serialize-error "^5.0.0"
+
+global-dirs@^0.1.0:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-0.1.1.tgz#b319c0dd4607f353f3be9cca4c72fc148c49f445"
+  integrity sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=
+  dependencies:
+    ini "^1.3.4"
+
+global-modules@2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780"
+  integrity sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==
+  dependencies:
+    global-prefix "^3.0.0"
+
+global-modules@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-1.0.0.tgz#6d770f0eb523ac78164d72b5e71a8877265cc3ea"
+  integrity sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==
+  dependencies:
+    global-prefix "^1.0.1"
+    is-windows "^1.0.1"
+    resolve-dir "^1.0.0"
+
+global-prefix@^1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-1.0.2.tgz#dbf743c6c14992593c655568cb66ed32c0122ebe"
+  integrity sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=
+  dependencies:
+    expand-tilde "^2.0.2"
+    homedir-polyfill "^1.0.1"
+    ini "^1.3.4"
+    is-windows "^1.0.1"
+    which "^1.2.14"
+
+global-prefix@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-3.0.0.tgz#fc85f73064df69f50421f47f883fe5b913ba9b97"
+  integrity sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==
+  dependencies:
+    ini "^1.3.5"
+    kind-of "^6.0.2"
+    which "^1.3.1"
+
+global-tunnel-ng@^2.7.1:
+  version "2.7.1"
+  resolved "https://registry.yarnpkg.com/global-tunnel-ng/-/global-tunnel-ng-2.7.1.tgz#d03b5102dfde3a69914f5ee7d86761ca35d57d8f"
+  integrity sha512-4s+DyciWBV0eK148wqXxcmVAbFVPqtc3sEtUE/GTQfuU80rySLcMhUmHKSHI7/LDj8q0gDYI1lIhRRB7ieRAqg==
+  dependencies:
+    encodeurl "^1.0.2"
+    lodash "^4.17.10"
+    npm-conf "^1.1.3"
+    tunnel "^0.0.6"
+
+global@^4.3.0:
+  version "4.4.0"
+  resolved "https://registry.yarnpkg.com/global/-/global-4.4.0.tgz#3e7b105179006a323ed71aafca3e9c57a5cc6406"
+  integrity sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==
+  dependencies:
+    min-document "^2.19.0"
+    process "^0.11.10"
+
+globals@^11.1.0:
+  version "11.12.0"
+  resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
+  integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
+
+globalthis@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.0.tgz#c5fb98213a9b4595f59cf3e7074f141b4169daae"
+  integrity sha512-vcCAZTJ3r5Qcu5l8/2oyVdoFwxKgfYnMTR2vwWeux/NAVZK3PwcMaWkdUIn4GJbmKuRK7xcvDsLuK+CKcXyodg==
+  dependencies:
+    define-properties "^1.1.2"
+    function-bind "^1.1.1"
+    object-keys "^1.0.12"
+
+globby@^6.1.0:
+  version "6.1.0"
+  resolved "https://registry.yarnpkg.com/globby/-/globby-6.1.0.tgz#f5a6d70e8395e21c858fb0489d64df02424d506c"
+  integrity sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=
+  dependencies:
+    array-union "^1.0.1"
+    glob "^7.0.3"
+    object-assign "^4.0.1"
+    pify "^2.0.0"
+    pinkie-promise "^2.0.0"
+
+got@^9.6.0:
+  version "9.6.0"
+  resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85"
+  integrity sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==
+  dependencies:
+    "@sindresorhus/is" "^0.14.0"
+    "@szmarczak/http-timer" "^1.1.2"
+    cacheable-request "^6.0.0"
+    decompress-response "^3.3.0"
+    duplexer3 "^0.1.4"
+    get-stream "^4.1.0"
+    lowercase-keys "^1.0.1"
+    mimic-response "^1.0.1"
+    p-cancelable "^1.0.0"
+    to-readable-stream "^1.0.0"
+    url-parse-lax "^3.0.0"
+
+graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0:
+  version "4.2.3"
+  resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423"
+  integrity sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==
+
+handle-thing@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.0.tgz#0e039695ff50c93fc288557d696f3c1dc6776754"
+  integrity sha512-d4sze1JNC454Wdo2fkuyzCr6aHcbL6PGGuFAz0Li/NcOm1tCHGnWDRmJP85dh9IhQErTc2svWFEX5xHIOo//kQ==
+
+has-ansi@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91"
+  integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=
+  dependencies:
+    ansi-regex "^2.0.0"
+
+has-flag@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa"
+  integrity sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=
+
+has-flag@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
+  integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0=
+
+has-symbols@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.0.tgz#ba1a8f1af2a0fc39650f5c850367704122063b44"
+  integrity sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=
+
+has-unicode@^2.0.0:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9"
+  integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=
+
+has-value@^0.3.1:
+  version "0.3.1"
+  resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f"
+  integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=
+  dependencies:
+    get-value "^2.0.3"
+    has-values "^0.1.4"
+    isobject "^2.0.0"
+
+has-value@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177"
+  integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=
+  dependencies:
+    get-value "^2.0.6"
+    has-values "^1.0.0"
+    isobject "^3.0.0"
+
+has-values@^0.1.4:
+  version "0.1.4"
+  resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771"
+  integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E=
+
+has-values@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f"
+  integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=
+  dependencies:
+    is-number "^3.0.0"
+    kind-of "^4.0.0"
+
+has-yarn@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/has-yarn/-/has-yarn-2.1.0.tgz#137e11354a7b5bf11aa5cb649cf0c6f3ff2b2e77"
+  integrity sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==
+
+has@^1.0.1, has@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
+  integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==
+  dependencies:
+    function-bind "^1.1.1"
+
+hash-base@^3.0.0:
+  version "3.0.4"
+  resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.0.4.tgz#5fc8686847ecd73499403319a6b0a3f3f6ae4918"
+  integrity sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=
+  dependencies:
+    inherits "^2.0.1"
+    safe-buffer "^5.0.1"
+
+hash.js@^1.0.0, hash.js@^1.0.3:
+  version "1.1.7"
+  resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42"
+  integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==
+  dependencies:
+    inherits "^2.0.3"
+    minimalistic-assert "^1.0.1"
+
+he@1.2.x:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
+  integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
+
+hmac-drbg@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1"
+  integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=
+  dependencies:
+    hash.js "^1.0.3"
+    minimalistic-assert "^1.0.0"
+    minimalistic-crypto-utils "^1.0.1"
+
+hoist-non-react-statics@^3.3.0:
+  version "3.3.0"
+  resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.0.tgz#b09178f0122184fb95acf525daaecb4d8f45958b"
+  integrity sha512-0XsbTXxgiaCDYDIWFcwkmerZPSwywfUqYmwT4jzewKTQSWoE6FCMoUVOeBJWK3E/CrWbxRG3m5GzY4lnIwGRBA==
+  dependencies:
+    react-is "^16.7.0"
+
+homedir-polyfill@^1.0.1:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz#743298cef4e5af3e194161fbadcc2151d3a058e8"
+  integrity sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==
+  dependencies:
+    parse-passwd "^1.0.0"
+
+hosted-git-info@^2.1.4, hosted-git-info@^2.7.1:
+  version "2.8.5"
+  resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.5.tgz#759cfcf2c4d156ade59b0b2dfabddc42a6b9c70c"
+  integrity sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg==
+
+hpack.js@^2.1.6:
+  version "2.1.6"
+  resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2"
+  integrity sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=
+  dependencies:
+    inherits "^2.0.1"
+    obuf "^1.0.0"
+    readable-stream "^2.0.1"
+    wbuf "^1.1.0"
+
+html-comment-regex@^1.1.0:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.2.tgz#97d4688aeb5c81886a364faa0cad1dda14d433a7"
+  integrity sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==
+
+html-entities@^1.2.1:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.1.tgz#0df29351f0721163515dfb9e5543e5f6eed5162f"
+  integrity sha1-DfKTUfByEWNRXfueVUPl9u7VFi8=
+
+html-loader@^1.0.0-alpha.0:
+  version "1.0.0-alpha.0"
+  resolved "https://registry.yarnpkg.com/html-loader/-/html-loader-1.0.0-alpha.0.tgz#3f4ae7b490a587619be6d1eaa8ce16683580c642"
+  integrity sha512-KcuaIRWTU0kFjOJCs32a3JsGNCWkeOak0/F/uvJNp3x/N4McXdqHpcK64cYTozK7QLPKKtUqb9h7wR9K9rYRkg==
+  dependencies:
+    "@posthtml/esm" "^1.0.0"
+    htmlnano "^0.1.6"
+    loader-utils "^1.1.0"
+    posthtml "^0.11.2"
+    schema-utils "^0.4.3"
+
+html-minifier@^3.2.3:
+  version "3.5.21"
+  resolved "https://registry.yarnpkg.com/html-minifier/-/html-minifier-3.5.21.tgz#d0040e054730e354db008463593194015212d20c"
+  integrity sha512-LKUKwuJDhxNa3uf/LPR/KVjm/l3rBqtYeCOAekvG8F1vItxMUpueGd94i/asDDr8/1u7InxzFA5EeGjhhG5mMA==
+  dependencies:
+    camel-case "3.0.x"
+    clean-css "4.2.x"
+    commander "2.17.x"
+    he "1.2.x"
+    param-case "2.1.x"
+    relateurl "0.2.x"
+    uglify-js "3.4.x"
+
+html-webpack-plugin@^3.2.0:
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz#b01abbd723acaaa7b37b6af4492ebda03d9dd37b"
+  integrity sha1-sBq71yOsqqeze2r0SS69oD2d03s=
+  dependencies:
+    html-minifier "^3.2.3"
+    loader-utils "^0.2.16"
+    lodash "^4.17.3"
+    pretty-error "^2.0.2"
+    tapable "^1.0.0"
+    toposort "^1.0.0"
+    util.promisify "1.0.0"
+
+htmlnano@^0.1.6:
+  version "0.1.10"
+  resolved "https://registry.yarnpkg.com/htmlnano/-/htmlnano-0.1.10.tgz#a0a548eb4c76ae2cf2423ec7a25c881734d3dea6"
+  integrity sha512-eTEUzz8VdWYp+w/KUdb99kwao4reR64epUySyZkQeepcyzPQ2n2EPWzibf6QDxmkGy10Kr+CKxYqI3izSbmhJQ==
+  dependencies:
+    cssnano "^3.4.0"
+    object-assign "^4.0.1"
+    posthtml "^0.11.3"
+    posthtml-render "^1.1.4"
+    svgo "^1.0.5"
+    terser "^3.8.1"
+
+htmlparser2@^3.3.0, htmlparser2@^3.9.2:
+  version "3.10.1"
+  resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.10.1.tgz#bd679dc3f59897b6a34bb10749c855bb53a9392f"
+  integrity sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==
+  dependencies:
+    domelementtype "^1.3.1"
+    domhandler "^2.3.0"
+    domutils "^1.5.1"
+    entities "^1.1.1"
+    inherits "^2.0.1"
+    readable-stream "^3.1.1"
+
+http-cache-semantics@^4.0.0:
+  version "4.0.3"
+  resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.0.3.tgz#495704773277eeef6e43f9ab2c2c7d259dda25c5"
+  integrity sha512-TcIMG3qeVLgDr1TEd2XvHaTnMPwYQUQMIBLy+5pLSDKYFc7UIqj39w8EGzZkaxoLv/l2K8HaI0t5AVA+YYgUew==
+
+http-deceiver@^1.2.7:
+  version "1.2.7"
+  resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87"
+  integrity sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=
+
+http-errors@1.7.2:
+  version "1.7.2"
+  resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f"
+  integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==
+  dependencies:
+    depd "~1.1.2"
+    inherits "2.0.3"
+    setprototypeof "1.1.1"
+    statuses ">= 1.5.0 < 2"
+    toidentifier "1.0.0"
+
+http-errors@~1.6.2:
+  version "1.6.3"
+  resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d"
+  integrity sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=
+  dependencies:
+    depd "~1.1.2"
+    inherits "2.0.3"
+    setprototypeof "1.1.0"
+    statuses ">= 1.4.0 < 2"
+
+http-errors@~1.7.2:
+  version "1.7.3"
+  resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06"
+  integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==
+  dependencies:
+    depd "~1.1.2"
+    inherits "2.0.4"
+    setprototypeof "1.1.1"
+    statuses ">= 1.5.0 < 2"
+    toidentifier "1.0.0"
+
+"http-parser-js@>=0.4.0 <0.4.11":
+  version "0.4.10"
+  resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.4.10.tgz#92c9c1374c35085f75db359ec56cc257cbb93fa4"
+  integrity sha1-ksnBN0w1CF912zWexWzCV8u5P6Q=
+
+http-proxy-middleware@0.19.1:
+  version "0.19.1"
+  resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz#183c7dc4aa1479150306498c210cdaf96080a43a"
+  integrity sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==
+  dependencies:
+    http-proxy "^1.17.0"
+    is-glob "^4.0.0"
+    lodash "^4.17.11"
+    micromatch "^3.1.10"
+
+http-proxy@^1.17.0:
+  version "1.18.0"
+  resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.0.tgz#dbe55f63e75a347db7f3d99974f2692a314a6a3a"
+  integrity sha512-84I2iJM/n1d4Hdgc6y2+qY5mDaz2PUVjlg9znE9byl+q0uC3DeByqBGReQu5tpLK0TAqTIXScRUV+dg7+bUPpQ==
+  dependencies:
+    eventemitter3 "^4.0.0"
+    follow-redirects "^1.0.0"
+    requires-port "^1.0.0"
+
+https-browserify@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73"
+  integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=
+
+iconv-lite@0.4.24, iconv-lite@^0.4.4:
+  version "0.4.24"
+  resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
+  integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
+  dependencies:
+    safer-buffer ">= 2.1.2 < 3"
+
+iconv-lite@^0.5.0:
+  version "0.5.0"
+  resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.5.0.tgz#59cdde0a2a297cc2aeb0c6445a195ee89f127550"
+  integrity sha512-NnEhI9hIEKHOzJ4f697DMz9IQEXr/MMJ5w64vN2/4Ai+wRnvV7SBrL0KLoRlwaKVghOc7LQ5YkPLuX146b6Ydw==
+  dependencies:
+    safer-buffer ">= 2.1.2 < 3"
+
+icss-utils@^4.0.0, icss-utils@^4.1.1:
+  version "4.1.1"
+  resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-4.1.1.tgz#21170b53789ee27447c2f47dd683081403f9a467"
+  integrity sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA==
+  dependencies:
+    postcss "^7.0.14"
+
+ieee754@^1.1.4:
+  version "1.1.13"
+  resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84"
+  integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==
+
+iferr@^0.1.5:
+  version "0.1.5"
+  resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501"
+  integrity sha1-xg7taebY/bazEEofy8ocGS3FtQE=
+
+ignore-walk@^3.0.1:
+  version "3.0.3"
+  resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.3.tgz#017e2447184bfeade7c238e4aefdd1e8f95b1e37"
+  integrity sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==
+  dependencies:
+    minimatch "^3.0.4"
+
+immutable@^4.0.0-rc.12:
+  version "4.0.0-rc.12"
+  resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.0.0-rc.12.tgz#ca59a7e4c19ae8d9bf74a97bdf0f6e2f2a5d0217"
+  integrity sha512-0M2XxkZLx/mi3t8NVwIm1g8nHoEmM9p9UBl/G9k4+hm0kBgOVdMV/B3CY5dQ8qG8qc80NN4gDV4HQv6FTJ5q7A==
+
+import-lazy@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43"
+  integrity sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=
+
+import-local@2.0.0, import-local@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/import-local/-/import-local-2.0.0.tgz#55070be38a5993cf18ef6db7e961f5bee5c5a09d"
+  integrity sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==
+  dependencies:
+    pkg-dir "^3.0.0"
+    resolve-cwd "^2.0.0"
+
+imurmurhash@^0.1.4:
+  version "0.1.4"
+  resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
+  integrity sha1-khi5srkoojixPcT7a21XbyMUU+o=
+
+indexes-of@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607"
+  integrity sha1-8w9xbI4r00bHtn0985FVZqfAVgc=
+
+infer-owner@^1.0.3:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467"
+  integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==
+
+inflight@^1.0.4:
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
+  integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=
+  dependencies:
+    once "^1.3.0"
+    wrappy "1"
+
+inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3:
+  version "2.0.4"
+  resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
+  integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
+
+inherits@2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1"
+  integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=
+
+inherits@2.0.3:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
+  integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
+
+ini@^1.3.4, ini@^1.3.5, ini@~1.3.0:
+  version "1.3.5"
+  resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
+  integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==
+
+internal-ip@^4.3.0:
+  version "4.3.0"
+  resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-4.3.0.tgz#845452baad9d2ca3b69c635a137acb9a0dad0907"
+  integrity sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg==
+  dependencies:
+    default-gateway "^4.2.0"
+    ipaddr.js "^1.9.0"
+
+interpret@1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.2.0.tgz#d5061a6224be58e8083985f5014d844359576296"
+  integrity sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==
+
+invariant@^2.2.2:
+  version "2.2.4"
+  resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"
+  integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==
+  dependencies:
+    loose-envify "^1.0.0"
+
+invert-kv@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02"
+  integrity sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==
+
+ip-regex@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9"
+  integrity sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=
+
+ip@^1.1.0, ip@^1.1.5:
+  version "1.1.5"
+  resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a"
+  integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=
+
+ipaddr.js@1.9.0:
+  version "1.9.0"
+  resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.0.tgz#37df74e430a0e47550fe54a2defe30d8acd95f65"
+  integrity sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==
+
+ipaddr.js@^1.9.0:
+  version "1.9.1"
+  resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3"
+  integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==
+
+is-absolute-url@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6"
+  integrity sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=
+
+is-absolute-url@^3.0.3:
+  version "3.0.3"
+  resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-3.0.3.tgz#96c6a22b6a23929b11ea0afb1836c36ad4a5d698"
+  integrity sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==
+
+is-accessor-descriptor@^0.1.6:
+  version "0.1.6"
+  resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6"
+  integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=
+  dependencies:
+    kind-of "^3.0.2"
+
+is-accessor-descriptor@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656"
+  integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==
+  dependencies:
+    kind-of "^6.0.0"
+
+is-arguments@^1.0.4:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.0.4.tgz#3faf966c7cba0ff437fb31f6250082fcf0448cf3"
+  integrity sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==
+
+is-binary-path@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898"
+  integrity sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=
+  dependencies:
+    binary-extensions "^1.0.0"
+
+is-buffer@^1.1.5:
+  version "1.1.6"
+  resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
+  integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
+
+is-callable@^1.1.4:
+  version "1.1.4"
+  resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.4.tgz#1e1adf219e1eeb684d691f9d6a05ff0d30a24d75"
+  integrity sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==
+
+is-ci@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c"
+  integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==
+  dependencies:
+    ci-info "^2.0.0"
+
+is-data-descriptor@^0.1.4:
+  version "0.1.4"
+  resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56"
+  integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=
+  dependencies:
+    kind-of "^3.0.2"
+
+is-data-descriptor@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7"
+  integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==
+  dependencies:
+    kind-of "^6.0.0"
+
+is-date-object@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16"
+  integrity sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=
+
+is-descriptor@^0.1.0:
+  version "0.1.6"
+  resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca"
+  integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==
+  dependencies:
+    is-accessor-descriptor "^0.1.6"
+    is-data-descriptor "^0.1.4"
+    kind-of "^5.0.0"
+
+is-descriptor@^1.0.0, is-descriptor@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec"
+  integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==
+  dependencies:
+    is-accessor-descriptor "^1.0.0"
+    is-data-descriptor "^1.0.0"
+    kind-of "^6.0.2"
+
+is-extendable@^0.1.0, is-extendable@^0.1.1:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89"
+  integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=
+
+is-extendable@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4"
+  integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==
+  dependencies:
+    is-plain-object "^2.0.4"
+
+is-extglob@^2.1.0, is-extglob@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
+  integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=
+
+is-fullwidth-code-point@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb"
+  integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs=
+  dependencies:
+    number-is-nan "^1.0.0"
+
+is-fullwidth-code-point@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
+  integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=
+
+is-glob@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a"
+  integrity sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=
+  dependencies:
+    is-extglob "^2.1.0"
+
+is-glob@^4.0.0:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc"
+  integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==
+  dependencies:
+    is-extglob "^2.1.1"
+
+is-installed-globally@^0.1.0:
+  version "0.1.0"
+  resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.1.0.tgz#0dfd98f5a9111716dd535dda6492f67bf3d25a80"
+  integrity sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=
+  dependencies:
+    global-dirs "^0.1.0"
+    is-path-inside "^1.0.0"
+
+is-npm@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-3.0.0.tgz#ec9147bfb629c43f494cf67936a961edec7e8053"
+  integrity sha512-wsigDr1Kkschp2opC4G3yA6r9EgVA6NjRpWzIi9axXqeIaAATPRJc4uLujXe3Nd9uO8KoDyA4MD6aZSeXTADhA==
+
+is-number@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195"
+  integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=
+  dependencies:
+    kind-of "^3.0.2"
+
+is-obj@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f"
+  integrity sha1-PkcprB9f3gJc19g6iW2rn09n2w8=
+
+is-path-cwd@^2.0.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-2.2.0.tgz#67d43b82664a7b5191fd9119127eb300048a9fdb"
+  integrity sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==
+
+is-path-in-cwd@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz#bfe2dca26c69f397265a4009963602935a053acb"
+  integrity sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==
+  dependencies:
+    is-path-inside "^2.1.0"
+
+is-path-inside@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.1.tgz#8ef5b7de50437a3fdca6b4e865ef7aa55cb48036"
+  integrity sha1-jvW33lBDej/cprToZe96pVy0gDY=
+  dependencies:
+    path-is-inside "^1.0.1"
+
+is-path-inside@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-2.1.0.tgz#7c9810587d659a40d27bcdb4d5616eab059494b2"
+  integrity sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==
+  dependencies:
+    path-is-inside "^1.0.2"
+
+is-plain-obj@^1.0.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e"
+  integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4=
+
+is-plain-object@^2.0.3, is-plain-object@^2.0.4:
+  version "2.0.4"
+  resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677"
+  integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==
+  dependencies:
+    isobject "^3.0.1"
+
+is-regex@^1.0.4:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491"
+  integrity sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=
+  dependencies:
+    has "^1.0.1"
+
+is-stream@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
+  integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ=
+
+is-svg@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-2.1.0.tgz#cf61090da0d9efbcab8722deba6f032208dbb0e9"
+  integrity sha1-z2EJDaDZ77yrhyLeum8DIgjbsOk=
+  dependencies:
+    html-comment-regex "^1.1.0"
+
+is-symbol@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.2.tgz#a055f6ae57192caee329e7a860118b497a950f38"
+  integrity sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==
+  dependencies:
+    has-symbols "^1.0.0"
+
+is-windows@^1.0.1, is-windows@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d"
+  integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==
+
+is-wsl@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d"
+  integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=
+
+is-yarn-global@^0.3.0:
+  version "0.3.0"
+  resolved "https://registry.yarnpkg.com/is-yarn-global/-/is-yarn-global-0.3.0.tgz#d502d3382590ea3004893746754c89139973e232"
+  integrity sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==
+
+isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
+  integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
+
+isbinaryfile@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-4.0.2.tgz#bfc45642da645681c610cca831022e30af426488"
+  integrity sha512-C3FSxJdNrEr2F4z6uFtNzECDM5hXk+46fxaa+cwBe5/XrWSmzdG8DDgyjfX6/NRdBB21q2JXuRAzPCUs+fclnQ==
+
+isexe@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
+  integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=
+
+isobject@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89"
+  integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=
+  dependencies:
+    isarray "1.0.0"
+
+isobject@^3.0.0, isobject@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
+  integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8=
+
+js-base64@^2.1.9:
+  version "2.5.1"
+  resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.5.1.tgz#1efa39ef2c5f7980bb1784ade4a8af2de3291121"
+  integrity sha512-M7kLczedRMYX4L8Mdh4MzyAMM9O5osx+4FcOQuTvr3A9F2D9S5JXheN0ewNbrvK2UatkTRhL5ejGmGSjNMiZuw==
+
+js-levenshtein@^1.1.3:
+  version "1.1.6"
+  resolved "https://registry.yarnpkg.com/js-levenshtein/-/js-levenshtein-1.1.6.tgz#c6cee58eb3550372df8deb85fad5ce66ce01d59d"
+  integrity sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==
+
+"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
+  integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
+
+js-yaml@^3.13.1:
+  version "3.13.1"
+  resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847"
+  integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==
+  dependencies:
+    argparse "^1.0.7"
+    esprima "^4.0.0"
+
+js-yaml@~3.7.0:
+  version "3.7.0"
+  resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.7.0.tgz#5c967ddd837a9bfdca5f2de84253abe8a1c03b80"
+  integrity sha1-XJZ93YN6m/3KXy3oQlOr6KHAO4A=
+  dependencies:
+    argparse "^1.0.7"
+    esprima "^2.6.0"
+
+js2xmlparser@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/js2xmlparser/-/js2xmlparser-4.0.0.tgz#ae14cc711b2892083eed6e219fbc993d858bc3a5"
+  integrity sha512-WuNgdZOXVmBk5kUPMcTcVUpbGRzLfNkv7+7APq7WiDihpXVKrgxo6wwRpRl9OQeEBgKCVk9mR7RbzrnNWC8oBw==
+  dependencies:
+    xmlcreate "^2.0.0"
+
+jsdoc@^3.6.3:
+  version "3.6.3"
+  resolved "https://registry.yarnpkg.com/jsdoc/-/jsdoc-3.6.3.tgz#dccea97d0e62d63d306b8b3ed1527173b5e2190d"
+  integrity sha512-Yf1ZKA3r9nvtMWHO1kEuMZTlHOF8uoQ0vyo5eH7SQy5YeIiHM+B0DgKnn+X6y6KDYZcF7G2SPkKF+JORCXWE/A==
+  dependencies:
+    "@babel/parser" "^7.4.4"
+    bluebird "^3.5.4"
+    catharsis "^0.8.11"
+    escape-string-regexp "^2.0.0"
+    js2xmlparser "^4.0.0"
+    klaw "^3.0.0"
+    markdown-it "^8.4.2"
+    markdown-it-anchor "^5.0.2"
+    marked "^0.7.0"
+    mkdirp "^0.5.1"
+    requizzle "^0.2.3"
+    strip-json-comments "^3.0.1"
+    taffydb "2.6.2"
+    underscore "~1.9.1"
+
+jsesc@^2.5.1:
+  version "2.5.2"
+  resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4"
+  integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==
+
+jsesc@~0.5.0:
+  version "0.5.0"
+  resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d"
+  integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=
+
+json-buffer@3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898"
+  integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=
+
+json-parse-better-errors@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9"
+  integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==
+
+json-schema-traverse@^0.4.1:
+  version "0.4.1"
+  resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
+  integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
+
+json-stringify-safe@^5.0.1:
+  version "5.0.1"
+  resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
+  integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=
+
+json3@^3.3.2:
+  version "3.3.3"
+  resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.3.tgz#7fc10e375fc5ae42c4705a5cc0aa6f62be305b81"
+  integrity sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA==
+
+json5@^0.5.0:
+  version "0.5.1"
+  resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821"
+  integrity sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=
+
+json5@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe"
+  integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==
+  dependencies:
+    minimist "^1.2.0"
+
+json5@^2.1.0:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.1.tgz#81b6cb04e9ba496f1c7005d07b4368a2638f90b6"
+  integrity sha512-l+3HXD0GEI3huGq1njuqtzYK8OYJyXMkOLtQ53pjWh89tvWS2h6l+1zMkYWqlb57+SiQodKZyvMEFb2X+KrFhQ==
+  dependencies:
+    minimist "^1.2.0"
+
+jsonfile@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
+  integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=
+  optionalDependencies:
+    graceful-fs "^4.1.6"
+
+keyv@^3.0.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9"
+  integrity sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==
+  dependencies:
+    json-buffer "3.0.0"
+
+killable@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892"
+  integrity sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==
+
+kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0:
+  version "3.2.2"
+  resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64"
+  integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=
+  dependencies:
+    is-buffer "^1.1.5"
+
+kind-of@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57"
+  integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc=
+  dependencies:
+    is-buffer "^1.1.5"
+
+kind-of@^5.0.0:
+  version "5.1.0"
+  resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d"
+  integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==
+
+kind-of@^6.0.0, kind-of@^6.0.2:
+  version "6.0.2"
+  resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051"
+  integrity sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==
+
+klaw@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/klaw/-/klaw-3.0.0.tgz#b11bec9cf2492f06756d6e809ab73a2910259146"
+  integrity sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==
+  dependencies:
+    graceful-fs "^4.1.9"
+
+latest-version@^5.0.0:
+  version "5.1.0"
+  resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-5.1.0.tgz#119dfe908fe38d15dfa43ecd13fa12ec8832face"
+  integrity sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==
+  dependencies:
+    package-json "^6.3.0"
+
+lazy-val@^1.0.4:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/lazy-val/-/lazy-val-1.0.4.tgz#882636a7245c2cfe6e0a4e3ba6c5d68a137e5c65"
+  integrity sha512-u93kb2fPbIrfzBuLjZE+w+fJbUUMhNDXxNmMfaqNgpfQf1CO5ZSe2LfsnBqVAk7i/2NF48OSoRj+Xe2VT+lE8Q==
+
+lcid@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/lcid/-/lcid-2.0.0.tgz#6ef5d2df60e52f82eb228a4c373e8d1f397253cf"
+  integrity sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==
+  dependencies:
+    invert-kv "^2.0.0"
+
+linkify-it@^2.0.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-2.2.0.tgz#e3b54697e78bf915c70a38acd78fd09e0058b1cf"
+  integrity sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw==
+  dependencies:
+    uc.micro "^1.0.1"
+
+loader-runner@^2.4.0:
+  version "2.4.0"
+  resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357"
+  integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==
+
+loader-utils@1.2.3, loader-utils@^1.0.2, loader-utils@^1.1.0, loader-utils@^1.2.3:
+  version "1.2.3"
+  resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.2.3.tgz#1ff5dc6911c9f0a062531a4c04b609406108c2c7"
+  integrity sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==
+  dependencies:
+    big.js "^5.2.2"
+    emojis-list "^2.0.0"
+    json5 "^1.0.1"
+
+loader-utils@^0.2.16:
+  version "0.2.17"
+  resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-0.2.17.tgz#f86e6374d43205a6e6c60e9196f17c0299bfb348"
+  integrity sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=
+  dependencies:
+    big.js "^3.1.3"
+    emojis-list "^2.0.0"
+    json5 "^0.5.0"
+    object-assign "^4.0.1"
+
+locate-path@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e"
+  integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==
+  dependencies:
+    p-locate "^3.0.0"
+    path-exists "^3.0.0"
+
+lodash.memoize@^4.1.2:
+  version "4.1.2"
+  resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
+  integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=
+
+lodash.uniq@^4.5.0:
+  version "4.5.0"
+  resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
+  integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
+
+lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.3, lodash@^4.17.5, lodash@^4.2.0:
+  version "4.17.15"
+  resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
+  integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
+
+loglevel@^1.6.4:
+  version "1.6.6"
+  resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.6.tgz#0ee6300cc058db6b3551fa1c4bf73b83bb771312"
+  integrity sha512-Sgr5lbboAUBo3eXCSPL4/KoVz3ROKquOjcctxmHIt+vol2DrqTQe3SwkKKuYhEiWB5kYa13YyopJ69deJ1irzQ==
+
+loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.0, loose-envify@^1.4.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
+  integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
+  dependencies:
+    js-tokens "^3.0.0 || ^4.0.0"
+
+lower-case@^1.1.1:
+  version "1.1.4"
+  resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac"
+  integrity sha1-miyr0bno4K6ZOkv31YdcOcQujqw=
+
+lowercase-keys@^1.0.0, lowercase-keys@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f"
+  integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==
+
+lowercase-keys@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479"
+  integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==
+
+lru-cache@^4.0.1:
+  version "4.1.5"
+  resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd"
+  integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==
+  dependencies:
+    pseudomap "^1.0.2"
+    yallist "^2.1.2"
+
+lru-cache@^5.1.1:
+  version "5.1.1"
+  resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920"
+  integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==
+  dependencies:
+    yallist "^3.0.2"
+
+make-dir@^1.0.0:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c"
+  integrity sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==
+  dependencies:
+    pify "^3.0.0"
+
+make-dir@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5"
+  integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==
+  dependencies:
+    pify "^4.0.1"
+    semver "^5.6.0"
+
+mamacro@^0.0.3:
+  version "0.0.3"
+  resolved "https://registry.yarnpkg.com/mamacro/-/mamacro-0.0.3.tgz#ad2c9576197c9f1abf308d0787865bd975a3f3e4"
+  integrity sha512-qMEwh+UujcQ+kbz3T6V+wAmO2U8veoq2w+3wY8MquqwVA3jChfwY+Tk52GZKDfACEPjuZ7r2oJLejwpt8jtwTA==
+
+map-age-cleaner@^0.1.1:
+  version "0.1.3"
+  resolved "https://registry.yarnpkg.com/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz#7d583a7306434c055fe474b0f45078e6e1b4b92a"
+  integrity sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==
+  dependencies:
+    p-defer "^1.0.0"
+
+map-cache@^0.2.2:
+  version "0.2.2"
+  resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf"
+  integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=
+
+map-visit@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f"
+  integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=
+  dependencies:
+    object-visit "^1.0.0"
+
+markdown-it-anchor@^5.0.2:
+  version "5.2.5"
+  resolved "https://registry.yarnpkg.com/markdown-it-anchor/-/markdown-it-anchor-5.2.5.tgz#dbf13cfcdbffd16a510984f1263e1d479a47d27a"
+  integrity sha512-xLIjLQmtym3QpoY9llBgApknl7pxAcN3WDRc2d3rwpl+/YvDZHPmKscGs+L6E05xf2KrCXPBvosWt7MZukwSpQ==
+
+markdown-it@^8.4.2:
+  version "8.4.2"
+  resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-8.4.2.tgz#386f98998dc15a37722aa7722084f4020bdd9b54"
+  integrity sha512-GcRz3AWTqSUphY3vsUqQSFMbgR38a4Lh3GWlHRh/7MRwz8mcu9n2IO7HOh+bXHrR9kOPDl5RNCaEsrneb+xhHQ==
+  dependencies:
+    argparse "^1.0.7"
+    entities "~1.1.1"
+    linkify-it "^2.0.0"
+    mdurl "^1.0.1"
+    uc.micro "^1.0.5"
+
+marked@^0.7.0:
+  version "0.7.0"
+  resolved "https://registry.yarnpkg.com/marked/-/marked-0.7.0.tgz#b64201f051d271b1edc10a04d1ae9b74bb8e5c0e"
+  integrity sha512-c+yYdCZJQrsRjTPhUx7VKkApw9bwDkNbHUKo1ovgcfDjb2kc8rLuRbIFyXL5WOEUwzSSKo3IXpph2K6DqB/KZg==
+
+matcher@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/matcher/-/matcher-2.0.0.tgz#85fe38d97670dbd2a46590cf099401e2ffb4755c"
+  integrity sha512-nlmfSlgHBFx36j/Pl/KQPbIaqE8Zf0TqmSMjsuddHDg6PMSVgmyW9HpkLs0o0M1n2GIZ/S2BZBLIww/xjhiGng==
+  dependencies:
+    escape-string-regexp "^2.0.0"
+
+math-expression-evaluator@^1.2.14:
+  version "1.2.17"
+  resolved "https://registry.yarnpkg.com/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz#de819fdbcd84dccd8fae59c6aeb79615b9d266ac"
+  integrity sha1-3oGf282E3M2PrlnGrreWFbnSZqw=
+
+md5.js@^1.3.4:
+  version "1.3.5"
+  resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f"
+  integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==
+  dependencies:
+    hash-base "^3.0.0"
+    inherits "^2.0.1"
+    safe-buffer "^5.1.2"
+
+mdn-data@2.0.4:
+  version "2.0.4"
+  resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.4.tgz#699b3c38ac6f1d728091a64650b65d388502fd5b"
+  integrity sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==
+
+mdurl@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e"
+  integrity sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=
+
+media-typer@0.3.0:
+  version "0.3.0"
+  resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
+  integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=
+
+mem@^4.0.0:
+  version "4.3.0"
+  resolved "https://registry.yarnpkg.com/mem/-/mem-4.3.0.tgz#461af497bc4ae09608cdb2e60eefb69bff744178"
+  integrity sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==
+  dependencies:
+    map-age-cleaner "^0.1.1"
+    mimic-fn "^2.0.0"
+    p-is-promise "^2.0.0"
+
+memory-fs@^0.4.0, memory-fs@^0.4.1:
+  version "0.4.1"
+  resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552"
+  integrity sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=
+  dependencies:
+    errno "^0.1.3"
+    readable-stream "^2.0.1"
+
+memory-fs@^0.5.0:
+  version "0.5.0"
+  resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.5.0.tgz#324c01288b88652966d161db77838720845a8e3c"
+  integrity sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==
+  dependencies:
+    errno "^0.1.3"
+    readable-stream "^2.0.1"
+
+merge-descriptors@1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
+  integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=
+
+methods@~1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
+  integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=
+
+micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.4:
+  version "3.1.10"
+  resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23"
+  integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==
+  dependencies:
+    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"
+
+miller-rabin@^4.0.0:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d"
+  integrity sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==
+  dependencies:
+    bn.js "^4.0.0"
+    brorand "^1.0.1"
+
+mime-db@1.40.0:
+  version "1.40.0"
+  resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.40.0.tgz#a65057e998db090f732a68f6c276d387d4126c32"
+  integrity sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==
+
+"mime-db@>= 1.40.0 < 2":
+  version "1.42.0"
+  resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.42.0.tgz#3e252907b4c7adb906597b4b65636272cf9e7bac"
+  integrity sha512-UbfJCR4UAVRNgMpfImz05smAXK7+c+ZntjaA26ANtkXLlOe947Aag5zdIcKQULAiF9Cq4WxBi9jUs5zkA84bYQ==
+
+mime-types@~2.1.17, mime-types@~2.1.24:
+  version "2.1.24"
+  resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.24.tgz#b6f8d0b3e951efb77dedeca194cff6d16f676f81"
+  integrity sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==
+  dependencies:
+    mime-db "1.40.0"
+
+mime@1.6.0:
+  version "1.6.0"
+  resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
+  integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
+
+mime@^2.4.4:
+  version "2.4.4"
+  resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.4.tgz#bd7b91135fc6b01cde3e9bae33d659b63d8857e5"
+  integrity sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==
+
+mimic-fn@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
+  integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
+
+mimic-response@^1.0.0, mimic-response@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b"
+  integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==
+
+min-document@^2.19.0:
+  version "2.19.0"
+  resolved "https://registry.yarnpkg.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685"
+  integrity sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=
+  dependencies:
+    dom-walk "^0.1.0"
+
+mini-css-extract-plugin@^0.7.0:
+  version "0.7.0"
+  resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.7.0.tgz#5ba8290fbb4179a43dd27cca444ba150bee743a0"
+  integrity sha512-RQIw6+7utTYn8DBGsf/LpRgZCJMpZt+kuawJ/fju0KiOL6nAaTBNmCJwS7HtwSCXfS47gCkmtBFS7HdsquhdxQ==
+  dependencies:
+    loader-utils "^1.1.0"
+    normalize-url "1.9.1"
+    schema-utils "^1.0.0"
+    webpack-sources "^1.1.0"
+
+minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7"
+  integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==
+
+minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a"
+  integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=
+
+minimatch@^3.0.4:
+  version "3.0.4"
+  resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
+  integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
+  dependencies:
+    brace-expansion "^1.1.7"
+
+minimist@0.0.8:
+  version "0.0.8"
+  resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
+  integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=
+
+minimist@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
+  integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=
+
+minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0:
+  version "2.9.0"
+  resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6"
+  integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==
+  dependencies:
+    safe-buffer "^5.1.2"
+    yallist "^3.0.0"
+
+minizlib@^1.2.1:
+  version "1.3.3"
+  resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d"
+  integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==
+  dependencies:
+    minipass "^2.9.0"
+
+mississippi@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-3.0.0.tgz#ea0a3291f97e0b5e8776b363d5f0a12d94c67022"
+  integrity sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==
+  dependencies:
+    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@^1.2.0:
+  version "1.3.2"
+  resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566"
+  integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==
+  dependencies:
+    for-in "^1.0.2"
+    is-extendable "^1.0.1"
+
+mkdirp@0.5.1, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.1:
+  version "0.5.1"
+  resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
+  integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=
+  dependencies:
+    minimist "0.0.8"
+
+move-concurrently@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92"
+  integrity sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=
+  dependencies:
+    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"
+
+ms@2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
+  integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
+
+ms@2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
+  integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==
+
+ms@^2.1.1:
+  version "2.1.2"
+  resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
+  integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
+
+multicast-dns-service-types@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz#899f11d9686e5e05cb91b35d5f0e63b773cfc901"
+  integrity sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=
+
+multicast-dns@^6.0.1:
+  version "6.2.3"
+  resolved "https://registry.yarnpkg.com/multicast-dns/-/multicast-dns-6.2.3.tgz#a0ec7bd9055c4282f790c3c82f4e28db3b31b229"
+  integrity sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==
+  dependencies:
+    dns-packet "^1.3.1"
+    thunky "^1.0.2"
+
+nan@^2.12.1:
+  version "2.14.0"
+  resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c"
+  integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==
+
+nanomatch@^1.2.9:
+  version "1.2.13"
+  resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119"
+  integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==
+  dependencies:
+    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"
+
+needle@^2.2.1:
+  version "2.4.0"
+  resolved "https://registry.yarnpkg.com/needle/-/needle-2.4.0.tgz#6833e74975c444642590e15a750288c5f939b57c"
+  integrity sha512-4Hnwzr3mi5L97hMYeNl8wRW/Onhy4nUKR/lVemJ8gJedxxUyBLm9kkrDColJvoSfwi0jCNhD+xCdOtiGDQiRZg==
+  dependencies:
+    debug "^3.2.6"
+    iconv-lite "^0.4.4"
+    sax "^1.2.4"
+
+negotiator@0.6.2:
+  version "0.6.2"
+  resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb"
+  integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==
+
+neo-async@^2.5.0, neo-async@^2.6.1:
+  version "2.6.1"
+  resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.1.tgz#ac27ada66167fa8849a6addd837f6b189ad2081c"
+  integrity sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==
+
+nice-try@^1.0.4:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
+  integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
+
+no-case@^2.2.0:
+  version "2.3.2"
+  resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac"
+  integrity sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==
+  dependencies:
+    lower-case "^1.1.1"
+
+node-forge@0.9.0:
+  version "0.9.0"
+  resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.9.0.tgz#d624050edbb44874adca12bb9a52ec63cb782579"
+  integrity sha512-7ASaDa3pD+lJ3WvXFsxekJQelBKRpne+GOVbLbtHYdd7pFspyeuJHnWfLplGf3SwKGbfs/aYl5V/JCIaHVUKKQ==
+
+node-gyp-build@^4.1.0:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.2.0.tgz#2c2b05f461f4178641a6ce2d7159f04094e9376d"
+  integrity sha512-4oiumOLhCDU9Rronz8PZ5S4IvT39H5+JEv/hps9V8s7RSLhsac0TCP78ulnHXOo8X1wdpPiTayGlM1jr4IbnaQ==
+
+node-libs-browser@^2.2.1:
+  version "2.2.1"
+  resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.2.1.tgz#b64f513d18338625f90346d27b0d235e631f6425"
+  integrity sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==
+  dependencies:
+    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"
+
+node-loader@^0.6.0:
+  version "0.6.0"
+  resolved "https://registry.yarnpkg.com/node-loader/-/node-loader-0.6.0.tgz#c797ef51095ed5859902b157f6384f6361e05ae8"
+  integrity sha1-x5fvUQle1YWZArFX9jhPY2HgWug=
+
+node-pre-gyp@^0.12.0:
+  version "0.12.0"
+  resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.12.0.tgz#39ba4bb1439da030295f899e3b520b7785766149"
+  integrity sha512-4KghwV8vH5k+g2ylT+sLTjy5wmUOb9vPhnM8NHvRf9dHmnW/CndrFXy2aRPaPST6dugXSdHXfeaHQm77PIz/1A==
+  dependencies:
+    detect-libc "^1.0.2"
+    mkdirp "^0.5.1"
+    needle "^2.2.1"
+    nopt "^4.0.1"
+    npm-packlist "^1.1.6"
+    npmlog "^4.0.2"
+    rc "^1.2.7"
+    rimraf "^2.6.1"
+    semver "^5.3.0"
+    tar "^4"
+
+node-releases@^1.1.38:
+  version "1.1.39"
+  resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.39.tgz#c1011f30343aff5b633153b10ff691d278d08e8d"
+  integrity sha512-8MRC/ErwNCHOlAFycy9OPca46fQYUjbJRDcZTHVWIGXIjYLM73k70vv3WkYutVnM4cCo4hE0MqBVVZjP6vjISA==
+  dependencies:
+    semver "^6.3.0"
+
+node-releases@^1.1.42:
+  version "1.1.42"
+  resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.42.tgz#a999f6a62f8746981f6da90627a8d2fc090bbad7"
+  integrity sha512-OQ/ESmUqGawI2PRX+XIRao44qWYBBfN54ImQYdWVTQqUckuejOg76ysSqDBK8NG3zwySRVnX36JwDQ6x+9GxzA==
+  dependencies:
+    semver "^6.3.0"
+
+nopt@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d"
+  integrity sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=
+  dependencies:
+    abbrev "1"
+    osenv "^0.1.4"
+
+normalize-package-data@^2.5.0:
+  version "2.5.0"
+  resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8"
+  integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==
+  dependencies:
+    hosted-git-info "^2.1.4"
+    resolve "^1.10.0"
+    semver "2 || 3 || 4 || 5"
+    validate-npm-package-license "^3.0.1"
+
+normalize-path@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9"
+  integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=
+  dependencies:
+    remove-trailing-separator "^1.0.1"
+
+normalize-path@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
+  integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
+
+normalize-range@^0.1.2:
+  version "0.1.2"
+  resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942"
+  integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=
+
+normalize-url@1.9.1, normalize-url@^1.4.0, normalize-url@^1.9.1:
+  version "1.9.1"
+  resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-1.9.1.tgz#2cc0d66b31ea23036458436e3620d85954c66c3c"
+  integrity sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=
+  dependencies:
+    object-assign "^4.0.1"
+    prepend-http "^1.0.0"
+    query-string "^4.1.0"
+    sort-keys "^1.0.0"
+
+normalize-url@^4.1.0:
+  version "4.5.0"
+  resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.0.tgz#453354087e6ca96957bd8f5baf753f5982142129"
+  integrity sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==
+
+npm-bundled@^1.0.1:
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.6.tgz#e7ba9aadcef962bb61248f91721cd932b3fe6bdd"
+  integrity sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g==
+
+npm-conf@^1.1.3:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/npm-conf/-/npm-conf-1.1.3.tgz#256cc47bd0e218c259c4e9550bf413bc2192aff9"
+  integrity sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw==
+  dependencies:
+    config-chain "^1.1.11"
+    pify "^3.0.0"
+
+npm-packlist@^1.1.6:
+  version "1.4.6"
+  resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.6.tgz#53ba3ed11f8523079f1457376dd379ee4ea42ff4"
+  integrity sha512-u65uQdb+qwtGvEJh/DgQgW1Xg7sqeNbmxYyrvlNznaVTjV3E5P6F/EFjM+BVHXl7JJlsdG8A64M0XI8FI/IOlg==
+  dependencies:
+    ignore-walk "^3.0.1"
+    npm-bundled "^1.0.1"
+
+npm-run-path@^2.0.0:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f"
+  integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=
+  dependencies:
+    path-key "^2.0.0"
+
+npmlog@^4.0.2:
+  version "4.1.2"
+  resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b"
+  integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==
+  dependencies:
+    are-we-there-yet "~1.1.2"
+    console-control-strings "~1.1.0"
+    gauge "~2.7.3"
+    set-blocking "~2.0.0"
+
+nth-check@^1.0.2, nth-check@~1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c"
+  integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==
+  dependencies:
+    boolbase "~1.0.0"
+
+num2fraction@^1.2.2:
+  version "1.2.2"
+  resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede"
+  integrity sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=
+
+number-is-nan@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
+  integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=
+
+object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1:
+  version "4.1.1"
+  resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
+  integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
+
+object-copy@^0.1.0:
+  version "0.1.0"
+  resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c"
+  integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw=
+  dependencies:
+    copy-descriptor "^0.1.0"
+    define-property "^0.2.5"
+    kind-of "^3.0.3"
+
+object-inspect@^1.6.0:
+  version "1.6.0"
+  resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.6.0.tgz#c70b6cbf72f274aab4c34c0c82f5167bf82cf15b"
+  integrity sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ==
+
+object-is@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.0.1.tgz#0aa60ec9989a0b3ed795cf4d06f62cf1ad6539b6"
+  integrity sha1-CqYOyZiaCz7Xlc9NBvYs8a1lObY=
+
+object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
+  integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==
+
+object-visit@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb"
+  integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=
+  dependencies:
+    isobject "^3.0.0"
+
+object.assign@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da"
+  integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==
+  dependencies:
+    define-properties "^1.1.2"
+    function-bind "^1.1.1"
+    has-symbols "^1.0.0"
+    object-keys "^1.0.11"
+
+object.getownpropertydescriptors@^2.0.3:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz#8758c846f5b407adab0f236e0986f14b051caa16"
+  integrity sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=
+  dependencies:
+    define-properties "^1.1.2"
+    es-abstract "^1.5.1"
+
+object.pick@^1.3.0:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747"
+  integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=
+  dependencies:
+    isobject "^3.0.1"
+
+object.values@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.0.tgz#bf6810ef5da3e5325790eaaa2be213ea84624da9"
+  integrity sha512-8mf0nKLAoFX6VlNVdhGj31SVYpaNFtUnuoOXWyFEstsWRgU837AK+JYM0iAxwkSzGRbwn8cbFmgbyxj1j4VbXg==
+  dependencies:
+    define-properties "^1.1.3"
+    es-abstract "^1.12.0"
+    function-bind "^1.1.1"
+    has "^1.0.3"
+
+obuf@^1.0.0, obuf@^1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e"
+  integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==
+
+on-finished@~2.3.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947"
+  integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=
+  dependencies:
+    ee-first "1.1.1"
+
+on-headers@~1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f"
+  integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==
+
+once@^1.3.0, once@^1.3.1, once@^1.4.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
+  integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
+  dependencies:
+    wrappy "1"
+
+opn@^5.5.0:
+  version "5.5.0"
+  resolved "https://registry.yarnpkg.com/opn/-/opn-5.5.0.tgz#fc7164fab56d235904c51c3b27da6758ca3b9bfc"
+  integrity sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==
+  dependencies:
+    is-wsl "^1.1.0"
+
+original@^1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/original/-/original-1.0.2.tgz#e442a61cffe1c5fd20a65f3261c26663b303f25f"
+  integrity sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==
+  dependencies:
+    url-parse "^1.4.3"
+
+os-browserify@^0.3.0:
+  version "0.3.0"
+  resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27"
+  integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=
+
+os-homedir@^1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3"
+  integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M=
+
+os-locale@^3.0.0, os-locale@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-3.1.0.tgz#a802a6ee17f24c10483ab9935719cef4ed16bf1a"
+  integrity sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==
+  dependencies:
+    execa "^1.0.0"
+    lcid "^2.0.0"
+    mem "^4.0.0"
+
+os-tmpdir@^1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
+  integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=
+
+osenv@^0.1.4:
+  version "0.1.5"
+  resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410"
+  integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==
+  dependencies:
+    os-homedir "^1.0.0"
+    os-tmpdir "^1.0.0"
+
+p-cancelable@^1.0.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc"
+  integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==
+
+p-defer@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c"
+  integrity sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=
+
+p-finally@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae"
+  integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=
+
+p-is-promise@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-2.1.0.tgz#918cebaea248a62cf7ffab8e3bca8c5f882fc42e"
+  integrity sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==
+
+p-limit@^2.0.0:
+  version "2.2.1"
+  resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.1.tgz#aa07a788cc3151c939b5131f63570f0dd2009537"
+  integrity sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==
+  dependencies:
+    p-try "^2.0.0"
+
+p-locate@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4"
+  integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==
+  dependencies:
+    p-limit "^2.0.0"
+
+p-map@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175"
+  integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==
+
+p-retry@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-3.0.1.tgz#316b4c8893e2c8dc1cfa891f406c4b422bebf328"
+  integrity sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w==
+  dependencies:
+    retry "^0.12.0"
+
+p-try@^2.0.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
+  integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
+
+package-json@^6.3.0:
+  version "6.5.0"
+  resolved "https://registry.yarnpkg.com/package-json/-/package-json-6.5.0.tgz#6feedaca35e75725876d0b0e64974697fed145b0"
+  integrity sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==
+  dependencies:
+    got "^9.6.0"
+    registry-auth-token "^4.0.0"
+    registry-url "^5.0.0"
+    semver "^6.2.0"
+
+pako@~1.0.5:
+  version "1.0.10"
+  resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.10.tgz#4328badb5086a426aa90f541977d4955da5c9732"
+  integrity sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw==
+
+parallel-transform@^1.1.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.2.0.tgz#9049ca37d6cb2182c3b1d2c720be94d14a5814fc"
+  integrity sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==
+  dependencies:
+    cyclist "^1.0.1"
+    inherits "^2.0.3"
+    readable-stream "^2.1.5"
+
+param-case@2.1.x:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/param-case/-/param-case-2.1.1.tgz#df94fd8cf6531ecf75e6bef9a0858fbc72be2247"
+  integrity sha1-35T9jPZTHs915r75oIWPvHK+Ikc=
+  dependencies:
+    no-case "^2.2.0"
+
+parse-asn1@^5.0.0:
+  version "5.1.5"
+  resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.5.tgz#003271343da58dc94cace494faef3d2147ecea0e"
+  integrity sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ==
+  dependencies:
+    asn1.js "^4.0.0"
+    browserify-aes "^1.0.0"
+    create-hash "^1.1.0"
+    evp_bytestokey "^1.0.0"
+    pbkdf2 "^3.0.3"
+    safe-buffer "^5.1.1"
+
+parse-passwd@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6"
+  integrity sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=
+
+parseurl@~1.3.2, parseurl@~1.3.3:
+  version "1.3.3"
+  resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
+  integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
+
+pascalcase@^0.1.1:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14"
+  integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=
+
+path-browserify@0.0.1:
+  version "0.0.1"
+  resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a"
+  integrity sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==
+
+path-dirname@^1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0"
+  integrity sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=
+
+path-exists@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515"
+  integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=
+
+path-is-absolute@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
+  integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
+
+path-is-inside@^1.0.1, path-is-inside@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53"
+  integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=
+
+path-key@^2.0.0, path-key@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"
+  integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=
+
+path-parse@^1.0.6:
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c"
+  integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==
+
+path-to-regexp@0.1.7:
+  version "0.1.7"
+  resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
+  integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=
+
+pbkdf2@^3.0.3:
+  version "3.0.17"
+  resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.17.tgz#976c206530617b14ebb32114239f7b09336e93a6"
+  integrity sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==
+  dependencies:
+    create-hash "^1.1.2"
+    create-hmac "^1.1.4"
+    ripemd160 "^2.0.1"
+    safe-buffer "^5.0.1"
+    sha.js "^2.4.8"
+
+pend@~1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
+  integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA=
+
+pify@^2.0.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
+  integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw=
+
+pify@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176"
+  integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=
+
+pify@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231"
+  integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==
+
+pinkie-promise@^2.0.0:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa"
+  integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o=
+  dependencies:
+    pinkie "^2.0.0"
+
+pinkie@^2.0.0:
+  version "2.0.4"
+  resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870"
+  integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA=
+
+pkg-dir@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3"
+  integrity sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==
+  dependencies:
+    find-up "^3.0.0"
+
+portfinder@^1.0.25:
+  version "1.0.25"
+  resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.25.tgz#254fd337ffba869f4b9d37edc298059cb4d35eca"
+  integrity sha512-6ElJnHBbxVA1XSLgBp7G1FiCkQdlqGzuF7DswL5tcea+E8UpuvPU7beVAjjRwCioTS9ZluNbu+ZyRvgTsmqEBg==
+  dependencies:
+    async "^2.6.2"
+    debug "^3.1.1"
+    mkdirp "^0.5.1"
+
+posix-character-classes@^0.1.0:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
+  integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=
+
+postcss-calc@^5.2.0:
+  version "5.3.1"
+  resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-5.3.1.tgz#77bae7ca928ad85716e2fda42f261bf7c1d65b5e"
+  integrity sha1-d7rnypKK2FcW4v2kLyYb98HWW14=
+  dependencies:
+    postcss "^5.0.2"
+    postcss-message-helpers "^2.0.0"
+    reduce-css-calc "^1.2.6"
+
+postcss-colormin@^2.1.8:
+  version "2.2.2"
+  resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-2.2.2.tgz#6631417d5f0e909a3d7ec26b24c8a8d1e4f96e4b"
+  integrity sha1-ZjFBfV8OkJo9fsJrJMio0eT5bks=
+  dependencies:
+    colormin "^1.0.5"
+    postcss "^5.0.13"
+    postcss-value-parser "^3.2.3"
+
+postcss-convert-values@^2.3.4:
+  version "2.6.1"
+  resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-2.6.1.tgz#bbd8593c5c1fd2e3d1c322bb925dcae8dae4d62d"
+  integrity sha1-u9hZPFwf0uPRwyK7kl3K6Nrk1i0=
+  dependencies:
+    postcss "^5.0.11"
+    postcss-value-parser "^3.1.2"
+
+postcss-discard-comments@^2.0.4:
+  version "2.0.4"
+  resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz#befe89fafd5b3dace5ccce51b76b81514be00e3d"
+  integrity sha1-vv6J+v1bPazlzM5Rt2uBUUvgDj0=
+  dependencies:
+    postcss "^5.0.14"
+
+postcss-discard-duplicates@^2.0.1:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-2.1.0.tgz#b9abf27b88ac188158a5eb12abcae20263b91932"
+  integrity sha1-uavye4isGIFYpesSq8riAmO5GTI=
+  dependencies:
+    postcss "^5.0.4"
+
+postcss-discard-empty@^2.0.1:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-2.1.0.tgz#d2b4bd9d5ced5ebd8dcade7640c7d7cd7f4f92b5"
+  integrity sha1-0rS9nVztXr2Nyt52QMfXzX9PkrU=
+  dependencies:
+    postcss "^5.0.14"
+
+postcss-discard-overridden@^0.1.1:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-0.1.1.tgz#8b1eaf554f686fb288cd874c55667b0aa3668d58"
+  integrity sha1-ix6vVU9ob7KIzYdMVWZ7CqNmjVg=
+  dependencies:
+    postcss "^5.0.16"
+
+postcss-discard-unused@^2.2.1:
+  version "2.2.3"
+  resolved "https://registry.yarnpkg.com/postcss-discard-unused/-/postcss-discard-unused-2.2.3.tgz#bce30b2cc591ffc634322b5fb3464b6d934f4433"
+  integrity sha1-vOMLLMWR/8Y0Mitfs0ZLbZNPRDM=
+  dependencies:
+    postcss "^5.0.14"
+    uniqs "^2.0.0"
+
+postcss-filter-plugins@^2.0.0:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/postcss-filter-plugins/-/postcss-filter-plugins-2.0.3.tgz#82245fdf82337041645e477114d8e593aa18b8ec"
+  integrity sha512-T53GVFsdinJhgwm7rg1BzbeBRomOg9y5MBVhGcsV0CxurUdVj1UlPdKtn7aqYA/c/QVkzKMjq2bSV5dKG5+AwQ==
+  dependencies:
+    postcss "^5.0.4"
+
+postcss-merge-idents@^2.1.5:
+  version "2.1.7"
+  resolved "https://registry.yarnpkg.com/postcss-merge-idents/-/postcss-merge-idents-2.1.7.tgz#4c5530313c08e1d5b3bbf3d2bbc747e278eea270"
+  integrity sha1-TFUwMTwI4dWzu/PSu8dH4njuonA=
+  dependencies:
+    has "^1.0.1"
+    postcss "^5.0.10"
+    postcss-value-parser "^3.1.1"
+
+postcss-merge-longhand@^2.0.1:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-2.0.2.tgz#23d90cd127b0a77994915332739034a1a4f3d658"
+  integrity sha1-I9kM0Sewp3mUkVMyc5A0oaTz1lg=
+  dependencies:
+    postcss "^5.0.4"
+
+postcss-merge-rules@^2.0.3:
+  version "2.1.2"
+  resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-2.1.2.tgz#d1df5dfaa7b1acc3be553f0e9e10e87c61b5f721"
+  integrity sha1-0d9d+qexrMO+VT8OnhDofGG19yE=
+  dependencies:
+    browserslist "^1.5.2"
+    caniuse-api "^1.5.2"
+    postcss "^5.0.4"
+    postcss-selector-parser "^2.2.2"
+    vendors "^1.0.0"
+
+postcss-message-helpers@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/postcss-message-helpers/-/postcss-message-helpers-2.0.0.tgz#a4f2f4fab6e4fe002f0aed000478cdf52f9ba60e"
+  integrity sha1-pPL0+rbk/gAvCu0ABHjN9S+bpg4=
+
+postcss-minify-font-values@^1.0.2:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-1.0.5.tgz#4b58edb56641eba7c8474ab3526cafd7bbdecb69"
+  integrity sha1-S1jttWZB66fIR0qzUmyv17vey2k=
+  dependencies:
+    object-assign "^4.0.1"
+    postcss "^5.0.4"
+    postcss-value-parser "^3.0.2"
+
+postcss-minify-gradients@^1.0.1:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-1.0.5.tgz#5dbda11373703f83cfb4a3ea3881d8d75ff5e6e1"
+  integrity sha1-Xb2hE3NwP4PPtKPqOIHY11/15uE=
+  dependencies:
+    postcss "^5.0.12"
+    postcss-value-parser "^3.3.0"
+
+postcss-minify-params@^1.0.4:
+  version "1.2.2"
+  resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-1.2.2.tgz#ad2ce071373b943b3d930a3fa59a358c28d6f1f3"
+  integrity sha1-rSzgcTc7lDs9kwo/pZo1jCjW8fM=
+  dependencies:
+    alphanum-sort "^1.0.1"
+    postcss "^5.0.2"
+    postcss-value-parser "^3.0.2"
+    uniqs "^2.0.0"
+
+postcss-minify-selectors@^2.0.4:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-2.1.1.tgz#b2c6a98c0072cf91b932d1a496508114311735bf"
+  integrity sha1-ssapjAByz5G5MtGkllCBFDEXNb8=
+  dependencies:
+    alphanum-sort "^1.0.2"
+    has "^1.0.1"
+    postcss "^5.0.14"
+    postcss-selector-parser "^2.0.0"
+
+postcss-modules-extract-imports@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz#818719a1ae1da325f9832446b01136eeb493cd7e"
+  integrity sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ==
+  dependencies:
+    postcss "^7.0.5"
+
+postcss-modules-local-by-default@^3.0.2:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.2.tgz#e8a6561be914aaf3c052876377524ca90dbb7915"
+  integrity sha512-jM/V8eqM4oJ/22j0gx4jrp63GSvDH6v86OqyTHHUvk4/k1vceipZsaymiZ5PvocqZOl5SFHiFJqjs3la0wnfIQ==
+  dependencies:
+    icss-utils "^4.1.1"
+    postcss "^7.0.16"
+    postcss-selector-parser "^6.0.2"
+    postcss-value-parser "^4.0.0"
+
+postcss-modules-scope@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-2.1.0.tgz#ad3f5bf7856114f6fcab901b0502e2a2bc39d4eb"
+  integrity sha512-91Rjps0JnmtUB0cujlc8KIKCsJXWjzuxGeT/+Q2i2HXKZ7nBUeF9YQTZZTNvHVoNYj1AthsjnGLtqDUE0Op79A==
+  dependencies:
+    postcss "^7.0.6"
+    postcss-selector-parser "^6.0.0"
+
+postcss-modules-scope@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-2.1.1.tgz#33d4fc946602eb5e9355c4165d68a10727689dba"
+  integrity sha512-OXRUPecnHCg8b9xWvldG/jUpRIGPNRka0r4D4j0ESUU2/5IOnpsjfPPmDprM3Ih8CgZ8FXjWqaniK5v4rWt3oQ==
+  dependencies:
+    postcss "^7.0.6"
+    postcss-selector-parser "^6.0.0"
+
+postcss-modules-values@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-3.0.0.tgz#5b5000d6ebae29b4255301b4a3a54574423e7f10"
+  integrity sha512-1//E5jCBrZ9DmRX+zCtmQtRSV6PV42Ix7Bzj9GbwJceduuf7IqP8MgeTXuRDHOWj2m0VzZD5+roFWDuU8RQjcg==
+  dependencies:
+    icss-utils "^4.0.0"
+    postcss "^7.0.6"
+
+postcss-normalize-charset@^1.1.0:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-1.1.1.tgz#ef9ee71212d7fe759c78ed162f61ed62b5cb93f1"
+  integrity sha1-757nEhLX/nWceO0WL2HtYrXLk/E=
+  dependencies:
+    postcss "^5.0.5"
+
+postcss-normalize-url@^3.0.7:
+  version "3.0.8"
+  resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-3.0.8.tgz#108f74b3f2fcdaf891a2ffa3ea4592279fc78222"
+  integrity sha1-EI90s/L82viRov+j6kWSJ5/HgiI=
+  dependencies:
+    is-absolute-url "^2.0.0"
+    normalize-url "^1.4.0"
+    postcss "^5.0.14"
+    postcss-value-parser "^3.2.3"
+
+postcss-ordered-values@^2.1.0:
+  version "2.2.3"
+  resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-2.2.3.tgz#eec6c2a67b6c412a8db2042e77fe8da43f95c11d"
+  integrity sha1-7sbCpntsQSqNsgQud/6NpD+VwR0=
+  dependencies:
+    postcss "^5.0.4"
+    postcss-value-parser "^3.0.1"
+
+postcss-reduce-idents@^2.2.2:
+  version "2.4.0"
+  resolved "https://registry.yarnpkg.com/postcss-reduce-idents/-/postcss-reduce-idents-2.4.0.tgz#c2c6d20cc958284f6abfbe63f7609bf409059ad3"
+  integrity sha1-wsbSDMlYKE9qv75j92Cb9AkFmtM=
+  dependencies:
+    postcss "^5.0.4"
+    postcss-value-parser "^3.0.2"
+
+postcss-reduce-initial@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-1.0.1.tgz#68f80695f045d08263a879ad240df8dd64f644ea"
+  integrity sha1-aPgGlfBF0IJjqHmtJA343WT2ROo=
+  dependencies:
+    postcss "^5.0.4"
+
+postcss-reduce-transforms@^1.0.3:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-1.0.4.tgz#ff76f4d8212437b31c298a42d2e1444025771ae1"
+  integrity sha1-/3b02CEkN7McKYpC0uFEQCV3GuE=
+  dependencies:
+    has "^1.0.1"
+    postcss "^5.0.8"
+    postcss-value-parser "^3.0.1"
+
+postcss-selector-parser@^2.0.0, postcss-selector-parser@^2.2.2:
+  version "2.2.3"
+  resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-2.2.3.tgz#f9437788606c3c9acee16ffe8d8b16297f27bb90"
+  integrity sha1-+UN3iGBsPJrO4W/+jYsWKX8nu5A=
+  dependencies:
+    flatten "^1.0.2"
+    indexes-of "^1.0.1"
+    uniq "^1.0.1"
+
+postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.2:
+  version "6.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz#934cf799d016c83411859e09dcecade01286ec5c"
+  integrity sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg==
+  dependencies:
+    cssesc "^3.0.0"
+    indexes-of "^1.0.1"
+    uniq "^1.0.1"
+
+postcss-svgo@^2.1.1:
+  version "2.1.6"
+  resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-2.1.6.tgz#b6df18aa613b666e133f08adb5219c2684ac108d"
+  integrity sha1-tt8YqmE7Zm4TPwittSGcJoSsEI0=
+  dependencies:
+    is-svg "^2.0.0"
+    postcss "^5.0.14"
+    postcss-value-parser "^3.2.3"
+    svgo "^0.7.0"
+
+postcss-unique-selectors@^2.0.2:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-2.0.2.tgz#981d57d29ddcb33e7b1dfe1fd43b8649f933ca1d"
+  integrity sha1-mB1X0p3csz57Hf4f1DuGSfkzyh0=
+  dependencies:
+    alphanum-sort "^1.0.1"
+    postcss "^5.0.4"
+    uniqs "^2.0.0"
+
+postcss-value-parser@^3.0.1, postcss-value-parser@^3.0.2, postcss-value-parser@^3.1.1, postcss-value-parser@^3.1.2, postcss-value-parser@^3.2.3, postcss-value-parser@^3.3.0:
+  version "3.3.1"
+  resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281"
+  integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==
+
+postcss-value-parser@^4.0.0, postcss-value-parser@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.0.2.tgz#482282c09a42706d1fc9a069b73f44ec08391dc9"
+  integrity sha512-LmeoohTpp/K4UiyQCwuGWlONxXamGzCMtFxLq4W1nZVGIQLYvMCJx3yAF9qyyuFpflABI9yVdtJAqbihOsCsJQ==
+
+postcss-zindex@^2.0.1:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/postcss-zindex/-/postcss-zindex-2.2.0.tgz#d2109ddc055b91af67fc4cb3b025946639d2af22"
+  integrity sha1-0hCd3AVbka9n/EyzsCWUZjnSryI=
+  dependencies:
+    has "^1.0.1"
+    postcss "^5.0.4"
+    uniqs "^2.0.0"
+
+postcss@^5.0.10, postcss@^5.0.11, postcss@^5.0.12, postcss@^5.0.13, postcss@^5.0.14, postcss@^5.0.16, postcss@^5.0.2, postcss@^5.0.4, postcss@^5.0.5, postcss@^5.0.8, postcss@^5.2.16:
+  version "5.2.18"
+  resolved "https://registry.yarnpkg.com/postcss/-/postcss-5.2.18.tgz#badfa1497d46244f6390f58b319830d9107853c5"
+  integrity sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==
+  dependencies:
+    chalk "^1.1.3"
+    js-base64 "^2.1.9"
+    source-map "^0.5.6"
+    supports-color "^3.2.3"
+
+postcss@^7.0.14, postcss@^7.0.16, postcss@^7.0.17, postcss@^7.0.5, postcss@^7.0.6:
+  version "7.0.21"
+  resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.21.tgz#06bb07824c19c2021c5d056d5b10c35b989f7e17"
+  integrity sha512-uIFtJElxJo29QC753JzhidoAhvp/e/Exezkdhfmt8AymWT6/5B7W1WmponYWkHk2eg6sONyTch0A3nkMPun3SQ==
+  dependencies:
+    chalk "^2.4.2"
+    source-map "^0.6.1"
+    supports-color "^6.1.0"
+
+postcss@^7.0.23:
+  version "7.0.23"
+  resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.23.tgz#9f9759fad661b15964f3cfc3140f66f1e05eadc1"
+  integrity sha512-hOlMf3ouRIFXD+j2VJecwssTwbvsPGJVMzupptg+85WA+i7MwyrydmQAgY3R+m0Bc0exunhbJmijy8u8+vufuQ==
+  dependencies:
+    chalk "^2.4.2"
+    source-map "^0.6.1"
+    supports-color "^6.1.0"
+
+posthtml-parser@^0.4.1:
+  version "0.4.2"
+  resolved "https://registry.yarnpkg.com/posthtml-parser/-/posthtml-parser-0.4.2.tgz#a132bbdf0cd4bc199d34f322f5c1599385d7c6c1"
+  integrity sha512-BUIorsYJTvS9UhXxPTzupIztOMVNPa/HtAm9KHni9z6qEfiJ1bpOBL5DfUOL9XAc3XkLIEzBzpph+Zbm4AdRAg==
+  dependencies:
+    htmlparser2 "^3.9.2"
+
+posthtml-render@^1.1.4, posthtml-render@^1.1.5:
+  version "1.1.5"
+  resolved "https://registry.yarnpkg.com/posthtml-render/-/posthtml-render-1.1.5.tgz#387934e85438a3de77085fbc7d264efb00bd0e0f"
+  integrity sha512-yvt54j0zCBHQVEFAuR+yHld8CZrCa/E1Z/OcFNCV1IEWTLVxT8O7nYnM4IIw1CD4r8kaRd3lc42+0lgCKgm87w==
+
+posthtml@^0.11.2, posthtml@^0.11.3:
+  version "0.11.6"
+  resolved "https://registry.yarnpkg.com/posthtml/-/posthtml-0.11.6.tgz#e349d51af7929d0683b9d8c3abd8166beecc90a8"
+  integrity sha512-C2hrAPzmRdpuL3iH0TDdQ6XCc9M7Dcc3zEW5BLerY65G4tWWszwv6nG/ksi6ul5i2mx22ubdljgktXCtNkydkw==
+  dependencies:
+    posthtml-parser "^0.4.1"
+    posthtml-render "^1.1.5"
+
+prepend-http@^1.0.0:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc"
+  integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=
+
+prepend-http@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897"
+  integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=
+
+pretty-error@^2.0.2:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-2.1.1.tgz#5f4f87c8f91e5ae3f3ba87ab4cf5e03b1a17f1a3"
+  integrity sha1-X0+HyPkeWuPzuoerTPXgOxoX8aM=
+  dependencies:
+    renderkid "^2.0.1"
+    utila "~0.4"
+
+private@^0.1.6:
+  version "0.1.8"
+  resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff"
+  integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==
+
+process-nextick-args@~2.0.0:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
+  integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
+
+process@^0.11.10:
+  version "0.11.10"
+  resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
+  integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI=
+
+promise-inflight@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3"
+  integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM=
+
+prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2:
+  version "15.7.2"
+  resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
+  integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
+  dependencies:
+    loose-envify "^1.4.0"
+    object-assign "^4.1.1"
+    react-is "^16.8.1"
+
+proto-list@~1.2.1:
+  version "1.2.4"
+  resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849"
+  integrity sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=
+
+proxy-addr@~2.0.5:
+  version "2.0.5"
+  resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.5.tgz#34cbd64a2d81f4b1fd21e76f9f06c8a45299ee34"
+  integrity sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==
+  dependencies:
+    forwarded "~0.1.2"
+    ipaddr.js "1.9.0"
+
+prr@~1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476"
+  integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY=
+
+pseudomap@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"
+  integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM=
+
+public-encrypt@^4.0.0:
+  version "4.0.3"
+  resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.3.tgz#4fcc9d77a07e48ba7527e7cbe0de33d0701331e0"
+  integrity sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==
+  dependencies:
+    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"
+
+pump@^2.0.0:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909"
+  integrity sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==
+  dependencies:
+    end-of-stream "^1.1.0"
+    once "^1.3.1"
+
+pump@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64"
+  integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==
+  dependencies:
+    end-of-stream "^1.1.0"
+    once "^1.3.1"
+
+pumpify@^1.3.3:
+  version "1.5.1"
+  resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.5.1.tgz#36513be246ab27570b1a374a5ce278bfd74370ce"
+  integrity sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==
+  dependencies:
+    duplexify "^3.6.0"
+    inherits "^2.0.3"
+    pump "^2.0.0"
+
+punycode@1.3.2:
+  version "1.3.2"
+  resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d"
+  integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=
+
+punycode@^1.2.4:
+  version "1.4.1"
+  resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
+  integrity sha1-wNWmOycYgArY4esPpSachN1BhF4=
+
+punycode@^2.1.0:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
+  integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
+
+q@^1.1.2:
+  version "1.5.1"
+  resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"
+  integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=
+
+qs@6.7.0:
+  version "6.7.0"
+  resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc"
+  integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==
+
+query-string@^4.1.0:
+  version "4.3.4"
+  resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.3.4.tgz#bbb693b9ca915c232515b228b1a02b609043dbeb"
+  integrity sha1-u7aTucqRXCMlFbIosaArYJBD2+s=
+  dependencies:
+    object-assign "^4.1.0"
+    strict-uri-encode "^1.0.0"
+
+querystring-es3@^0.2.0:
+  version "0.2.1"
+  resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73"
+  integrity sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=
+
+querystring@0.2.0:
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620"
+  integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=
+
+querystringify@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.1.1.tgz#60e5a5fd64a7f8bfa4d2ab2ed6fdf4c85bad154e"
+  integrity sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==
+
+randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"
+  integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==
+  dependencies:
+    safe-buffer "^5.1.0"
+
+randomfill@^1.0.3:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458"
+  integrity sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==
+  dependencies:
+    randombytes "^2.0.5"
+    safe-buffer "^5.1.0"
+
+range-parser@^1.2.1, range-parser@~1.2.1:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
+  integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
+
+raw-body@2.4.0:
+  version "2.4.0"
+  resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332"
+  integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==
+  dependencies:
+    bytes "3.1.0"
+    http-errors "1.7.2"
+    iconv-lite "0.4.24"
+    unpipe "1.0.0"
+
+rc@^1.2.7, rc@^1.2.8:
+  version "1.2.8"
+  resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed"
+  integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==
+  dependencies:
+    deep-extend "^0.6.0"
+    ini "~1.3.0"
+    minimist "^1.2.0"
+    strip-json-comments "~2.0.1"
+
+react-dom@^16.12.0:
+  version "16.12.0"
+  resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.12.0.tgz#0da4b714b8d13c2038c9396b54a92baea633fe11"
+  integrity sha512-LMxFfAGrcS3kETtQaCkTKjMiifahaMySFDn71fZUNpPHZQEzmk/GiAeIT8JSOrHB23fnuCOMruL2a8NYlw+8Gw==
+  dependencies:
+    loose-envify "^1.1.0"
+    object-assign "^4.1.1"
+    prop-types "^15.6.2"
+    scheduler "^0.18.0"
+
+react-draggable@^4.2.0:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/react-draggable/-/react-draggable-4.2.0.tgz#40cc5209082ca7d613104bf6daf31372cc0e1114"
+  integrity sha512-5wFq//gEoeTYprnd4ze8GrFc+Rbnx+9RkOMR3vk4EbWxj02U6L6T3yrlKeiw4X5CtjD2ma2+b3WujghcXNRzkw==
+  dependencies:
+    classnames "^2.2.5"
+    prop-types "^15.6.0"
+
+react-hot-loader@^4.12.18:
+  version "4.12.18"
+  resolved "https://registry.yarnpkg.com/react-hot-loader/-/react-hot-loader-4.12.18.tgz#a9029e34af2690d76208f9a35189d73c2dfea6a7"
+  integrity sha512-qYD0Qi9lIbg9jLyfmodfqvAQqCBsoPKxAhca8Nxvy2/2pO5Q9r2kM28jN0bbbSnhwK8dJ7FjsfVtXKOxMW+bqw==
+  dependencies:
+    fast-levenshtein "^2.0.6"
+    global "^4.3.0"
+    hoist-non-react-statics "^3.3.0"
+    loader-utils "^1.1.0"
+    prop-types "^15.6.1"
+    react-lifecycles-compat "^3.0.4"
+    shallowequal "^1.1.0"
+    source-map "^0.7.3"
+
+react-is@^16.7.0, react-is@^16.8.1:
+  version "16.11.0"
+  resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.11.0.tgz#b85dfecd48ad1ce469ff558a882ca8e8313928fa"
+  integrity sha512-gbBVYR2p8mnriqAwWx9LbuUrShnAuSCNnuPGyc7GJrMVQtPDAh8iLpv7FRuMPFb56KkaVZIYSz1PrjI9q0QPCw==
+
+react-lifecycles-compat@^3.0.4:
+  version "3.0.4"
+  resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
+  integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==
+
+react-virtualized@^9.21.2:
+  version "9.21.2"
+  resolved "https://registry.yarnpkg.com/react-virtualized/-/react-virtualized-9.21.2.tgz#02e6df65c1e020c8dbf574ec4ce971652afca84e"
+  integrity sha512-oX7I7KYiUM7lVXQzmhtF4Xg/4UA5duSA+/ZcAvdWlTLFCoFYq1SbauJT5gZK9cZS/wdYR6TPGpX/dqzvTqQeBA==
+  dependencies:
+    babel-runtime "^6.26.0"
+    clsx "^1.0.1"
+    dom-helpers "^5.0.0"
+    loose-envify "^1.3.0"
+    prop-types "^15.6.0"
+    react-lifecycles-compat "^3.0.4"
+
+react@^16.8:
+  version "16.11.0"
+  resolved "https://registry.yarnpkg.com/react/-/react-16.11.0.tgz#d294545fe62299ccee83363599bf904e4a07fdbb"
+  integrity sha512-M5Y8yITaLmU0ynd0r1Yvfq98Rmll6q8AxaEe88c8e7LxO8fZ2cNgmFt0aGAS9wzf1Ao32NKXtCl+/tVVtkxq6g==
+  dependencies:
+    loose-envify "^1.1.0"
+    object-assign "^4.1.1"
+    prop-types "^15.6.2"
+
+read-config-file@5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/read-config-file/-/read-config-file-5.0.0.tgz#1487c983fae9c1b672d3acda5cac899a2d451f02"
+  integrity sha512-jIKUu+C84bfnKxyJ5j30CxCqgXWYjZLXuVE/NYlMEpeni+dhESgAeZOZd0JZbg1xTkMmnCdxksDoarkOyfEsOg==
+  dependencies:
+    dotenv "^8.0.0"
+    dotenv-expand "^5.1.0"
+    fs-extra "^8.1.0"
+    js-yaml "^3.13.1"
+    json5 "^2.1.0"
+    lazy-val "^1.0.4"
+
+read-config-file@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/read-config-file/-/read-config-file-4.0.1.tgz#ece5f6b1a5e6a46d0d93fdd0339f2f60ab892776"
+  integrity sha512-5caED3uo2IAZMPcbh/9hx/O29s2430RLxtnFDdzxpH/epEpawOrQnGBHueotIXUrGPPIgdNQN+S/CIp2WmiSfw==
+  dependencies:
+    ajv "^6.10.1"
+    ajv-keywords "^3.4.1"
+    dotenv "^8.0.0"
+    dotenv-expand "^5.1.0"
+    fs-extra "^8.1.0"
+    js-yaml "^3.13.1"
+    json5 "^2.1.0"
+    lazy-val "^1.0.4"
+
+"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6:
+  version "2.3.6"
+  resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf"
+  integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==
+  dependencies:
+    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"
+
+readable-stream@^3.0.6, readable-stream@^3.1.1:
+  version "3.4.0"
+  resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.4.0.tgz#a51c26754658e0a3c21dbf59163bd45ba6f447fc"
+  integrity sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==
+  dependencies:
+    inherits "^2.0.3"
+    string_decoder "^1.1.1"
+    util-deprecate "^1.0.1"
+
+readdirp@^2.2.1:
+  version "2.2.1"
+  resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525"
+  integrity sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==
+  dependencies:
+    graceful-fs "^4.1.11"
+    micromatch "^3.1.10"
+    readable-stream "^2.0.2"
+
+reduce-css-calc@^1.2.6:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz#747c914e049614a4c9cfbba629871ad1d2927716"
+  integrity sha1-dHyRTgSWFKTJz7umKYca0dKSdxY=
+  dependencies:
+    balanced-match "^0.4.2"
+    math-expression-evaluator "^1.2.14"
+    reduce-function-call "^1.0.1"
+
+reduce-function-call@^1.0.1:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/reduce-function-call/-/reduce-function-call-1.0.3.tgz#60350f7fb252c0a67eb10fd4694d16909971300f"
+  integrity sha512-Hl/tuV2VDgWgCSEeWMLwxLZqX7OK59eU1guxXsRKTAyeYimivsKdtcV4fu3r710tpG5GmDKDhQ0HSZLExnNmyQ==
+  dependencies:
+    balanced-match "^1.0.0"
+
+regenerate-unicode-properties@^8.1.0:
+  version "8.1.0"
+  resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz#ef51e0f0ea4ad424b77bf7cb41f3e015c70a3f0e"
+  integrity sha512-LGZzkgtLY79GeXLm8Dp0BVLdQlWICzBnJz/ipWUgo59qBaZ+BHtq51P2q1uVZlppMuUAT37SDk39qUbjTWB7bA==
+  dependencies:
+    regenerate "^1.4.0"
+
+regenerate@^1.4.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11"
+  integrity sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==
+
+regenerator-runtime@^0.11.0:
+  version "0.11.1"
+  resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
+  integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==
+
+regenerator-runtime@^0.13.2:
+  version "0.13.3"
+  resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz#7cf6a77d8f5c6f60eb73c5fc1955b2ceb01e6bf5"
+  integrity sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==
+
+regenerator-transform@^0.14.0:
+  version "0.14.1"
+  resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.1.tgz#3b2fce4e1ab7732c08f665dfdb314749c7ddd2fb"
+  integrity sha512-flVuee02C3FKRISbxhXl9mGzdbWUVHubl1SMaknjxkFB1/iqpJhArQUvRxOOPEc/9tAiX0BaQ28FJH10E4isSQ==
+  dependencies:
+    private "^0.1.6"
+
+regex-not@^1.0.0, regex-not@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c"
+  integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==
+  dependencies:
+    extend-shallow "^3.0.2"
+    safe-regex "^1.1.0"
+
+regexp.prototype.flags@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.2.0.tgz#6b30724e306a27833eeb171b66ac8890ba37e41c"
+  integrity sha512-ztaw4M1VqgMwl9HlPpOuiYgItcHlunW0He2fE6eNfT6E/CF2FtYi9ofOYe4mKntstYk0Fyh/rDRBdS3AnxjlrA==
+  dependencies:
+    define-properties "^1.1.2"
+
+regexpu-core@^4.6.0:
+  version "4.6.0"
+  resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.6.0.tgz#2037c18b327cfce8a6fea2a4ec441f2432afb8b6"
+  integrity sha512-YlVaefl8P5BnFYOITTNzDvan1ulLOiXJzCNZxduTIosN17b87h3bvG9yHMoHaRuo88H4mQ06Aodj5VtYGGGiTg==
+  dependencies:
+    regenerate "^1.4.0"
+    regenerate-unicode-properties "^8.1.0"
+    regjsgen "^0.5.0"
+    regjsparser "^0.6.0"
+    unicode-match-property-ecmascript "^1.0.4"
+    unicode-match-property-value-ecmascript "^1.1.0"
+
+registry-auth-token@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-4.0.0.tgz#30e55961eec77379da551ea5c4cf43cbf03522be"
+  integrity sha512-lpQkHxd9UL6tb3k/aHAVfnVtn+Bcs9ob5InuFLLEDqSqeq+AljB8GZW9xY0x7F+xYwEcjKe07nyoxzEYz6yvkw==
+  dependencies:
+    rc "^1.2.8"
+    safe-buffer "^5.0.1"
+
+registry-url@^5.0.0:
+  version "5.1.0"
+  resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-5.1.0.tgz#e98334b50d5434b81136b44ec638d9c2009c5009"
+  integrity sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==
+  dependencies:
+    rc "^1.2.8"
+
+regjsgen@^0.5.0:
+  version "0.5.1"
+  resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.1.tgz#48f0bf1a5ea205196929c0d9798b42d1ed98443c"
+  integrity sha512-5qxzGZjDs9w4tzT3TPhCJqWdCc3RLYwy9J2NB0nm5Lz+S273lvWcpjaTGHsT1dc6Hhfq41uSEOw8wBmxrKOuyg==
+
+regjsparser@^0.6.0:
+  version "0.6.0"
+  resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.6.0.tgz#f1e6ae8b7da2bae96c99399b868cd6c933a2ba9c"
+  integrity sha512-RQ7YyokLiQBomUJuUG8iGVvkgOLxwyZM8k6d3q5SAXpg4r5TZJZigKFvC6PpD+qQ98bCDC5YelPeA3EucDoNeQ==
+  dependencies:
+    jsesc "~0.5.0"
+
+relateurl@0.2.x:
+  version "0.2.7"
+  resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9"
+  integrity sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=
+
+remove-trailing-separator@^1.0.1:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef"
+  integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8=
+
+renderkid@^2.0.1:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/renderkid/-/renderkid-2.0.3.tgz#380179c2ff5ae1365c522bf2fcfcff01c5b74149"
+  integrity sha512-z8CLQp7EZBPCwCnncgf9C4XAi3WR0dv+uWu/PjIyhhAb5d6IJ/QZqlHFprHeKT+59//V6BNUsLbvN8+2LarxGA==
+  dependencies:
+    css-select "^1.1.0"
+    dom-converter "^0.2"
+    htmlparser2 "^3.3.0"
+    strip-ansi "^3.0.0"
+    utila "^0.4.0"
+
+repeat-element@^1.1.2:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce"
+  integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==
+
+repeat-string@^1.6.1:
+  version "1.6.1"
+  resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637"
+  integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc=
+
+require-directory@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
+  integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I=
+
+require-main-filename@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1"
+  integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=
+
+require-main-filename@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b"
+  integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==
+
+requires-port@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
+  integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=
+
+requizzle@^0.2.3:
+  version "0.2.3"
+  resolved "https://registry.yarnpkg.com/requizzle/-/requizzle-0.2.3.tgz#4675c90aacafb2c036bd39ba2daa4a1cb777fded"
+  integrity sha512-YanoyJjykPxGHii0fZP0uUPEXpvqfBDxWV7s6GKAiiOsiqhX6vHNyW3Qzdmqp/iq/ExbhaGbVrjB4ruEVSM4GQ==
+  dependencies:
+    lodash "^4.17.14"
+
+resolve-cwd@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a"
+  integrity sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=
+  dependencies:
+    resolve-from "^3.0.0"
+
+resolve-dir@^1.0.0, resolve-dir@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/resolve-dir/-/resolve-dir-1.0.1.tgz#79a40644c362be82f26effe739c9bb5382046f43"
+  integrity sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=
+  dependencies:
+    expand-tilde "^2.0.0"
+    global-modules "^1.0.0"
+
+resolve-from@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748"
+  integrity sha1-six699nWiBvItuZTM17rywoYh0g=
+
+resolve-url@^0.2.1:
+  version "0.2.1"
+  resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a"
+  integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=
+
+resolve@^1.10.0, resolve@^1.3.2:
+  version "1.12.0"
+  resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.12.0.tgz#3fc644a35c84a48554609ff26ec52b66fa577df6"
+  integrity sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==
+  dependencies:
+    path-parse "^1.0.6"
+
+responselike@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7"
+  integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=
+  dependencies:
+    lowercase-keys "^1.0.0"
+
+ret@~0.1.10:
+  version "0.1.15"
+  resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc"
+  integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==
+
+retry@^0.12.0:
+  version "0.12.0"
+  resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b"
+  integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=
+
+rimraf@^2.5.2, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.3:
+  version "2.7.1"
+  resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec"
+  integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==
+  dependencies:
+    glob "^7.1.3"
+
+ripemd160@^2.0.0, ripemd160@^2.0.1:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c"
+  integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==
+  dependencies:
+    hash-base "^3.0.0"
+    inherits "^2.0.1"
+
+roarr@^2.14.2:
+  version "2.14.4"
+  resolved "https://registry.yarnpkg.com/roarr/-/roarr-2.14.4.tgz#2d9d26864ce95ce570b4b5c5e548aefd40bb2bf7"
+  integrity sha512-QMzRAQGZFPgnx4nNWp4Q+WHfiZh2HTSEjNaxFLrEIj3PmcQ1GHL5OjaaIyF9ybUDD2aZ9t3Awc/obrRPils9ng==
+  dependencies:
+    boolean "^2.0.3"
+    detect-node "^2.0.4"
+    globalthis "^1.0.0"
+    json-stringify-safe "^5.0.1"
+    semver-compare "^1.0.0"
+    sprintf-js "^1.1.2"
+
+run-queue@^1.0.0, run-queue@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz#e848396f057d223f24386924618e25694161ec47"
+  integrity sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=
+  dependencies:
+    aproba "^1.1.1"
+
+safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
+  version "5.1.2"
+  resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
+  integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
+
+safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0:
+  version "5.2.0"
+  resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519"
+  integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==
+
+safe-regex@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e"
+  integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4=
+  dependencies:
+    ret "~0.1.10"
+
+"safer-buffer@>= 2.1.2 < 3":
+  version "2.1.2"
+  resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
+  integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
+
+sanitize-filename@^1.6.2:
+  version "1.6.3"
+  resolved "https://registry.yarnpkg.com/sanitize-filename/-/sanitize-filename-1.6.3.tgz#755ebd752045931977e30b2025d340d7c9090378"
+  integrity sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==
+  dependencies:
+    truncate-utf8-bytes "^1.0.0"
+
+sax@^1.2.4, sax@~1.2.1, sax@~1.2.4:
+  version "1.2.4"
+  resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
+  integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
+
+scheduler@^0.17.0:
+  version "0.17.0"
+  resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.17.0.tgz#7c9c673e4ec781fac853927916d1c426b6f3ddfe"
+  integrity sha512-7rro8Io3tnCPuY4la/NuI5F2yfESpnfZyT6TtkXnSWVkcu0BCDJ+8gk5ozUaFaxpIyNuWAPXrH0yFcSi28fnDA==
+  dependencies:
+    loose-envify "^1.1.0"
+    object-assign "^4.1.1"
+
+scheduler@^0.18.0:
+  version "0.18.0"
+  resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.18.0.tgz#5901ad6659bc1d8f3fdaf36eb7a67b0d6746b1c4"
+  integrity sha512-agTSHR1Nbfi6ulI0kYNK0203joW2Y5W4po4l+v03tOoiJKpTBbxpNhWDvqc/4IcOw+KLmSiQLTasZ4cab2/UWQ==
+  dependencies:
+    loose-envify "^1.1.0"
+    object-assign "^4.1.1"
+
+schema-utils@^0.4.3:
+  version "0.4.7"
+  resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.4.7.tgz#ba74f597d2be2ea880131746ee17d0a093c68187"
+  integrity sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==
+  dependencies:
+    ajv "^6.1.0"
+    ajv-keywords "^3.1.0"
+
+schema-utils@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-1.0.0.tgz#0b79a93204d7b600d4b2850d1f66c2a34951c770"
+  integrity sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==
+  dependencies:
+    ajv "^6.1.0"
+    ajv-errors "^1.0.0"
+    ajv-keywords "^3.1.0"
+
+schema-utils@^2.0.0, schema-utils@^2.4.1:
+  version "2.5.0"
+  resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.5.0.tgz#8f254f618d402cc80257486213c8970edfd7c22f"
+  integrity sha512-32ISrwW2scPXHUSusP8qMg5dLUawKkyV+/qIEV9JdXKx+rsM6mi8vZY8khg2M69Qom16rtroWXD3Ybtiws38gQ==
+  dependencies:
+    ajv "^6.10.2"
+    ajv-keywords "^3.4.1"
+
+schema-utils@^2.6.0:
+  version "2.6.1"
+  resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.6.1.tgz#eb78f0b945c7bcfa2082b3565e8db3548011dc4f"
+  integrity sha512-0WXHDs1VDJyo+Zqs9TKLKyD/h7yDpHUhEFsM2CzkICFdoX1av+GBq/J2xRTFfsQO5kBfhZzANf2VcIm84jqDbg==
+  dependencies:
+    ajv "^6.10.2"
+    ajv-keywords "^3.4.1"
+
+select-hose@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca"
+  integrity sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=
+
+selfsigned@^1.10.7:
+  version "1.10.7"
+  resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-1.10.7.tgz#da5819fd049d5574f28e88a9bcc6dbc6e6f3906b"
+  integrity sha512-8M3wBCzeWIJnQfl43IKwOmC4H/RAp50S8DF60znzjW5GVqTcSe2vWclt7hmYVPkKPlHWOu5EaWOMZ2Y6W8ZXTA==
+  dependencies:
+    node-forge "0.9.0"
+
+semver-compare@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc"
+  integrity sha1-De4hahyUGrN+nvsXiPavxf9VN/w=
+
+semver-diff@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-2.1.0.tgz#4bbb8437c8d37e4b0cf1a68fd726ec6d645d6d36"
+  integrity sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=
+  dependencies:
+    semver "^5.0.3"
+
+"semver@2 || 3 || 4 || 5", semver@^5.0.3, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.6.0:
+  version "5.7.1"
+  resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
+  integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
+
+semver@7.0.0:
+  version "7.0.0"
+  resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e"
+  integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==
+
+semver@^6.2.0, semver@^6.3.0:
+  version "6.3.0"
+  resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
+  integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
+
+send@0.17.1:
+  version "0.17.1"
+  resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8"
+  integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==
+  dependencies:
+    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"
+
+serialize-error@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-5.0.0.tgz#a7ebbcdb03a5d71a6ed8461ffe0fc1a1afed62ac"
+  integrity sha512-/VtpuyzYf82mHYTtI4QKtwHa79vAdU5OQpNPAmE/0UDdlGT0ZxHwC+J6gXkw29wwoVI8fMPsfcVHOwXtUQYYQA==
+  dependencies:
+    type-fest "^0.8.0"
+
+serialize-javascript@^1.7.0:
+  version "1.9.1"
+  resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-1.9.1.tgz#cfc200aef77b600c47da9bb8149c943e798c2fdb"
+  integrity sha512-0Vb/54WJ6k5v8sSWN09S0ora+Hnr+cX40r9F170nT+mSkaxltoE/7R3OrIdBSUv1OoiobH1QoWQbCnAO+e8J1A==
+
+serialize-javascript@^2.1.2:
+  version "2.1.2"
+  resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-2.1.2.tgz#ecec53b0e0317bdc95ef76ab7074b7384785fa61"
+  integrity sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==
+
+serve-index@^1.9.1:
+  version "1.9.1"
+  resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239"
+  integrity sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=
+  dependencies:
+    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"
+
+serve-static@1.14.1:
+  version "1.14.1"
+  resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9"
+  integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==
+  dependencies:
+    encodeurl "~1.0.2"
+    escape-html "~1.0.3"
+    parseurl "~1.3.3"
+    send "0.17.1"
+
+set-blocking@^2.0.0, set-blocking@~2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
+  integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc=
+
+set-value@^2.0.0, set-value@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b"
+  integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==
+  dependencies:
+    extend-shallow "^2.0.1"
+    is-extendable "^0.1.1"
+    is-plain-object "^2.0.3"
+    split-string "^3.0.1"
+
+setimmediate@^1.0.4:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
+  integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=
+
+setprototypeof@1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656"
+  integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==
+
+setprototypeof@1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683"
+  integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==
+
+sha.js@^2.4.0, sha.js@^2.4.8:
+  version "2.4.11"
+  resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7"
+  integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==
+  dependencies:
+    inherits "^2.0.1"
+    safe-buffer "^5.0.1"
+
+shallowequal@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-1.1.0.tgz#188d521de95b9087404fd4dcb68b13df0ae4e7f8"
+  integrity sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==
+
+shebang-command@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"
+  integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=
+  dependencies:
+    shebang-regex "^1.0.0"
+
+shebang-regex@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3"
+  integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=
+
+signal-exit@^3.0.0, signal-exit@^3.0.2:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
+  integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=
+
+snapdragon-node@^2.0.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b"
+  integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==
+  dependencies:
+    define-property "^1.0.0"
+    isobject "^3.0.0"
+    snapdragon-util "^3.0.1"
+
+snapdragon-util@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2"
+  integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==
+  dependencies:
+    kind-of "^3.2.0"
+
+snapdragon@^0.8.1:
+  version "0.8.2"
+  resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d"
+  integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==
+  dependencies:
+    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"
+
+sockjs-client@1.4.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.4.0.tgz#c9f2568e19c8fd8173b4997ea3420e0bb306c7d5"
+  integrity sha512-5zaLyO8/nri5cua0VtOrFXBPK1jbL4+1cebT/mmKA1E1ZXOvJrII75bPu0l0k843G/+iAbhEqzyKr0w/eCCj7g==
+  dependencies:
+    debug "^3.2.5"
+    eventsource "^1.0.7"
+    faye-websocket "~0.11.1"
+    inherits "^2.0.3"
+    json3 "^3.3.2"
+    url-parse "^1.4.3"
+
+sockjs@0.3.19:
+  version "0.3.19"
+  resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.19.tgz#d976bbe800af7bd20ae08598d582393508993c0d"
+  integrity sha512-V48klKZl8T6MzatbLlzzRNhMepEys9Y4oGFpypBFFn1gLI/QQ9HtLLyWJNbPlwGLelOVOEijUbTTJeLLI59jLw==
+  dependencies:
+    faye-websocket "^0.10.0"
+    uuid "^3.0.1"
+
+sort-keys@^1.0.0:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-1.1.2.tgz#441b6d4d346798f1b4e49e8920adfba0e543f9ad"
+  integrity sha1-RBttTTRnmPG05J6JIK37oOVD+a0=
+  dependencies:
+    is-plain-obj "^1.0.0"
+
+source-list-map@^2.0.0:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34"
+  integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==
+
+source-map-resolve@^0.5.0:
+  version "0.5.2"
+  resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.2.tgz#72e2cc34095543e43b2c62b2c4c10d4a9054f259"
+  integrity sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==
+  dependencies:
+    atob "^2.1.1"
+    decode-uri-component "^0.2.0"
+    resolve-url "^0.2.1"
+    source-map-url "^0.4.0"
+    urix "^0.1.0"
+
+source-map-support@^0.5.12, source-map-support@^0.5.13, source-map-support@^0.5.16, source-map-support@~0.5.10, source-map-support@~0.5.12:
+  version "0.5.16"
+  resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042"
+  integrity sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==
+  dependencies:
+    buffer-from "^1.0.0"
+    source-map "^0.6.0"
+
+source-map-url@^0.4.0:
+  version "0.4.0"
+  resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3"
+  integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=
+
+source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6:
+  version "0.5.7"
+  resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
+  integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
+
+source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1:
+  version "0.6.1"
+  resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
+  integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
+
+source-map@^0.7.3:
+  version "0.7.3"
+  resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383"
+  integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==
+
+spdx-correct@^3.0.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.0.tgz#fb83e504445268f154b074e218c87c003cd31df4"
+  integrity sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==
+  dependencies:
+    spdx-expression-parse "^3.0.0"
+    spdx-license-ids "^3.0.0"
+
+spdx-exceptions@^2.1.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz#2ea450aee74f2a89bfb94519c07fcd6f41322977"
+  integrity sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==
+
+spdx-expression-parse@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz#99e119b7a5da00e05491c9fa338b7904823b41d0"
+  integrity sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==
+  dependencies:
+    spdx-exceptions "^2.1.0"
+    spdx-license-ids "^3.0.0"
+
+spdx-license-ids@^3.0.0:
+  version "3.0.5"
+  resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz#3694b5804567a458d3c8045842a6358632f62654"
+  integrity sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==
+
+spdy-transport@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-3.0.0.tgz#00d4863a6400ad75df93361a1608605e5dcdcf31"
+  integrity sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==
+  dependencies:
+    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"
+
+spdy@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/spdy/-/spdy-4.0.1.tgz#6f12ed1c5db7ea4f24ebb8b89ba58c87c08257f2"
+  integrity sha512-HeZS3PBdMA+sZSu0qwpCxl3DeALD5ASx8pAX0jZdKXSpPWbQ6SYGnlg3BBmYLx5LtiZrmkAZfErCm2oECBcioA==
+  dependencies:
+    debug "^4.1.0"
+    handle-thing "^2.0.0"
+    http-deceiver "^1.2.7"
+    select-hose "^2.0.0"
+    spdy-transport "^3.0.0"
+
+split-string@^3.0.1, split-string@^3.0.2:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2"
+  integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==
+  dependencies:
+    extend-shallow "^3.0.0"
+
+sprintf-js@^1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.2.tgz#da1765262bf8c0f571749f2ad6c26300207ae673"
+  integrity sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==
+
+sprintf-js@~1.0.2:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
+  integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
+
+ssri@^6.0.1:
+  version "6.0.1"
+  resolved "https://registry.yarnpkg.com/ssri/-/ssri-6.0.1.tgz#2a3c41b28dd45b62b63676ecb74001265ae9edd8"
+  integrity sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==
+  dependencies:
+    figgy-pudding "^3.5.1"
+
+stable@^0.1.8:
+  version "0.1.8"
+  resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf"
+  integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==
+
+stat-mode@^0.3.0:
+  version "0.3.0"
+  resolved "https://registry.yarnpkg.com/stat-mode/-/stat-mode-0.3.0.tgz#69283b081f851582b328d2a4ace5f591ce52f54b"
+  integrity sha512-QjMLR0A3WwFY2aZdV0okfFEJB5TRjkggXZjxP3A1RsWsNHNu3YPv8btmtc6iCFZ0Rul3FE93OYogvhOUClU+ng==
+
+static-extend@^0.1.1:
+  version "0.1.2"
+  resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6"
+  integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=
+  dependencies:
+    define-property "^0.2.5"
+    object-copy "^0.1.0"
+
+"statuses@>= 1.4.0 < 2", "statuses@>= 1.5.0 < 2", statuses@~1.5.0:
+  version "1.5.0"
+  resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
+  integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=
+
+stream-browserify@^2.0.1:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.2.tgz#87521d38a44aa7ee91ce1cd2a47df0cb49dd660b"
+  integrity sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==
+  dependencies:
+    inherits "~2.0.1"
+    readable-stream "^2.0.2"
+
+stream-each@^1.1.0:
+  version "1.2.3"
+  resolved "https://registry.yarnpkg.com/stream-each/-/stream-each-1.2.3.tgz#ebe27a0c389b04fbcc233642952e10731afa9bae"
+  integrity sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==
+  dependencies:
+    end-of-stream "^1.1.0"
+    stream-shift "^1.0.0"
+
+stream-http@^2.7.2:
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.3.tgz#b2d242469288a5a27ec4fe8933acf623de6514fc"
+  integrity sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==
+  dependencies:
+    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@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.0.tgz#d5c752825e5367e786f78e18e445ea223a155952"
+  integrity sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=
+
+strict-uri-encode@^1.0.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713"
+  integrity sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=
+
+string-width@^1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"
+  integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=
+  dependencies:
+    code-point-at "^1.0.0"
+    is-fullwidth-code-point "^1.0.0"
+    strip-ansi "^3.0.0"
+
+"string-width@^1.0.2 || 2", string-width@^2.0.0, string-width@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e"
+  integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==
+  dependencies:
+    is-fullwidth-code-point "^2.0.0"
+    strip-ansi "^4.0.0"
+
+string-width@^3.0.0, string-width@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961"
+  integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==
+  dependencies:
+    emoji-regex "^7.0.1"
+    is-fullwidth-code-point "^2.0.0"
+    strip-ansi "^5.1.0"
+
+string.prototype.trimleft@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz#6cc47f0d7eb8d62b0f3701611715a3954591d634"
+  integrity sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw==
+  dependencies:
+    define-properties "^1.1.3"
+    function-bind "^1.1.1"
+
+string.prototype.trimright@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz#669d164be9df9b6f7559fa8e89945b168a5a6c58"
+  integrity sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg==
+  dependencies:
+    define-properties "^1.1.3"
+    function-bind "^1.1.1"
+
+string_decoder@^1.0.0, string_decoder@^1.1.1:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
+  integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==
+  dependencies:
+    safe-buffer "~5.2.0"
+
+string_decoder@~1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
+  integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
+  dependencies:
+    safe-buffer "~5.1.0"
+
+strip-ansi@^3.0.0, strip-ansi@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf"
+  integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=
+  dependencies:
+    ansi-regex "^2.0.0"
+
+strip-ansi@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f"
+  integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8=
+  dependencies:
+    ansi-regex "^3.0.0"
+
+strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0:
+  version "5.2.0"
+  resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae"
+  integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==
+  dependencies:
+    ansi-regex "^4.1.0"
+
+strip-eof@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf"
+  integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=
+
+strip-json-comments@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.0.1.tgz#85713975a91fb87bf1b305cca77395e40d2a64a7"
+  integrity sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==
+
+strip-json-comments@~2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
+  integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo=
+
+style-loader@^0.23.1:
+  version "0.23.1"
+  resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-0.23.1.tgz#cb9154606f3e771ab6c4ab637026a1049174d925"
+  integrity sha512-XK+uv9kWwhZMZ1y7mysB+zoihsEj4wneFWAS5qoiLwzW0WzSqMrrsIy+a3zkQJq0ipFtBpX5W3MqyRIBF/WFGg==
+  dependencies:
+    loader-utils "^1.1.0"
+    schema-utils "^1.0.0"
+
+sumchecker@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/sumchecker/-/sumchecker-3.0.0.tgz#da5457b4605184575c76540e5e99cc777cb8ce4c"
+  integrity sha512-yreseuC/z4iaodVoq07XULEOO9p4jnQazO7mbrnDSvWAU/y2cbyIKs+gWJptfcGu9R+1l27K8Rkj0bfvqnBpgQ==
+  dependencies:
+    debug "^4.1.0"
+
+supports-color@6.1.0, supports-color@^6.1.0:
+  version "6.1.0"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3"
+  integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==
+  dependencies:
+    has-flag "^3.0.0"
+
+supports-color@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
+  integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=
+
+supports-color@^3.2.3:
+  version "3.2.3"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6"
+  integrity sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=
+  dependencies:
+    has-flag "^1.0.0"
+
+supports-color@^5.3.0:
+  version "5.5.0"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
+  integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
+  dependencies:
+    has-flag "^3.0.0"
+
+svgo@^0.7.0:
+  version "0.7.2"
+  resolved "https://registry.yarnpkg.com/svgo/-/svgo-0.7.2.tgz#9f5772413952135c6fefbf40afe6a4faa88b4bb5"
+  integrity sha1-n1dyQTlSE1xv779Ar+ak+qiLS7U=
+  dependencies:
+    coa "~1.0.1"
+    colors "~1.1.2"
+    csso "~2.3.1"
+    js-yaml "~3.7.0"
+    mkdirp "~0.5.1"
+    sax "~1.2.1"
+    whet.extend "~0.9.9"
+
+svgo@^1.0.5:
+  version "1.3.2"
+  resolved "https://registry.yarnpkg.com/svgo/-/svgo-1.3.2.tgz#b6dc511c063346c9e415b81e43401145b96d4167"
+  integrity sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==
+  dependencies:
+    chalk "^2.4.1"
+    coa "^2.0.2"
+    css-select "^2.0.0"
+    css-select-base-adapter "^0.1.1"
+    css-tree "1.0.0-alpha.37"
+    csso "^4.0.2"
+    js-yaml "^3.13.1"
+    mkdirp "~0.5.1"
+    object.values "^1.1.0"
+    sax "~1.2.4"
+    stable "^0.1.8"
+    unquote "~1.1.1"
+    util.promisify "~1.0.0"
+
+taffydb@2.6.2:
+  version "2.6.2"
+  resolved "https://registry.yarnpkg.com/taffydb/-/taffydb-2.6.2.tgz#7cbcb64b5a141b6a2efc2c5d2c67b4e150b2a268"
+  integrity sha1-fLy2S1oUG2ou/CxdLGe04VCyomg=
+
+tapable@^1.0.0, tapable@^1.1.3:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2"
+  integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==
+
+tar@^4:
+  version "4.4.13"
+  resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525"
+  integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==
+  dependencies:
+    chownr "^1.1.1"
+    fs-minipass "^1.2.5"
+    minipass "^2.8.6"
+    minizlib "^1.2.1"
+    mkdirp "^0.5.0"
+    safe-buffer "^5.1.2"
+    yallist "^3.0.3"
+
+temp-file@^3.3.4:
+  version "3.3.4"
+  resolved "https://registry.yarnpkg.com/temp-file/-/temp-file-3.3.4.tgz#73af868cd7cb7400a44e4bb03e653b2280ce2878"
+  integrity sha512-qSZ5W5q54iyGnP8cNl49RE0jTJc5CrzNocux5APD5yIxcgonoMuMSbsZfaZy8rTGCYo0Xz6ySVv3adagZ8gffg==
+  dependencies:
+    async-exit-hook "^2.0.1"
+    fs-extra "^8.1.0"
+
+term-size@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/term-size/-/term-size-1.2.0.tgz#458b83887f288fc56d6fffbfad262e26638efa69"
+  integrity sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=
+  dependencies:
+    execa "^0.7.0"
+
+terser-webpack-plugin@^1.3.0:
+  version "1.4.1"
+  resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.4.1.tgz#61b18e40eaee5be97e771cdbb10ed1280888c2b4"
+  integrity sha512-ZXmmfiwtCLfz8WKZyYUuuHf3dMYEjg8NrjHMb0JqHVHVOSkzp3cW2/XG1fP3tRhqEqSzMwzzRQGtAPbs4Cncxg==
+  dependencies:
+    cacache "^12.0.2"
+    find-cache-dir "^2.1.0"
+    is-wsl "^1.1.0"
+    schema-utils "^1.0.0"
+    serialize-javascript "^1.7.0"
+    source-map "^0.6.1"
+    terser "^4.1.2"
+    webpack-sources "^1.4.0"
+    worker-farm "^1.7.0"
+
+terser-webpack-plugin@^1.4.3:
+  version "1.4.3"
+  resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.4.3.tgz#5ecaf2dbdc5fb99745fd06791f46fc9ddb1c9a7c"
+  integrity sha512-QMxecFz/gHQwteWwSo5nTc6UaICqN1bMedC5sMtUc7y3Ha3Q8y6ZO0iCR8pq4RJC8Hjf0FEPEHZqcMB/+DFCrA==
+  dependencies:
+    cacache "^12.0.2"
+    find-cache-dir "^2.1.0"
+    is-wsl "^1.1.0"
+    schema-utils "^1.0.0"
+    serialize-javascript "^2.1.2"
+    source-map "^0.6.1"
+    terser "^4.1.2"
+    webpack-sources "^1.4.0"
+    worker-farm "^1.7.0"
+
+terser@^3.8.1:
+  version "3.17.0"
+  resolved "https://registry.yarnpkg.com/terser/-/terser-3.17.0.tgz#f88ffbeda0deb5637f9d24b0da66f4e15ab10cb2"
+  integrity sha512-/FQzzPJmCpjAH9Xvk2paiWrFq+5M6aVOf+2KRbwhByISDX/EujxsK+BAvrhb6H+2rtrLCHK9N01wO014vrIwVQ==
+  dependencies:
+    commander "^2.19.0"
+    source-map "~0.6.1"
+    source-map-support "~0.5.10"
+
+terser@^4.1.2:
+  version "4.3.9"
+  resolved "https://registry.yarnpkg.com/terser/-/terser-4.3.9.tgz#e4be37f80553d02645668727777687dad26bbca8"
+  integrity sha512-NFGMpHjlzmyOtPL+fDw3G7+6Ueh/sz4mkaUYa4lJCxOPTNzd0Uj0aZJOmsDYoSQyfuVoWDMSWTPU3huyOm2zdA==
+  dependencies:
+    commander "^2.20.0"
+    source-map "~0.6.1"
+    source-map-support "~0.5.12"
+
+through2@^2.0.0:
+  version "2.0.5"
+  resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd"
+  integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==
+  dependencies:
+    readable-stream "~2.3.6"
+    xtend "~4.0.1"
+
+thunky@^1.0.2:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d"
+  integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==
+
+timers-browserify@^2.0.4:
+  version "2.0.11"
+  resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.11.tgz#800b1f3eee272e5bc53ee465a04d0e804c31211f"
+  integrity sha512-60aV6sgJ5YEbzUdn9c8kYGIqOubPoUdqQCul3SBAsRCZ40s6Y5cMcrW4dt3/k/EsbLVJNl9n6Vz3fTc+k2GeKQ==
+  dependencies:
+    setimmediate "^1.0.4"
+
+to-arraybuffer@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43"
+  integrity sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=
+
+to-fast-properties@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
+  integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=
+
+to-object-path@^0.3.0:
+  version "0.3.0"
+  resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af"
+  integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=
+  dependencies:
+    kind-of "^3.0.2"
+
+to-readable-stream@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771"
+  integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==
+
+to-regex-range@^2.1.0:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38"
+  integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=
+  dependencies:
+    is-number "^3.0.0"
+    repeat-string "^1.6.1"
+
+to-regex@^3.0.1, to-regex@^3.0.2:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce"
+  integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==
+  dependencies:
+    define-property "^2.0.2"
+    extend-shallow "^3.0.2"
+    regex-not "^1.0.2"
+    safe-regex "^1.1.0"
+
+toidentifier@1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553"
+  integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==
+
+toposort@^1.0.0:
+  version "1.0.7"
+  resolved "https://registry.yarnpkg.com/toposort/-/toposort-1.0.7.tgz#2e68442d9f64ec720b8cc89e6443ac6caa950029"
+  integrity sha1-LmhELZ9k7HILjMieZEOsbKqVACk=
+
+truncate-utf8-bytes@^1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz#405923909592d56f78a5818434b0b78489ca5f2b"
+  integrity sha1-QFkjkJWS1W94pYGENLC3hInKXys=
+  dependencies:
+    utf8-byte-length "^1.0.1"
+
+tslib@^1.9.0:
+  version "1.10.0"
+  resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a"
+  integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==
+
+tty-browserify@0.0.0:
+  version "0.0.0"
+  resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6"
+  integrity sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=
+
+tunnel@^0.0.6:
+  version "0.0.6"
+  resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c"
+  integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==
+
+type-fest@^0.3.0:
+  version "0.3.1"
+  resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.3.1.tgz#63d00d204e059474fe5e1b7c011112bbd1dc29e1"
+  integrity sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==
+
+type-fest@^0.8.0:
+  version "0.8.1"
+  resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d"
+  integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==
+
+type-is@~1.6.17, type-is@~1.6.18:
+  version "1.6.18"
+  resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131"
+  integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==
+  dependencies:
+    media-typer "0.3.0"
+    mime-types "~2.1.24"
+
+typedarray@^0.0.6:
+  version "0.0.6"
+  resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
+  integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
+
+uc.micro@^1.0.1, uc.micro@^1.0.5:
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac"
+  integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==
+
+uglify-js@3.4.x:
+  version "3.4.10"
+  resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.10.tgz#9ad9563d8eb3acdfb8d38597d2af1d815f6a755f"
+  integrity sha512-Y2VsbPVs0FIshJztycsO2SfPk7/KAF/T72qzv9u5EpQ4kB2hQoHlhNQTsNyy6ul7lQtqJN/AoWeS23OzEiEFxw==
+  dependencies:
+    commander "~2.19.0"
+    source-map "~0.6.1"
+
+underscore@~1.9.1:
+  version "1.9.1"
+  resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.9.1.tgz#06dce34a0e68a7babc29b365b8e74b8925203961"
+  integrity sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg==
+
+unicode-canonical-property-names-ecmascript@^1.0.4:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818"
+  integrity sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==
+
+unicode-match-property-ecmascript@^1.0.4:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz#8ed2a32569961bce9227d09cd3ffbb8fed5f020c"
+  integrity sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==
+  dependencies:
+    unicode-canonical-property-names-ecmascript "^1.0.4"
+    unicode-property-aliases-ecmascript "^1.0.4"
+
+unicode-match-property-value-ecmascript@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.1.0.tgz#5b4b426e08d13a80365e0d657ac7a6c1ec46a277"
+  integrity sha512-hDTHvaBk3RmFzvSl0UVrUmC3PuW9wKVnpoUDYH0JDkSIovzw+J5viQmeYHxVSBptubnr7PbH2e0fnpDRQnQl5g==
+
+unicode-property-aliases-ecmascript@^1.0.4:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.5.tgz#a9cc6cc7ce63a0a3023fc99e341b94431d405a57"
+  integrity sha512-L5RAqCfXqAwR3RriF8pM0lU0w4Ryf/GgzONwi6KnL1taJQa7x1TCxdJnILX59WIGOwR57IVxn7Nej0fz1Ny6fw==
+
+union-value@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847"
+  integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==
+  dependencies:
+    arr-union "^3.1.0"
+    get-value "^2.0.6"
+    is-extendable "^0.1.1"
+    set-value "^2.0.1"
+
+uniq@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff"
+  integrity sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=
+
+uniqs@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/uniqs/-/uniqs-2.0.0.tgz#ffede4b36b25290696e6e165d4a59edb998e6b02"
+  integrity sha1-/+3ks2slKQaW5uFl1KWe25mOawI=
+
+unique-filename@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230"
+  integrity sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==
+  dependencies:
+    unique-slug "^2.0.0"
+
+unique-slug@^2.0.0:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.2.tgz#baabce91083fc64e945b0f3ad613e264f7cd4e6c"
+  integrity sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==
+  dependencies:
+    imurmurhash "^0.1.4"
+
+unique-string@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-1.0.0.tgz#9e1057cca851abb93398f8b33ae187b99caec11a"
+  integrity sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=
+  dependencies:
+    crypto-random-string "^1.0.0"
+
+universalify@^0.1.0:
+  version "0.1.2"
+  resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
+  integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==
+
+unpipe@1.0.0, unpipe@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
+  integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=
+
+unquote@~1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/unquote/-/unquote-1.1.1.tgz#8fded7324ec6e88a0ff8b905e7c098cdc086d544"
+  integrity sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=
+
+unset-value@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559"
+  integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=
+  dependencies:
+    has-value "^0.3.1"
+    isobject "^3.0.0"
+
+upath@^1.1.1:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894"
+  integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==
+
+update-notifier@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-3.0.1.tgz#78ecb68b915e2fd1be9f767f6e298ce87b736250"
+  integrity sha512-grrmrB6Zb8DUiyDIaeRTBCkgISYUgETNe7NglEbVsrLWXeESnlCSP50WfRSj/GmzMPl6Uchj24S/p80nP/ZQrQ==
+  dependencies:
+    boxen "^3.0.0"
+    chalk "^2.0.1"
+    configstore "^4.0.0"
+    has-yarn "^2.1.0"
+    import-lazy "^2.1.0"
+    is-ci "^2.0.0"
+    is-installed-globally "^0.1.0"
+    is-npm "^3.0.0"
+    is-yarn-global "^0.3.0"
+    latest-version "^5.0.0"
+    semver-diff "^2.0.0"
+    xdg-basedir "^3.0.0"
+
+upper-case@^1.1.1:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598"
+  integrity sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=
+
+uri-js@^4.2.2:
+  version "4.2.2"
+  resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0"
+  integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==
+  dependencies:
+    punycode "^2.1.0"
+
+urix@^0.1.0:
+  version "0.1.0"
+  resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72"
+  integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=
+
+url-loader@^2.0.1:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-2.2.0.tgz#af321aece1fd0d683adc8aaeb27829f29c75b46e"
+  integrity sha512-G8nk3np8ZAnwhHXas1JxJEwJyQdqFXAKJehfgZ/XrC48volFBRtO+FIKtF2u0Ma3bw+4vnDVjHPAQYlF9p2vsw==
+  dependencies:
+    loader-utils "^1.2.3"
+    mime "^2.4.4"
+    schema-utils "^2.4.1"
+
+url-parse-lax@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c"
+  integrity sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=
+  dependencies:
+    prepend-http "^2.0.0"
+
+url-parse@^1.4.3:
+  version "1.4.7"
+  resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.7.tgz#a8a83535e8c00a316e403a5db4ac1b9b853ae278"
+  integrity sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==
+  dependencies:
+    querystringify "^2.1.1"
+    requires-port "^1.0.0"
+
+url@^0.11.0:
+  version "0.11.0"
+  resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1"
+  integrity sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=
+  dependencies:
+    punycode "1.3.2"
+    querystring "0.2.0"
+
+use@^3.1.0:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"
+  integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==
+
+utf8-byte-length@^1.0.1:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz#f45f150c4c66eee968186505ab93fcbb8ad6bf61"
+  integrity sha1-9F8VDExm7uloGGUFq5P8u4rWv2E=
+
+util-deprecate@^1.0.1, util-deprecate@~1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
+  integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
+
+util.promisify@1.0.0, util.promisify@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.0.tgz#440f7165a459c9a16dc145eb8e72f35687097030"
+  integrity sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==
+  dependencies:
+    define-properties "^1.1.2"
+    object.getownpropertydescriptors "^2.0.3"
+
+util@0.10.3:
+  version "0.10.3"
+  resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9"
+  integrity sha1-evsa/lCAUkZInj23/g7TeTNqwPk=
+  dependencies:
+    inherits "2.0.1"
+
+util@^0.11.0:
+  version "0.11.1"
+  resolved "https://registry.yarnpkg.com/util/-/util-0.11.1.tgz#3236733720ec64bb27f6e26f421aaa2e1b588d61"
+  integrity sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==
+  dependencies:
+    inherits "2.0.3"
+
+utila@^0.4.0, utila@~0.4:
+  version "0.4.0"
+  resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c"
+  integrity sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=
+
+utils-merge@1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
+  integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=
+
+uuid@^3.0.1, uuid@^3.3.2:
+  version "3.3.3"
+  resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.3.tgz#4568f0216e78760ee1dbf3a4d2cf53e224112866"
+  integrity sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==
+
+v8-compile-cache@2.0.3:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.0.3.tgz#00f7494d2ae2b688cfe2899df6ed2c54bef91dbe"
+  integrity sha512-CNmdbwQMBjwr9Gsmohvm0pbL954tJrNzf6gWL3K+QMQf00PF7ERGrEiLgjuU3mKreLC2MeGhUsNV9ybTbLgd3w==
+
+validate-npm-package-license@^3.0.1:
+  version "3.0.4"
+  resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a"
+  integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==
+  dependencies:
+    spdx-correct "^3.0.0"
+    spdx-expression-parse "^3.0.0"
+
+vary@~1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
+  integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=
+
+vendors@^1.0.0:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.3.tgz#a6467781abd366217c050f8202e7e50cc9eef8c0"
+  integrity sha512-fOi47nsJP5Wqefa43kyWSg80qF+Q3XA6MUkgi7Hp1HQaKDQW4cQrK2D0P7mmbFtsV1N89am55Yru/nyEwRubcw==
+
+vm-browserify@^1.0.1:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0"
+  integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==
+
+watchpack@^1.6.0:
+  version "1.6.0"
+  resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.6.0.tgz#4bc12c2ebe8aa277a71f1d3f14d685c7b446cd00"
+  integrity sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA==
+  dependencies:
+    chokidar "^2.0.2"
+    graceful-fs "^4.1.2"
+    neo-async "^2.5.0"
+
+wbuf@^1.1.0, wbuf@^1.7.3:
+  version "1.7.3"
+  resolved "https://registry.yarnpkg.com/wbuf/-/wbuf-1.7.3.tgz#c1d8d149316d3ea852848895cb6a0bfe887b87df"
+  integrity sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==
+  dependencies:
+    minimalistic-assert "^1.0.0"
+
+webpack-cli@^3.3.5:
+  version "3.3.10"
+  resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-3.3.10.tgz#17b279267e9b4fb549023fae170da8e6e766da13"
+  integrity sha512-u1dgND9+MXaEt74sJR4PR7qkPxXUSQ0RXYq8x1L6Jg1MYVEmGPrH6Ah6C4arD4r0J1P5HKjRqpab36k0eIzPqg==
+  dependencies:
+    chalk "2.4.2"
+    cross-spawn "6.0.5"
+    enhanced-resolve "4.1.0"
+    findup-sync "3.0.0"
+    global-modules "2.0.0"
+    import-local "2.0.0"
+    interpret "1.2.0"
+    loader-utils "1.2.3"
+    supports-color "6.1.0"
+    v8-compile-cache "2.0.3"
+    yargs "13.2.4"
+
+webpack-dev-middleware@^3.7.2:
+  version "3.7.2"
+  resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.7.2.tgz#0019c3db716e3fa5cecbf64f2ab88a74bab331f3"
+  integrity sha512-1xC42LxbYoqLNAhV6YzTYacicgMZQTqRd27Sim9wn5hJrX3I5nxYy1SxSd4+gjUFsz1dQFj+yEe6zEVmSkeJjw==
+  dependencies:
+    memory-fs "^0.4.1"
+    mime "^2.4.4"
+    mkdirp "^0.5.1"
+    range-parser "^1.2.1"
+    webpack-log "^2.0.0"
+
+webpack-dev-server@^3.7.2:
+  version "3.9.0"
+  resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.9.0.tgz#27c3b5d0f6b6677c4304465ac817623c8b27b89c"
+  integrity sha512-E6uQ4kRrTX9URN9s/lIbqTAztwEPdvzVrcmHE8EQ9YnuT9J8Es5Wrd8n9BKg1a0oZ5EgEke/EQFgUsp18dSTBw==
+  dependencies:
+    ansi-html "0.0.7"
+    bonjour "^3.5.0"
+    chokidar "^2.1.8"
+    compression "^1.7.4"
+    connect-history-api-fallback "^1.6.0"
+    debug "^4.1.1"
+    del "^4.1.1"
+    express "^4.17.1"
+    html-entities "^1.2.1"
+    http-proxy-middleware "0.19.1"
+    import-local "^2.0.0"
+    internal-ip "^4.3.0"
+    ip "^1.1.5"
+    is-absolute-url "^3.0.3"
+    killable "^1.0.1"
+    loglevel "^1.6.4"
+    opn "^5.5.0"
+    p-retry "^3.0.1"
+    portfinder "^1.0.25"
+    schema-utils "^1.0.0"
+    selfsigned "^1.10.7"
+    semver "^6.3.0"
+    serve-index "^1.9.1"
+    sockjs "0.3.19"
+    sockjs-client "1.4.0"
+    spdy "^4.0.1"
+    strip-ansi "^3.0.1"
+    supports-color "^6.1.0"
+    url "^0.11.0"
+    webpack-dev-middleware "^3.7.2"
+    webpack-log "^2.0.0"
+    ws "^6.2.1"
+    yargs "12.0.5"
+
+webpack-log@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/webpack-log/-/webpack-log-2.0.0.tgz#5b7928e0637593f119d32f6227c1e0ac31e1b47f"
+  integrity sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==
+  dependencies:
+    ansi-colors "^3.0.0"
+    uuid "^3.3.2"
+
+webpack-merge@^4.2.1:
+  version "4.2.2"
+  resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-4.2.2.tgz#a27c52ea783d1398afd2087f547d7b9d2f43634d"
+  integrity sha512-TUE1UGoTX2Cd42j3krGYqObZbOD+xF7u28WB7tfUordytSjbWTIjK/8V0amkBfTYN4/pB/GIDlJZZ657BGG19g==
+  dependencies:
+    lodash "^4.17.15"
+
+webpack-sources@^1.1.0, webpack-sources@^1.4.0, webpack-sources@^1.4.1:
+  version "1.4.3"
+  resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933"
+  integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==
+  dependencies:
+    source-list-map "^2.0.0"
+    source-map "~0.6.1"
+
+webpack@^4.41.4:
+  version "4.41.4"
+  resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.41.4.tgz#4bec4125224bdf50efa8be6226c19047599cd034"
+  integrity sha512-Lc+2uB6NjpCWsHI3trkoISOI64h9QYIXenbEWj3bn3oyjfB1lEBXjWAfAyY2sM0rZn41oD5V91OLwKRwS6Wp8Q==
+  dependencies:
+    "@webassemblyjs/ast" "1.8.5"
+    "@webassemblyjs/helper-module-context" "1.8.5"
+    "@webassemblyjs/wasm-edit" "1.8.5"
+    "@webassemblyjs/wasm-parser" "1.8.5"
+    acorn "^6.2.1"
+    ajv "^6.10.2"
+    ajv-keywords "^3.4.1"
+    chrome-trace-event "^1.0.2"
+    enhanced-resolve "^4.1.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.1"
+    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.6.0"
+    webpack-sources "^1.4.1"
+
+websocket-driver@>=0.5.1:
+  version "0.7.3"
+  resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.3.tgz#a2d4e0d4f4f116f1e6297eba58b05d430100e9f9"
+  integrity sha512-bpxWlvbbB459Mlipc5GBzzZwhoZgGEZLuqPaR0INBGnPAY1vdBX6hPnoFXiw+3yWxDuHyQjO2oXTMyS8A5haFg==
+  dependencies:
+    http-parser-js ">=0.4.0 <0.4.11"
+    safe-buffer ">=5.1.0"
+    websocket-extensions ">=0.1.1"
+
+websocket-extensions@>=0.1.1:
+  version "0.1.3"
+  resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.3.tgz#5d2ff22977003ec687a4b87073dfbbac146ccf29"
+  integrity sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==
+
+whet.extend@~0.9.9:
+  version "0.9.9"
+  resolved "https://registry.yarnpkg.com/whet.extend/-/whet.extend-0.9.9.tgz#f877d5bf648c97e5aa542fadc16d6a259b9c11a1"
+  integrity sha1-+HfVv2SMl+WqVC+twW1qJZucEaE=
+
+which-module@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
+  integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=
+
+which@^1.2.14, which@^1.2.9, which@^1.3.1:
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
+  integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
+  dependencies:
+    isexe "^2.0.0"
+
+wide-align@^1.1.0:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457"
+  integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==
+  dependencies:
+    string-width "^1.0.2 || 2"
+
+widest-line@^2.0.0:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-2.0.1.tgz#7438764730ec7ef4381ce4df82fb98a53142a3fc"
+  integrity sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==
+  dependencies:
+    string-width "^2.1.1"
+
+worker-farm@^1.7.0:
+  version "1.7.0"
+  resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.7.0.tgz#26a94c5391bbca926152002f69b84a4bf772e5a8"
+  integrity sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==
+  dependencies:
+    errno "~0.1.7"
+
+wrap-ansi@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85"
+  integrity sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=
+  dependencies:
+    string-width "^1.0.1"
+    strip-ansi "^3.0.1"
+
+wrap-ansi@^5.1.0:
+  version "5.1.0"
+  resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09"
+  integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==
+  dependencies:
+    ansi-styles "^3.2.0"
+    string-width "^3.0.0"
+    strip-ansi "^5.0.0"
+
+wrappy@1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
+  integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
+
+write-file-atomic@^2.0.0:
+  version "2.4.3"
+  resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.4.3.tgz#1fd2e9ae1df3e75b8d8c367443c692d4ca81f481"
+  integrity sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==
+  dependencies:
+    graceful-fs "^4.1.11"
+    imurmurhash "^0.1.4"
+    signal-exit "^3.0.2"
+
+ws@^6.2.1:
+  version "6.2.1"
+  resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb"
+  integrity sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==
+  dependencies:
+    async-limiter "~1.0.0"
+
+xdg-basedir@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4"
+  integrity sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=
+
+xmlcreate@^2.0.0:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/xmlcreate/-/xmlcreate-2.0.1.tgz#2ec38bd7b708d213fd1a90e2431c4af9c09f6a52"
+  integrity sha512-MjGsXhKG8YjTKrDCXseFo3ClbMGvUD4en29H2Cev1dv4P/chlpw6KdYmlCWDkhosBVKRDjM836+3e3pm1cBNJA==
+
+xpipe@*:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/xpipe/-/xpipe-1.0.5.tgz#8dd8bf45fc3f7f55f0e054b878f43a62614dafdf"
+  integrity sha1-jdi/Rfw/f1Xw4FS4ePQ6YmFNr98=
+
+xtend@^4.0.0, xtend@~4.0.1:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
+  integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
+
+"y18n@^3.2.1 || ^4.0.0", y18n@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b"
+  integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==
+
+yallist@^2.1.2:
+  version "2.1.2"
+  resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"
+  integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=
+
+yallist@^3.0.0, yallist@^3.0.2, yallist@^3.0.3:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd"
+  integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==
+
+yargs-parser@^11.1.1:
+  version "11.1.1"
+  resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-11.1.1.tgz#879a0865973bca9f6bab5cbdf3b1c67ec7d3bcf4"
+  integrity sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==
+  dependencies:
+    camelcase "^5.0.0"
+    decamelize "^1.2.0"
+
+yargs-parser@^13.1.0, yargs-parser@^13.1.1:
+  version "13.1.1"
+  resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.1.tgz#d26058532aa06d365fe091f6a1fc06b2f7e5eca0"
+  integrity sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==
+  dependencies:
+    camelcase "^5.0.0"
+    decamelize "^1.2.0"
+
+yargs@12.0.5:
+  version "12.0.5"
+  resolved "https://registry.yarnpkg.com/yargs/-/yargs-12.0.5.tgz#05f5997b609647b64f66b81e3b4b10a368e7ad13"
+  integrity sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==
+  dependencies:
+    cliui "^4.0.0"
+    decamelize "^1.2.0"
+    find-up "^3.0.0"
+    get-caller-file "^1.0.1"
+    os-locale "^3.0.0"
+    require-directory "^2.1.1"
+    require-main-filename "^1.0.1"
+    set-blocking "^2.0.0"
+    string-width "^2.0.0"
+    which-module "^2.0.0"
+    y18n "^3.2.1 || ^4.0.0"
+    yargs-parser "^11.1.1"
+
+yargs@13.2.4:
+  version "13.2.4"
+  resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.2.4.tgz#0b562b794016eb9651b98bd37acf364aa5d6dc83"
+  integrity sha512-HG/DWAJa1PAnHT9JAhNa8AbAv3FPaiLzioSjCcmuXXhP8MlpHO5vwls4g4j6n30Z74GVQj8Xa62dWVx1QCGklg==
+  dependencies:
+    cliui "^5.0.0"
+    find-up "^3.0.0"
+    get-caller-file "^2.0.1"
+    os-locale "^3.1.0"
+    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.0"
+
+yargs@^13.2.4, yargs@^13.3.0:
+  version "13.3.0"
+  resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.0.tgz#4c657a55e07e5f2cf947f8a366567c04a0dedc83"
+  integrity sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==
+  dependencies:
+    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.1"
+
+yauzl@2.4.1:
+  version "2.4.1"
+  resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.4.1.tgz#9528f442dab1b2284e58b4379bb194e22e0c4005"
+  integrity sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=
+  dependencies:
+    fd-slicer "~1.0.1"
+
+zeromq@^6.0.0-beta.5:
+  version "6.0.0-beta.5"
+  resolved "https://registry.yarnpkg.com/zeromq/-/zeromq-6.0.0-beta.5.tgz#098505fcb66beb78eeb0c541efe415d3e2090292"
+  integrity sha512-zsSbjfLjDxQq/Mp6NemNK/AVpm1NeafJRgz7nos1nV9MI/3w/P76pFdlqzxRTW4cuNqyFXJ4Q7g2tUhaZi4rcg==
+  dependencies:
+    node-gyp-build "^4.1.0"
diff --git a/src/kernel_services/ast_printing/printer_tag.ml b/src/kernel_services/ast_printing/printer_tag.ml
index 9009c9ac71197952e6659c2505935138976c13d0..a2befca2aa5df23dd6a38a390fe48ae753240b3d 100644
--- a/src/kernel_services/ast_printing/printer_tag.ml
+++ b/src/kernel_services/ast_printing/printer_tag.ml
@@ -38,7 +38,7 @@ type localizable =
   | PIP of Property.t
 
 module Localizable =
-  Datatype.Make
+  Datatype.Make_with_collections
     (struct
       include Datatype.Undefined
       type t = localizable
@@ -69,11 +69,12 @@ module Localizable =
         | PStmt (_,ki1), PStmt (_,ki2) -> ki1.sid = ki2.sid
         | PStmtStart (_,ki1), PStmtStart (_,ki2) -> ki1.sid = ki2.sid
         | PLval (_,ki1,lv1), PLval (_,ki2,lv2) ->
-          Kinstr.equal ki1 ki2 && lv1 == lv2
+          Kinstr.equal ki1 ki2 &&
+          Lval.equal lv1 lv2
         | PTermLval (_,ki1,pi1,lv1), PTermLval (_,ki2,pi2,lv2) ->
-          Kinstr.equal ki1 ki2 && Property.equal pi1 pi2 &&
-          Logic_utils.is_same_tlval lv1 lv2
-        (* [JS 2008/01/21] term_lval are not shared: cannot use == *)
+          Kinstr.equal ki1 ki2 &&
+          Property.equal pi1 pi2 &&
+          Term_lval.equal lv1 lv2
         | PVDecl (_,_,v1), PVDecl (_,_,v2) -> Varinfo.equal v1 v2
         | PExp (_,_,e1), PExp(_,_,e2) -> Exp.equal e1 e2
         | PIP ip1, PIP ip2 -> Property.equal ip1 ip2
@@ -82,6 +83,37 @@ module Localizable =
           | PIP _ | PGlobal _), _
           ->  false
 
+      let compare l1 l2 = match l1,l2 with
+        | PStmt(_,s1) , PStmt(_,s2) -> Stmt.compare s1 s2
+        | PStmt _ , _ -> (-1)
+        | _ , PStmt _ -> 1
+        | PStmtStart(_,s1) , PStmtStart(_,s2) -> Stmt.compare s1 s2
+        | PStmtStart _ , _ -> (-1)
+        | _ , PStmtStart _ -> 1
+        | PLval (_,k1,lv1), PLval(_,k2,lv2) ->
+          let cmp = Kinstr.compare k1 k2 in
+          if cmp<>0 then cmp else Lval.compare lv1 lv2
+        | PLval _ , _ -> (-1)
+        | _ , PLval _ -> 1
+        | PTermLval(_,k1,p1,lv1) , PTermLval(_,k2,p2,lv2) ->
+          let cmp = Kinstr.compare k1 k2 in
+          if cmp<>0 then cmp else
+            let cmp = Property.compare p1 p2 in
+            if cmp<>0 then cmp else
+              Term_lval.compare lv1 lv2
+        | PTermLval _ , _ -> (-1)
+        | _ , PTermLval _ -> 1
+        | PVDecl(_,_,v1) , PVDecl(_,_,v2) -> Varinfo.compare v1 v2
+        | PVDecl _ , _ -> (-1)
+        | _ , PVDecl _ -> 1
+        | PExp(_,_,e1) , PExp(_,_,e2) -> Exp.compare e1 e2
+        | PExp _ , _ -> (-1)
+        | _ , PExp _ -> 1
+        | PIP p1 , PIP p2 -> Property.compare p1 p2
+        | PIP _ , _ -> (-1)
+        | _ , PIP _ -> 1
+        | PGlobal g1 , PGlobal g2 -> Global.compare g1 g2
+
       let pp_ki_loc fmt ki =
         match ki with
         | Kglobal -> (* no location, print 'global' *)
diff --git a/src/kernel_services/ast_printing/printer_tag.mli b/src/kernel_services/ast_printing/printer_tag.mli
index befac9be01d0e1273a2a189ec5dc4a43bf467b50..074ec5c58ef05e47a804294db77e5b7fd1903f03 100644
--- a/src/kernel_services/ast_printing/printer_tag.mli
+++ b/src/kernel_services/ast_printing/printer_tag.mli
@@ -41,7 +41,7 @@ type localizable =
                           definitions. *)
   | PIP of Property.t
 
-module Localizable: Datatype.S with type t = localizable
+module Localizable: Datatype.S_with_collections with type t = localizable
 
 val kf_of_localizable : localizable -> kernel_function option
 val ki_of_localizable : localizable -> kinstr
diff --git a/src/kernel_services/plugin_entry_points/db.mli b/src/kernel_services/plugin_entry_points/db.mli
index efe0822a53b112aadc3d0beb52d143f7581bc9fa..565c20806a04f78a636ff9dca8a062f9e6af3b87 100644
--- a/src/kernel_services/plugin_entry_points/db.mli
+++ b/src/kernel_services/plugin_entry_points/db.mli
@@ -1375,13 +1375,6 @@ val cancel : unit -> unit
    [job] on [data] while registering [trigger] as temporary (debounced) daemon.
    The daemon is finally unregistered at the end of the computation.
 
-   {b Raises} every exception raised during the execution of [job] on [data].
-
-   @param debounced the least amount of time, in milliseconds, between two
-   successive calls to the daemon (default is 0ms).
-   @param on_delayed the callback invoked as soon as the time since the last
-   [yield] is greater than [debounced] milliseconds (or 100ms at least).
-
    Illustrative example, where [...] is the debounced time:
    {[
        job data :    |<-------------------------------------------------->|<daemon removed>
@@ -1391,9 +1384,16 @@ val cancel : unit -> unit
        notes    :      (1)           (2)           (3)
    ]}
 
-   {ol {li First yield, normal trigger.}
-   {li Debounced yields leads to this second trigger.}
-   {li Delayed warning invoked since there was no yield for more than debounced period.}}
+   + First yield, normal trigger.
+   + Debounced yields leads to this second trigger.
+   + Delayed warning invoked since there was no yield for more than debounced period.
+
+   Raises every exception raised during the execution of [job] on [data].
+
+   @param debounced the least amount of time, in milliseconds, between two
+   successive calls to the daemon (default is 0ms).
+   @param on_delayed the callback invoked as soon as the time since the last
+   [yield] is greater than [debounced] milliseconds (or 100ms at least).
 *)
 val with_progress :
   ?debounced:int -> ?on_delayed:(int -> unit) ->
diff --git a/src/plugins/server/Makefile.in b/src/plugins/server/Makefile.in
index 48b3e23e469e7fa3aeb5903b08f38543e0cecdff..0c9644098263ac6ff133193aad18149ea977694a 100644
--- a/src/plugins/server/Makefile.in
+++ b/src/plugins/server/Makefile.in
@@ -39,11 +39,14 @@ PLUGIN_NAME:=Server
 PLUGIN_CMO:= \
 	server_parameters \
 	jbuffer \
-	doc syntax data main request \
+	doc syntax data \
+	main request states \
 	server_batch \
 	kernel_main \
 	kernel_project \
-	kernel_ast
+	kernel_ast \
+	kernel_properties
+
 PLUGIN_DISTRIBUTED:=$(PLUGIN_ENABLE)
 PLUGIN_DISTRIB_EXTERNAL:= Makefile.in configure.ac configure
 PLUGIN_TESTS_DIRS := batch
@@ -76,7 +79,10 @@ include $(FRAMAC_SHARE)/Makefile.dynamic
 ##############
 
 SERVER_API= \
-	doc.mli syntax.mli data.mli request.mli
+	doc.mli syntax.mli data.mli request.mli states.mli \
+	kernel_main.mli \
+	kernel_ast.mli \
+	kernel_properties.mli
 
 define Capitalize
 $(shell printf "%s%s" \
diff --git a/src/plugins/server/data.ml b/src/plugins/server/data.ml
index 01e6464bc795aaa363e03bd8dfe5d3c1b1bbdfe9..fc2c301f10a7b9767efac7374f0807850a768a66 100644
--- a/src/plugins/server/data.ml
+++ b/src/plugins/server/data.ml
@@ -63,6 +63,8 @@ let failure ?json msg =
 let failure_from_type_error msg json =
   failure ~json "%s" msg
 
+let page = Doc.page `Kernel ~title:"Basic Types" ~filename:"data.md"
+
 (* -------------------------------------------------------------------------- *)
 (* --- Option                                                             --- *)
 (* -------------------------------------------------------------------------- *)
@@ -73,7 +75,7 @@ struct
 
   let nullable = try ignore (A.of_json `Null) ; true with _ -> false
   let syntax =
-    Syntax.option (if nullable then A.syntax else Syntax.tuple [A.syntax])
+    Syntax.option (if not nullable then A.syntax else Syntax.tuple [A.syntax])
 
   let to_json = function
     | None -> `Null
@@ -234,25 +236,25 @@ end
 
 module Fmap = Map.Make(String)
 
-type 'a record = json Fmap.t
+module Record =
+struct
 
-type ('r,'a) field = {
-  member : 'r record -> bool ;
-  getter : 'r record -> 'a ;
-  setter : 'r record -> 'a -> 'r record ;
-}
+  type 'a record = json Fmap.t
 
-type 'a signature = {
-  page : Doc.page ;
-  name : string ;
-  descr : Markdown.text ;
-  mutable fields : Syntax.field list ;
-  mutable default : 'a record ;
-  mutable published : bool ;
-}
+  type ('r,'a) field = {
+    member : 'r record -> bool ;
+    getter : 'r record -> 'a ;
+    setter : 'r record -> 'a -> 'r record ;
+  }
 
-module Record =
-struct
+  type 'a signature = {
+    page : Doc.page ;
+    name : string ;
+    descr : Markdown.text ;
+    mutable fields : Syntax.field list ;
+    mutable default : 'a record ;
+    mutable published : bool ;
+  }
 
   module type S =
   sig
@@ -271,16 +273,24 @@ struct
     default = Fmap.empty ;
   }
 
+  let invalid name reason =
+    let msg = Printf.sprintf "Server.Data.Record.%s: %s" name reason in
+    raise (Invalid_argument msg)
+
   let field (type a r) (s : r signature)
       ~name ~descr ?default (d : a data) : (r,a) field =
     if s.published then
-      raise (Invalid_argument "Server.Data.Record.field") ;
+      invalid s.name (Printf.sprintf "published record (%s)" name) ;
     let module D = (val d) in
     begin match default with
       | None -> ()
       | Some v -> s.default <- Fmap.add name (D.to_json v) s.default
     end ;
-    let field = Syntax.{ name ; syntax = D.syntax ; descr } in
+    let field = Syntax.{
+        fd_name = name ;
+        fd_syntax = D.syntax ;
+        fd_descr = descr ;
+      } in
     s.fields <- field :: s.fields ;
     let member r = Fmap.mem name r in
     let getter r = D.of_json (Fmap.find name r) in
@@ -290,9 +300,13 @@ struct
   let option (type a r) (s : r signature)
       ~name ~descr (d : a data) : (r,a option) field =
     if s.published then
-      raise (Invalid_argument "Server.Data.Record.option") ;
+      invalid s.name (Printf.sprintf "published record (%s)" name) ;
     let module D = (val d) in
-    let field = Syntax.{ name ; syntax = option D.syntax ; descr } in
+    let field = Syntax.{
+        fd_name = name ;
+        fd_syntax = option D.syntax ;
+        fd_descr = descr ;
+      } in
     s.fields <- field :: s.fields ;
     let member r = Fmap.mem name r in
     let getter r =
@@ -304,7 +318,7 @@ struct
 
   let publish (type r) (s : r signature) =
     if s.published then
-      raise (Invalid_argument "Server.Data.Record.publish") ;
+      invalid s.name "already published record" ;
     let module M =
     struct
       type nonrec r = r
@@ -335,6 +349,157 @@ struct
 
 end
 
+module Jmarkdown : S with type t = Markdown.text =
+struct
+
+  type t = Markdown.text
+  let syntax = Syntax.publish ~page
+      ~name:"markdown" ~descr:(Markdown.plain "Markdown (inlined text)")
+      ~synopsis:Syntax.string ()
+  let of_json js = Markdown.plain (Ju.to_string js)
+  let to_json txt =
+    `String (Pretty_utils.to_string (Markdown.pp_text ?page:None) txt)
+
+end
+
+(* -------------------------------------------------------------------------- *)
+(* --- Enums                                                              --- *)
+(* -------------------------------------------------------------------------- *)
+
+module Tag = Collection
+    (struct
+      type t = Syntax.tag
+
+      let syntax = Syntax.publish ~page ~name:"tag"
+          ~descr:(Markdown.plain "Tag description")
+          ~synopsis:(Syntax.record [
+              "name",Syntax.string ;
+              "label",Jmarkdown.syntax ;
+              "descr",Jmarkdown.syntax ;
+            ]) ()
+
+      let to_json tg = `Assoc [
+          "name", `String tg.Syntax.tag_name ;
+          "label", Jmarkdown.to_json tg.tag_label ;
+          "descr" , Jmarkdown.to_json tg.tag_descr ;
+        ]
+
+      let of_json js = Syntax.{
+          tag_name = Ju.member "name" js |> Ju.to_string ;
+          tag_label = Ju.member "label" js |> Jmarkdown.of_json ;
+          tag_descr = Ju.member "descr" js |> Jmarkdown.of_json ;
+        }
+    end)
+
+module Enum =
+struct
+
+  type 'a dictionary = {
+    page : Doc.page ;
+    name : string ;
+    title : string ;
+    descr : Markdown.text ;
+    values : (string,'a option) Hashtbl.t ;
+    vindex : ('a,string) Hashtbl.t ;
+    mutable syntax : Markdown.text ;
+    mutable published : bool ;
+    mutable tags : Syntax.tag list ;
+  }
+
+  type 'a tag = string
+  type 'a prefix = string
+
+  let tag_name tg = tg
+  let tag_label a = function
+    | None -> Markdown.plain (String.(capitalize_ascii (lowercase_ascii a)))
+    | Some lbl -> lbl
+
+  let dictionary ~page ~name ~title ~descr () = {
+    page ; name ; descr ; title ;
+    published = false ;
+    values = Hashtbl.create 0 ;
+    vindex = Hashtbl.create 0 ;
+    syntax = [] ;
+    tags = [] ;
+  }
+
+  let invalid name reason =
+    let msg = Printf.sprintf "Server.Data.Enum.%s: %s" name reason in
+    raise (Invalid_argument msg)
+
+  let page (d : 'a dictionary) = d.page
+  let name (d : 'a dictionary) = d.name
+  let syntax (d : 'a dictionary) = d.syntax
+
+  let tag (d : 'a dictionary) ~name ?label ~descr ?value () : 'a tag =
+    if Hashtbl.mem d.values name then
+      invalid d.name (Printf.sprintf "duplicate tag (%s)" name) ;
+    let tg = Syntax.{
+        tag_name = name ;
+        tag_label = tag_label name label ;
+        tag_descr = descr ;
+      } in
+    d.tags <- tg :: d.tags ;
+    Hashtbl.add d.values name value ;
+    begin match value with
+      | None -> ()
+      | Some v -> Hashtbl.add d.vindex v name
+    end ; name
+
+  let instance = Printf.sprintf "%s:%s"
+
+  let prefix (d : 'a dictionary) ~prefix ?(var="*") ?label ~descr () =
+    let tg = Syntax.{
+        tag_name = instance prefix var ;
+        tag_label = tag_label (prefix ^ ".") label ;
+        tag_descr = descr ;
+      } in
+    d.tags <- tg :: d.tags ; prefix
+
+  let extends d prefix ~name ?label ~descr ?value () =
+    tag d ~name:(instance prefix name) ?label ~descr ?value ()
+
+  let to_json name vindex v =
+    try `String (Hashtbl.find vindex v)
+    with Not_found ->
+      failure "[%s] Value not found" name
+
+  let of_json name values js =
+    let tag = Ju.to_string js in
+    match Hashtbl.find values tag with
+    | Some v -> v
+    | None ->
+      failure "[%s] No registered value for tag '%s" name tag
+    | exception Not_found ->
+      failure "[%s] Not registered tag '%s" name tag
+
+  let tags d = List.rev d.tags
+
+  let publish (type a) (d : a dictionary) ?tag () =
+    if d.published then
+      invalid d.name "already published" ;
+    let module M =
+    struct
+      type t = a
+      let descr = d.descr
+      let syntax =
+        let tags () = [Syntax.tags ~title:d.title (List.rev d.tags)] in
+        Syntax.publish ~page:d.page ~name:d.name ~descr
+          ~synopsis:(Syntax.string) ~generated:tags ()
+      let of_json = of_json d.name d.values
+      let to_json =
+        match tag with
+        | None -> to_json d.name d.vindex
+        | Some to_tag -> fun x -> `String (to_tag x)
+    end in
+    begin
+      d.published <- true ;
+      d.syntax <- Syntax.text M.syntax ;
+      (module M : S with type t = a)
+    end
+
+end
+
 (* -------------------------------------------------------------------------- *)
 (* --- Index                                                              --- *)
 (* -------------------------------------------------------------------------- *)
@@ -512,78 +677,3 @@ struct
 end
 
 (* -------------------------------------------------------------------------- *)
-(* --- Dictionnary                                                        --- *)
-(* -------------------------------------------------------------------------- *)
-
-module type Enum =
-sig
-  type t
-  val values : (t * string * Markdown.text) list
-  include Info
-end
-
-module Dictionary(E : Enum) =
-struct
-
-  let registered = ref false
-  let index = Hashtbl.create 0
-  let lookup = Hashtbl.create 0
-
-  let register () =
-    if not !registered then
-      begin
-        registered := true ;
-        let invalid msg tag =
-          let msg = Printf.sprintf "Server.Data.Enum.%s: duplicate %s (%S)"
-              E.name msg tag in
-          raise (Invalid_argument msg)
-        in
-        List.iter
-          (fun (value,tag,_) ->
-             if Hashtbl.mem index value then invalid "value" tag ;
-             Hashtbl.add index value tag ;
-             if Hashtbl.mem lookup tag then invalid "tag" tag ;
-             Hashtbl.add lookup tag value ;
-          ) E.values
-      end
-
-  let values =
-    let open Markdown in
-    let caption = Some (plain "Values description") in
-    let header = [ plain E.name, Left; plain "Description", Left ] in
-    let content =
-      List.map
-        (fun (_,tag,descr) -> [ format "`%S`" tag ; descr ])
-        E.values
-    in
-    Table { caption; header; content }
-
-  include Collection
-      (struct
-        type t = E.t
-
-        let syntax = Syntax.publish
-            ~page:E.page ~name:E.name
-            ~synopsis:Syntax.ident
-            ~descr:E.descr ~details:[values] ()
-
-        let to_json value =
-          register () ;
-          try `String (Hashtbl.find index value)
-          with Not_found ->
-            raise (Invalid_argument
-                     (Printf.sprintf "[%s] Unregistered value" E.name))
-
-        let of_json js =
-          register () ;
-          let tag = Ju.to_string js in
-          try Hashtbl.find lookup tag
-          with Not_found ->
-            let msg = Printf.sprintf "[%s] Unregistered tag %S" E.name tag in
-            raise (Ju.Type_error(msg,js))
-
-      end)
-
-end
-
-(* -------------------------------------------------------------------------- *)
diff --git a/src/plugins/server/data.mli b/src/plugins/server/data.mli
index eeda161e364e7174cc779650c7d0c133df635516..bac87bebda5ca38ee8299dfe2e19a7d2e9767853 100644
--- a/src/plugins/server/data.mli
+++ b/src/plugins/server/data.mli
@@ -21,29 +21,56 @@
 (**************************************************************************)
 
 (* -------------------------------------------------------------------------- *)
-(** Data Encoding *)
+(** Data Encoding. *)
+
+(* -------------------------------------------------------------------------- *)
+(** {2 Datatypes}
+
+    This module is responsible for marshaling and demarshaling data to handle
+    communications between the server and the client in both directions.
+
+    Each datatype must be equipped with functions to encode and decode values
+    to/from JSON format. Moreover, data types shall be also properly documented
+    and registered in the generated documentation of the Frama-C server.
+
+    Generally speaking, we will have a module with signature [Data.D] for every
+    datatype to be exchanged with the server. For simple values, predefined
+    modules are already provided. More complex datatypes can be built with some
+    functors, typically for options, lists or arrays.
+
+    Records and enumerated types are typical in JSON formatting, but difficult
+    to build from OCaml records and abstract datatypes. For those kinds of data,
+    we provide an API based on the following general scheme:
+    - First create an empty container with its name, documentation and such;
+    - Then add each field or constructor to the container;
+    - Finally pack the container, which actually registers its complete
+      documentation and returns an OCaml value containing the resulting datatype
+      module.
+
+    Hence, in addition to module signature [Data.S] for values, there is also a
+    polymorphic type ['a Data.data] for module values carrying a data module with
+    type [t = 'a].
+
+    The same mechanism is used throughout modules [States] and [Request] each
+    time a JSON record or tag is needed. *)
 (* -------------------------------------------------------------------------- *)
 
 type json = Json.t
 
+val page : Doc.page (** Documentation page for general purpose data types. *)
 val pretty : Format.formatter -> json -> unit
 
+(** Datatype module signature. *)
 module type S =
 sig
   type t
-  val syntax : Syntax.t
+  val syntax : Syntax.t (** readable description of the JSON format *)
   val of_json : json -> t
   val to_json : t -> json
 end
 
 
-(** Datatype registration.
-
-    Name and page must be consistent with each other:
-    - The name must be lowercase, dash-separated list of identifiers
-    - Protocol data must start with ["<server>-*"]
-    - Plugin data must start with ["<plugin>-*"]
-*)
+(** Datatype informations. *)
 module type Info =
 sig
   val page : Doc.page
@@ -51,6 +78,7 @@ sig
   val descr : Markdown.text
 end
 
+(** Polymorphic data value. *)
 type 'a data = (module S with type t = 'a)
 
 (* -------------------------------------------------------------------------- *)
@@ -67,16 +95,6 @@ end
 
 module Collection(A : S) : S_collection with type t = A.t
 
-(* -------------------------------------------------------------------------- *)
-(** {2 Constructors} *)
-(* -------------------------------------------------------------------------- *)
-
-module Joption(A : S) : S with type t = A.t option
-module Jpair(A : S)(B : S) : S with type t = A.t * B.t
-module Jtriple(A : S)(B : S)(C : S) : S with type t = A.t * B.t * C.t
-module Jlist(A : S) : S with type t = A.t list
-module Jarray(A : S) : S with type t = A.t array
-
 (* -------------------------------------------------------------------------- *)
 (** {2 Atomic Data} *)
 (* -------------------------------------------------------------------------- *)
@@ -88,23 +106,33 @@ module Jint : S_collection with type t = int
 module Jfloat : S_collection with type t = float
 module Jstring : S_collection with type t = string
 module Jident : S_collection with type t = string (** Syntax is {i ident}. *)
-module Jtext : S with type t = json (** Rich text encoding, see [Jbuffer] *)
+module Jtext : S with type t = json (** Rich text encoding, see [Jbuffer]. *)
+module Jmarkdown : S with type t = Markdown.text
 
 (* -------------------------------------------------------------------------- *)
-(** {2 Records} *)
+(** {2 Constructors} *)
 (* -------------------------------------------------------------------------- *)
 
-type 'a record (** Records of type 'a *)
-type 'a signature  (** Opened signature for record of type ['a] *)
-type ('a,'b) field (** Field of type ['b] for a record of type ['a] *)
+module Joption(A : S) : S with type t = A.t option
+module Jpair(A : S)(B : S) : S with type t = A.t * B.t
+module Jtriple(A : S)(B : S)(C : S) : S with type t = A.t * B.t * C.t
+module Jlist(A : S) : S with type t = A.t list
+module Jarray(A : S) : S with type t = A.t array
+
+(* -------------------------------------------------------------------------- *)
+(** {2 Records} *)
+(* -------------------------------------------------------------------------- *)
 
 (** Record factory.
 
     You shall start by declaring a (ghost) type [r] and call
-    [Record.signature] to create a signature of type [r].
-    Then, populate the record with [Record.field] or [Record.option].
-    Finally, you shall call [Record.publish] to obtain a new data module
-    of type [Record with type r = r], which gives you a [Data] with an opaque
+    [Record.signature] to create a signature of type [r], which will be
+    your container to register your record fields.
+
+    Then, populate the signature with [Record.field] or [Record.option].
+    Finally, you shall call [Record.publish] to pack the record signature and
+    obtain a new data module of type [Record with type r = r],
+    which gives you a [Data] with an opaque
     type [t = r record] with fields of type [(r,a) field].
 
     {[
@@ -122,6 +150,10 @@ type ('a,'b) field (** Field of type ['b] for a record of type ['a] *)
 module Record :
 sig
 
+  type 'a record (** Records of type ['a]. *)
+  type 'a signature  (** Opened signature for record of type ['a]. *)
+  type ('a,'b) field (** Field of type ['b] for a record of type ['a]. *)
+
   (** Data with [type t = r record].
       Also contains getters and setters for fields. *)
   module type S =
@@ -134,30 +166,131 @@ sig
     val set : (r,'a) field -> 'a -> t -> t
   end
 
-  (** Create a new, opened record type *)
+  (** Create a new, opened record type. *)
   val signature : page:Doc.page -> name:string -> descr:Markdown.text ->
     unit -> 'a signature
 
-  (** Adds a field to an opened record *)
+  (** Adds a field to an opened record. *)
   val field : 'r signature ->
     name:string -> descr:Markdown.text -> ?default:'a -> 'a data ->
     ('r,'a) field
 
-  (** Adds a optional field to an opened record *)
+  (** Adds a optional field to an opened record. *)
   val option : 'r signature ->
     name:string -> descr:Markdown.text -> 'a data ->
     ('r,'a option) field
 
-  (** Publish and close an opened record *)
-  val publish : 'a signature -> (module S with type r = 'a)
+  (** Publish and close an opened record. *)
+  val publish : 'a signature ->
+    (module S with type r = 'a)
+
+end
+
+(* -------------------------------------------------------------------------- *)
+(** {2 Enums} *)
+(* -------------------------------------------------------------------------- *)
+
+module Tag : S_collection with type t = Syntax.tag
+
+(** Enum factory.
+
+    You shall start by declaring a dictionary with [Enum.dictionary] for your
+    values. Then, populate the dictionary with [Enum.tag] values. Finally, you
+    shall call [Enum.publish] to obtain a new data module for your type.
+
+    You have two options for computing tags: either you provide values when
+    declaring tags, and these tags will be associated to registered values for
+    both directions; alternatively you might provide a [~tag] function to
+    [Enum.publish].
+
+    The difficulty when providing values only at tag definition is to ensure
+    that all possible value has been registered.
+
+    The conversion values from and to json may fail when no value has been
+    registered with tags. *)
+module Enum :
+sig
+
+  type 'a dictionary
+  type 'a tag
+  type 'a prefix
+
+  val tag_name : 'a tag -> string
+
+  (** Creates an opened, empty dictionary. *)
+  val dictionary :
+    page:Doc.page -> name:string -> title:string -> descr:Markdown.text ->
+    unit -> 'a dictionary
+
+  (** Register a new tag in the dictionary.
+      The default label is the capitalized name.
+      The provided value, if any, will be used for decoding json tags.
+      If would be used also for encoding values to json tags if no [~tag]
+      function is provided when publishing the dictionary.
+      Registered values must be hashable with [Hashtbl.hash] function.
+
+      You may register a new tag {i after} the dictionary has been published. *)
+  val tag : 'a dictionary ->
+    name:string ->
+    ?label:Markdown.text -> descr:Markdown.text ->
+    ?value:'a ->
+    unit -> 'a tag
+
+  (** Register a new prefix tag in the dictionary.
+      The default label is the capitalized prefix.
+      To decoding from json is provided to prefix tags.
+      Encoding is done by emitting tags with form ['prefix:*'].
+      The variable part of the prefix is documented as ['prefix:xxx']
+      when [~var:"xxx"] is provided.
+
+      You may register a new prefix-tag {i after} the dictionary has
+      been published. *)
+  val prefix : 'a dictionary ->
+    prefix:string -> ?var:string ->
+    ?label:Markdown.text -> descr:Markdown.text ->
+    unit -> 'a prefix
+
+  (** Returns the tag for a value associated with the given prefix. *)
+  val instance : 'a prefix -> string -> 'a tag
+
+  (** Publish a new instance in the documentation. *)
+  val extends : 'a dictionary -> 'a prefix ->
+    name:string ->
+    ?label:Markdown.text -> descr:Markdown.text ->
+    ?value:'a ->
+    unit -> 'a tag
+
+  (** Obtain all the tags registered in the dictionary so far. *)
+  val tags : 'a dictionary -> Tag.t list
+
+  val page : 'a dictionary -> Doc.page
+  val name : 'a dictionary -> string
+  val syntax : 'a dictionary -> Markdown.text
+
+  (** Publish the dictionary. No more tag nor prefix can be added afterwards. If
+      no [~tag] function is provided, registered values with tags are used. *)
+  val publish :
+    'a dictionary -> ?tag:('a -> 'a tag) -> unit -> (module S with type t = 'a)
 
 end
 
 (* -------------------------------------------------------------------------- *)
-(** {2 Indexed Values} *)
+(** {2 Indexed Values}
+
+    These datatypes automatically index complex values with
+    unique identifiers. This avoids to encode the internal OCaml
+    representation of each value, by only providing to the server
+    a unique identifier for each value.
+
+    These datatype functors come into three flavors:
+    - [Index()] for projectified datatypes,
+    - [Static()] for project independant datatypes,
+    - [Identified()] for projectified values already identified by integers.
+
+*)
 (* -------------------------------------------------------------------------- *)
 
-(** Simplified [Map.S] *)
+(** Simplified [Map.S]. *)
 module type Map =
 sig
   type 'a t
@@ -167,11 +300,12 @@ sig
   val find : key -> 'a t -> 'a
 end
 
+(** Datatype extended with access to value identifiers. *)
 module type Index =
 sig
   include S_collection
   val get : t -> int
-  val find : int -> t (** @raise Not_found if not registered *)
+  val find : int -> t (** @raise Not_found if not registered. *)
   val clear : unit -> unit
   (** Clear index tables. Use with extreme care. *)
 end
@@ -182,10 +316,7 @@ module Static(M : Map)(I : Info) : Index with type t = M.key
 (** Builds a {i projectified} index. *)
 module Index(M : Map)(I : Info) : Index with type t = M.key
 
-(* -------------------------------------------------------------------------- *)
-(** {2 Identified Types} *)
-(* -------------------------------------------------------------------------- *)
-
+(** Datatype already identified by unique integers. *)
 module type IdentifiedType =
 sig
   type t
@@ -193,33 +324,24 @@ sig
   include Info
 end
 
-(** Builds a {i projectified} index on types with {i unique} identifiers *)
+(** Builds a {i projectified} index on types with {i unique} identifiers. *)
 module Identified(A : IdentifiedType) : Index with type t = A.t
 
 (* -------------------------------------------------------------------------- *)
-(** {2 Dictionary} *)
-(* -------------------------------------------------------------------------- *)
-
-module type Enum =
-sig
-  type t
-  val values : (t * string * Markdown.text) list
-  include Info
-end
+(** {2 Error handling}
 
-module Dictionary(E : Enum) : S_collection with type t = E.t
-
-(* -------------------------------------------------------------------------- *)
-(** {2 Error handling} *)
+    These utilities shall be used when writing your own encoding and decoding
+    values to JSON format.
+*)
 (* -------------------------------------------------------------------------- *)
 
-(** Exception thrown during the decoding of a request's inputs *)
+(** Exception thrown during the decoding of a request's inputs. *)
 exception InputError of string
 
 val failure : ?json:json -> ('a, Format.formatter, unit, 'b) format4 -> 'a
-(** @raise InputError with provided message *)
+(** @raise InputError with provided message. *)
 
 val failure_from_type_error : string -> json -> 'a
-(** @raise InputError from Yojson.Basic.Util.Type_error arguments *)
+(** @raise InputError from [Yojson.Basic.Util.Type_error] arguments. *)
 
 (* -------------------------------------------------------------------------- *)
diff --git a/src/plugins/server/doc.ml b/src/plugins/server/doc.ml
index cab11780aec9205349d18ca0595ccef333d7fcd3..5da581570cc7a043548647c9c2d956e8a3674ab1 100644
--- a/src/plugins/server/doc.ml
+++ b/src/plugins/server/doc.ml
@@ -31,6 +31,12 @@ module Pages = Map.Make(String)
 
 type chapter = [ `Protocol | `Kernel | `Plugin of string ]
 
+(* Section contents can be generated statically or dynamically.
+   Typically, general kernel dictionary entries can be extended by plugins.
+   The general case is to have a function that builds the (final) content
+   on demand. *)
+type section = (unit -> Markdown.elements)
+
 type page = {
   path : string ;
   rootdir : string ; (* path to document root *)
@@ -38,7 +44,7 @@ type page = {
   title : string ;
   order : int ;
   intro : Markdown.elements ;
-  mutable sections : Markdown.elements list ;
+  mutable sections : section list ;
 }
 
 let order = ref 0
@@ -82,10 +88,15 @@ let page chapter ~title ~filename =
                  sections=[] } in
     pages := Pages.add path page !pages ; page
 
-let publish ~page ?name ?(index=[]) ~title content sections =
+let static () = []
+
+let publish ~page ?name ?(index=[]) ~title
+    ?(contents=[])
+    ?(generated=static)
+    () =
   let id = match name with Some id -> id | None -> title in
   let href = Section( page.path , id ) in
-  let section = Markdown.section ?name ~title (content @ sections) in
+  let section () = Markdown.section ?name ~title (contents @ generated ()) in
   List.iter (fun entry -> entries := (entry , href) :: !entries) index ;
   page.sections <- section :: page.sections ; href
 
@@ -203,12 +214,15 @@ let pp_one_page ~root ~page ~title body =
   with Sys_error e ->
     Senv.fatal "Could not open file %s for writing: %s" full_path e
 
+(* Build section contents in reverse order *)
+let build d s = List.fold_left (fun d s -> s() :: d) d s
+
 let dump ~root ?(meta=true) () =
   begin
     Pages.iter
       (fun path page ->
          Senv.feedback "[doc] Page: '%s'" path ;
-         let body = Markdown.subsections page.intro (List.rev page.sections) in
+         let body = Markdown.subsections page.intro (build [] page.sections) in
          let title = page.title in
          pp_one_page ~root ~page:path ~title body ;
          if meta then
diff --git a/src/plugins/server/doc.mli b/src/plugins/server/doc.mli
index e1b2c6414d3596de73f9c44b60e5b714486721a7..99bac4c4df90be03c3aea931c96c4a9b6c6d5a52 100644
--- a/src/plugins/server/doc.mli
+++ b/src/plugins/server/doc.mli
@@ -48,21 +48,20 @@ val page : chapter -> title:string -> filename:string -> page
 
 (** Adds a section in the corresponding page.
     Returns an href to the published section.
-    If index items are provided, they are added
-    to the server documentation index.
+    If index items are provided, they are added to the server documentation
+    index.
 *)
 val publish :
   page:page ->
   ?name:string ->
   ?index:string list ->
   title:string ->
-  Markdown.elements ->
-  Markdown.elements ->
-  Markdown.href
+  ?contents:Markdown.elements ->
+  ?generated:(unit -> Markdown.elements) ->
+  unit -> Markdown.href
 
-(** Dumps all published pages of documentations. Unless [~meta:false],
-    also generates METADATA for each page in
-    [<filename>.json] for each page. *)
+(** Dumps all published pages of documentations. Unless [~meta:false], also
+    generates METADATA for each page in [<filename>.json] for each page. *)
 val dump : root:string -> ?meta:bool -> unit -> unit
 
 (* -------------------------------------------------------------------------- *)
diff --git a/src/plugins/server/jbuffer.mli b/src/plugins/server/jbuffer.mli
index 9f9bdb1cdd39486376e7f6c5ae2604cee3cf87a4..9db8cfab7eaed970a0cba77b1af9e913d1f4c161 100644
--- a/src/plugins/server/jbuffer.mli
+++ b/src/plugins/server/jbuffer.mli
@@ -20,6 +20,8 @@
 (*                                                                        *)
 (**************************************************************************)
 
+(** Rich text buffers with JSON marshalling. *)
+
 type json = Json.t
 
 (** All-in-one formatter. Return the JSON encoding of formatted text. *)
@@ -41,7 +43,7 @@ val create : ?indent:int -> ?margin:int -> unit -> buffer
 (** The underlying formatter of a buffer. *)
 val formatter : buffer -> Format.formatter
 
-(** Prints into the buffer's formatter. *)
+(** Prints into the buffer formatter. *)
 val bprintf : buffer -> ('a,Format.formatter,unit) format -> 'a
 
 val append : buffer -> string -> int -> int -> unit
@@ -53,7 +55,7 @@ val pop_tag : buffer -> Transitioning.Format.stag -> unit
     tags. *)
 val contents : buffer -> json
 
-(** Prints back a JSON encoding onto the provided formatter.
+(** Prints back a JSON encoding into the provided formatter.
     @raise Yojson.Basic.Util.Type_error in case of ill formatted buffer. *)
 val fprintf : Format.formatter -> json -> unit
 
diff --git a/src/plugins/server/kernel_ast.ml b/src/plugins/server/kernel_ast.ml
index 915f9e124e7ba171f9cf6eb4701fde253175640f..21870f3dfc505003653c9a64285bd3f3733552ca 100644
--- a/src/plugins/server/kernel_ast.ml
+++ b/src/plugins/server/kernel_ast.ml
@@ -20,7 +20,6 @@
 (*                                                                        *)
 (**************************************************************************)
 
-
 open Data
 module Sy = Syntax
 module Md = Markdown
@@ -42,16 +41,22 @@ let () = Request.register ~page
 (* ---  Printers                                                          --- *)
 (* -------------------------------------------------------------------------- *)
 
-module Tag =
+module Marker =
 struct
 
   open Printer_tag
 
-  type index = (string,localizable) Hashtbl.t
+  type index = {
+    tags : string Localizable.Hashtbl.t ;
+    locs : (string,localizable) Hashtbl.t ;
+  }
 
   let kid = ref 0
 
-  let index () = Hashtbl.create 0
+  let index () = {
+    tags = Localizable.Hashtbl.create 0 ;
+    locs = Hashtbl.create 0 ;
+  }
 
   module TYPE : Datatype.S with type t = index =
     Datatype.Make
@@ -70,30 +75,41 @@ struct
         let default = index
       end)
 
-  let of_stmt s = Printf.sprintf "#s%d" s.sid
-  let of_start s = Printf.sprintf "#k%d" s.sid
-  let of_varinfo v = Printf.sprintf "#v%d" v.vid
-
   let create_tag = function
-    | PStmt(_,st) -> of_stmt st
-    | PStmtStart(_,st) -> of_start st
-    | PVDecl(_,_,vi) -> of_varinfo vi
+    | PStmt(_,s) -> Printf.sprintf "#s%d" s.sid
+    | PStmtStart(_,s) -> Printf.sprintf "#k%d" s.sid
+    | PVDecl(_,_,v) -> Printf.sprintf "#v%d" v.vid
     | PLval _ -> Printf.sprintf "#l%d" (incr kid ; !kid)
-    | PExp _ -> Printf.sprintf "#e%d" (incr kid ; !kid)
+    | PExp(_,_,e) -> Printf.sprintf "#e%d" e.eid
     | PTermLval _ -> Printf.sprintf "#t%d" (incr kid ; !kid)
     | PGlobal _ -> Printf.sprintf "#g%d" (incr kid ; !kid)
     | PIP _ -> Printf.sprintf "#p%d" (incr kid ; !kid)
 
-  let create item =
-    let tag = create_tag item in
-    let index = STATE.get () in
-    Hashtbl.add index tag item ; tag
+  let create loc =
+    let { tags ; locs } = STATE.get () in
+    try Localizable.Hashtbl.find tags loc
+    with Not_found ->
+      let tag = create_tag loc in
+      Localizable.Hashtbl.add tags loc tag ;
+      Hashtbl.add locs tag loc ;
+      tag
+
+  let lookup = Hashtbl.find (STATE.get()).locs
+
+  type t = localizable
+  let syntax = Sy.publish ~page:Data.page ~name:"marker"
+      ~synopsis:Sy.ident
+      ~descr:(Md.plain "Localizable AST marker \
+                        (function, globals, statements, properties, etc.)") ()
 
-  let lookup = Hashtbl.find (STATE.get())
+  let to_json loc = `String (create loc)
+  let of_json js =
+    try lookup (Js.to_string js)
+    with Not_found -> Data.failure "not a localizable marker"
 
 end
 
-module PP = Printer_tag.Make(Tag)
+module Printer = Printer_tag.Make(Marker)
 
 (* -------------------------------------------------------------------------- *)
 (* --- Ast Data                                                           --- *)
@@ -102,19 +118,17 @@ module PP = Printer_tag.Make(Tag)
 module Stmt = Data.Collection
     (struct
       type t = stmt
-      let syntax = Sy.publish ~page ~name:"stmt"
+      let syntax = Sy.publish ~page:Data.page ~name:"stmt"
           ~synopsis:Sy.ident
           ~descr:(Md.plain "Code statement identifier") ()
-      let to_json st = `String (Tag.of_stmt st)
+      let to_json st =
+        let kf = Kernel_function.find_englobing_kf st in
+        Marker.to_json (PStmt(kf,st))
       let of_json js =
-        let id = Js.to_string js in
-        try
-          let open Printer_tag in
-          match Tag.lookup id with
-          | PStmt(_,st) -> st
-          | _ -> raise Not_found
-        with Not_found ->
-          Data.failure "Unknown stmt id: '%s'" id
+        let open Printer_tag in
+        match Marker.of_json js with
+        | PStmt(_,st) | PStmtStart(_,st) -> st
+        | _ -> Data.failure "not a stmt marker"
     end)
 
 module Ki = Data.Collection
@@ -123,7 +137,7 @@ module Ki = Data.Collection
       let syntax = Sy.union [ Sy.tag "global" ; Stmt.syntax ]
       let to_json = function
         | Kglobal -> `String "global"
-        | Kstmt st -> `String (Tag.of_stmt st)
+        | Kstmt st -> Stmt.to_json st
       let of_json = function
         | `String "global" -> Kglobal
         | js -> Kstmt (Stmt.of_json js)
@@ -132,7 +146,7 @@ module Ki = Data.Collection
 module Kf = Data.Collection
     (struct
       type t = kernel_function
-      let syntax = Sy.publish ~page ~name:"fct-id"
+      let syntax = Sy.publish ~page:Data.page ~name:"fct-id"
           ~synopsis:Sy.ident
           ~descr:(Md.plain "Function identified by its global name.") ()
       let to_json kf =
@@ -161,59 +175,7 @@ let () = Request.register ~page
     ~kind:`GET ~name:"kernel.ast.printFunction"
     ~descr:(Md.plain "Print the AST of a function")
     ~input:(module Kf) ~output:(module Jtext)
-    (fun kf -> Jbuffer.to_json PP.pp_global (Kernel_function.get_global kf))
-
-(* -------------------------------------------------------------------------- *)
-(* --- Properties                                                         --- *)
-(* -------------------------------------------------------------------------- *)
-
-module Property = struct
-  type p
-  let signature =
-    Record.signature ~page ~name:"property"
-      ~descr:(Md.plain "logical property") ()
-
-  let name = Record.field signature ~name:"name"
-      ~descr:(Md.plain "name") (module Jstring)
-  let property = Record.field signature ~name:"property"
-      ~descr:(Md.plain "logical property") (module Jstring)
-  let status = Record.field signature ~name:"status"
-      ~descr:(Md.plain "logical status") (module Jstring)
-  let file = Record.field signature ~name:"file"
-      ~descr:(Md.plain "file") (module Jstring)
-  let kf = Record.field signature ~name:"function"
-      ~descr:(Md.plain "kernel function") (module Kf.Joption)
-  let kinstr = Record.field signature ~name:"kinstr"
-      ~descr:(Md.plain "kinstr") (module Ki)
-
-  module R = (val (Record.publish signature) : Record.S with type r = p)
-  include R
-
-  let make ip =
-    let st = Property_status.Feedback.get ip in
-    let st = Format.asprintf "%a" Property_status.Feedback.pretty st in
-    let p = Format.asprintf "%a" Property.pretty ip in
-    let loc = Property.location ip in
-    let path = Filepath.(Normalized.to_pretty_string (fst loc).pos_path) in
-    default |> set property p |> set status st
-    |> set kf (Property.get_kf ip)
-    |> set kinstr (Property.get_kinstr ip)
-    |> set name (Property.Names.get_prop_name_id ip)
-    |> set file path
-end
-
-let get_properties () =
-  Property_status.fold (fun ip acc -> Property.make ip :: acc) []
-
-let () =
-  Request.register
-    ~page
-    ~kind:`GET
-    ~name:"kernel.ast.getProperties"
-    ~descr:(Md.plain "Collect all logical properties")
-    ~input:(module Junit)
-    ~output:(module Jlist (Property))
-    get_properties
+    (fun kf -> Jbuffer.to_json Printer.pp_global (Kernel_function.get_global kf))
 
 (* -------------------------------------------------------------------------- *)
 (* --- Files                                                              --- *)
diff --git a/src/plugins/server/kernel_ast.mli b/src/plugins/server/kernel_ast.mli
index 92ab7db460405f612a659e8b6fca3c8ceb73ad40..f683fabe5edc9c46c2a445391f9e30f362534d54 100644
--- a/src/plugins/server/kernel_ast.mli
+++ b/src/plugins/server/kernel_ast.mli
@@ -20,15 +20,27 @@
 (*                                                                        *)
 (**************************************************************************)
 
-open Cil_types
-
 (* -------------------------------------------------------------------------- *)
 (** Ast Data *)
 (* -------------------------------------------------------------------------- *)
 
-module PP : Printer_tag.S_pp
+open Cil_types
+
 module Kf : Data.S_collection with type t = kernel_function
 module Ki : Data.S_collection with type t = kinstr
 module Stmt : Data.S_collection with type t = stmt
 
+module Marker :
+sig
+  include Data.S with type t = Printer_tag.localizable
+  val create : t -> string (** Memoized unique identifier. *)
+  val lookup : string -> t (** Get back the localizable, if any. *)
+end
+
+(* -------------------------------------------------------------------------- *)
+(** Ast Printer *)
+(* -------------------------------------------------------------------------- *)
+
+module Printer : Printer_tag.S_pp
+
 (* -------------------------------------------------------------------------- *)
diff --git a/src/plugins/server/kernel_main.ml b/src/plugins/server/kernel_main.ml
index 22866e37d795837bb857aaac923f27cea03de156..67ec006f68be6abff406e2b5ca6ed9700e9a7d06 100644
--- a/src/plugins/server/kernel_main.ml
+++ b/src/plugins/server/kernel_main.ml
@@ -78,104 +78,114 @@ let () =
 (* --- File Positions                                                     --- *)
 (* -------------------------------------------------------------------------- *)
 
-module RawSource =
-struct
-  type t = Filepath.position
-
-  let syntax = Sy.publish ~page ~name:"source"
-      ~synopsis:(Sy.record [ "file" , Sy.string ; "line" , Sy.int ])
-      ~descr:(Md.plain "Source file positions.")
-      ~details:Md.([Block [Text (plain "The file path is normalized, \
-                                        and the line number starts at one.")]])
-      ()
+module LogSource = Collection
+    (struct
+      type t = Filepath.position
 
-  let to_json p = `Assoc [
-      "file" , `String (p.Filepath.pos_path :> string) ;
-      "line" , `Int p.Filepath.pos_lnum ;
-    ]
+      let syntax = Sy.publish ~page:Data.page ~name:"source"
+          ~synopsis:(Sy.record [ "file" , Sy.string ; "line" , Sy.int ])
+          ~descr:(Md.plain "Source file positions.")
+          ~details:Md.([Block [Text (plain "The file path is normalized, \
+                                            and the line number starts at one.")]])
+          ()
 
-  let of_json = function
-    | `Assoc [ "file" , `String path ; "line" , `Int line ]
-    | `Assoc [ "line" , `Int line ; "file" , `String path ]
-      -> Log.source ~file:(Filepath.Normalized.of_string path) ~line
-    | js -> failure_from_type_error "Invalid source format" js
+      let to_json p = `Assoc [
+          "file" , `String (p.Filepath.pos_path :> string) ;
+          "line" , `Int p.Filepath.pos_lnum ;
+        ]
 
-end
+      let of_json = function
+        | `Assoc [ "file" , `String path ; "line" , `Int line ]
+        | `Assoc [ "line" , `Int line ; "file" , `String path ]
+          -> Log.source ~file:(Filepath.Normalized.of_string path) ~line
+        | js -> failure_from_type_error "Invalid source format" js
 
-module LogSource = Collection(RawSource)
+    end)
 
 (* -------------------------------------------------------------------------- *)
 (* --- Log Lind                                                           --- *)
 (* -------------------------------------------------------------------------- *)
 
-module RawKind =
-struct
-  type t = Log.kind
-  let page = page
-  let name = "kind"
-  let descr = Md.plain "Frama-C message category."
-  let values = [
-    Log.Error,    "ERROR",    Md.plain "User Error" ;
-    Log.Warning,  "WARNING",  Md.plain "User Warning" ;
-    Log.Feedback, "FEEDBACK", Md.plain "Analyzer Feedback" ;
-    Log.Result,   "RESULT",   Md.plain "Analyzer Result" ;
-    Log.Failure,  "FAILURE",  Md.plain "Analyzer Failure" ;
-    Log.Debug,    "DEBUG",    Md.plain "Analyser Debug" ;
-  ]
-end
+module LogKind = Collection
+    (struct
+
+      let kinds = Enum.dictionary ~page
+          ~name:"logkind" ~title:"Log Kind"
+          ~descr:(Md.plain "Frama-C message category.")
+          ()
+
+      let t_kind value name descr =
+        Enum.tag kinds ~name ~descr:(Md.plain descr) ~value ()
+
+      let t_error = t_kind Log.Error "ERROR" "User Error"
+      let t_warning = t_kind Log.Warning "WARNING" "User Warning"
+      let t_feedback = t_kind Log.Feedback "FEEDBACK" "Plugin Feedback"
+      let t_result = t_kind Log.Result "RESULT" "Plugin Result"
+      let t_failure = t_kind Log.Failure "FAILURE" "Plugin Failure"
+      let t_debug = t_kind Log.Debug "DEBUG" "Analyser Debug"
+
+      let tag = function
+        | Log.Error -> t_error
+        | Log.Warning -> t_warning
+        | Log.Feedback -> t_feedback
+        | Log.Result -> t_result
+        | Log.Failure -> t_failure
+        | Log.Debug -> t_debug
+
+      let data = Enum.publish kinds ~tag ()
+      let () = Request.dictionary kinds
 
-module LogKind = Dictionary(RawKind)
+      include (val data : S with type t = Log.kind)
+    end)
 
 (* -------------------------------------------------------------------------- *)
 (* --- Log Events                                                         --- *)
 (* -------------------------------------------------------------------------- *)
 
-module RawEvent =
-struct
-
-  type rlog
-
-  let jlog : rlog signature = Record.signature ~page
-      ~name:"log" ~descr:(Md.plain "Message event record.") ()
-
-  let kind = Record.field jlog ~name:"kind"
-      ~descr:(Md.plain "Message kind") (module LogKind)
-  let plugin = Record.field jlog ~name:"plugin"
-      ~descr:(Md.plain "Emitter plugin") (module Jstring)
-  let message = Record.field jlog ~name:"message"
-      ~descr:(Md.plain "Message text") (module Jstring)
-  let category = Record.option jlog ~name:"category"
-      ~descr:(Md.plain "Message category (DEBUG or WARNING)") (module Jstring)
-  let source = Record.option jlog ~name:"source"
-      ~descr:(Md.plain "Source file position") (module LogSource)
-
-  module R = (val (Record.publish jlog) : Record.S with type r = rlog)
-
-  type t = Log.event
-  let syntax = R.syntax
-
-  let to_json evt =
-    R.default |>
-    R.set plugin evt.Log.evt_plugin |>
-    R.set kind evt.Log.evt_kind |>
-    R.set category evt.Log.evt_category |>
-    R.set source evt.Log.evt_source |>
-    R.set message evt.Log.evt_message |>
-    R.to_json
-
-  let of_json js =
-    let r = R.of_json js in
-    {
-      Log.evt_plugin = R.get plugin r ;
-      Log.evt_kind = R.get kind r ;
-      Log.evt_category = R.get category r ;
-      Log.evt_source = R.get source r ;
-      Log.evt_message = R.get message r ;
-    }
-
-end
-
-module LogEvent = Collection(RawEvent)
+module LogEvent = Collection
+    (struct
+
+      type rlog
+
+      let jlog : rlog Record.signature = Record.signature ~page
+          ~name:"log" ~descr:(Md.plain "Message event record.") ()
+
+      let kind = Record.field jlog ~name:"kind"
+          ~descr:(Md.plain "Message kind") (module LogKind)
+      let plugin = Record.field jlog ~name:"plugin"
+          ~descr:(Md.plain "Emitter plugin") (module Jstring)
+      let message = Record.field jlog ~name:"message"
+          ~descr:(Md.plain "Message text") (module Jstring)
+      let category = Record.option jlog ~name:"category"
+          ~descr:(Md.plain "Message category (DEBUG or WARNING)") (module Jstring)
+      let source = Record.option jlog ~name:"source"
+          ~descr:(Md.plain "Source file position") (module LogSource)
+
+      module R = (val (Record.publish jlog) : Record.S with type r = rlog)
+
+      type t = Log.event
+      let syntax = R.syntax
+
+      let to_json evt =
+        R.default |>
+        R.set plugin evt.Log.evt_plugin |>
+        R.set kind evt.Log.evt_kind |>
+        R.set category evt.Log.evt_category |>
+        R.set source evt.Log.evt_source |>
+        R.set message evt.Log.evt_message |>
+        R.to_json
+
+      let of_json js =
+        let r = R.of_json js in
+        {
+          Log.evt_plugin = R.get plugin r ;
+          Log.evt_kind = R.get kind r ;
+          Log.evt_category = R.get category r ;
+          Log.evt_source = R.get source r ;
+          Log.evt_message = R.get message r ;
+        }
+
+    end)
 
 (* -------------------------------------------------------------------------- *)
 (* --- Log Monitoring                                                     --- *)
diff --git a/src/plugins/server/kernel_properties.ml b/src/plugins/server/kernel_properties.ml
new file mode 100644
index 0000000000000000000000000000000000000000..72812b886a732d18efa1c6ec63143e5d97539f66
--- /dev/null
+++ b/src/plugins/server/kernel_properties.ml
@@ -0,0 +1,261 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  This file is part of Frama-C.                                         *)
+(*                                                                        *)
+(*  Copyright (C) 2007-2020                                               *)
+(*    CEA (Commissariat à l'énergie atomique et aux énergies              *)
+(*         alternatives)                                                  *)
+(*                                                                        *)
+(*  you can redistribute it and/or modify it under the terms of the GNU   *)
+(*  Lesser General Public License as published by the Free Software       *)
+(*  Foundation, version 2.1.                                              *)
+(*                                                                        *)
+(*  It is distributed in the hope that it will be useful,                 *)
+(*  but WITHOUT ANY WARRANTY; without even the implied warranty of        *)
+(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *)
+(*  GNU Lesser General Public License for more details.                   *)
+(*                                                                        *)
+(*  See the GNU Lesser General Public License version 2.1                 *)
+(*  for more details (enclosed in the file licenses/LGPLv2.1).            *)
+(*                                                                        *)
+(**************************************************************************)
+
+module Md = Markdown
+
+open Data
+open Kernel_main
+open Kernel_ast
+
+let page = Doc.page `Kernel ~title:"Property Services" ~filename:"properties.md"
+
+(* -------------------------------------------------------------------------- *)
+(* --- Property Kind                                                      --- *)
+(* -------------------------------------------------------------------------- *)
+
+module PropKind =
+struct
+  let kinds = Enum.dictionary ~page
+      ~name:"propkind" ~title:"Kind"
+      ~descr:(Md.plain "Property Kind")
+      ()
+
+  let t_kind name descr = Enum.tag kinds ~name ~descr:(Md.plain descr) ()
+  let t_clause name = t_kind name (Printf.sprintf "Clause `@%s`" name)
+  let t_loop name =
+    t_kind ("loop-" ^ name) (Printf.sprintf "Clause `@loop %s`" name)
+
+  let t_behavior = t_kind "behavior" "Contract behavior"
+  let t_complete = t_kind "complete" "Complete behaviors clause"
+  let t_disjoint = t_kind "disjoint" "Disjoint behaviors clause"
+
+  let t_assumes = t_clause "assumes"
+  let t_requires = t_clause "requires"
+  let t_breaks = t_clause "breaks"
+  let t_continues = t_clause "continues"
+  let t_returns = t_clause "returns"
+  let t_exits = t_clause "exits"
+  let t_ensures = t_clause "ensures"
+  let t_terminates = t_clause "terminates"
+  let t_allocates = t_clause "allocates"
+  let t_decreases = t_clause "decreases"
+  let t_assigns = t_clause "assigns"
+  let t_froms = t_kind "froms" "Clause `@assigns … \\from …`"
+
+  let t_assert = t_clause "assert"
+  let t_loop_invariant = t_loop "invariant"
+  let t_loop_assigns = t_loop "assigns"
+  let t_loop_variant = t_loop "variant"
+  let t_loop_allocates = t_loop "allocates"
+  let t_loop_pragma = t_loop "pragma"
+
+  let t_reachable = t_kind "reachable" "Reachable statement"
+  let t_code_contract = t_kind "code-contract" "Statement Contract"
+  let t_code_invariant = t_kind "code-invariant" "Generalized loop invariant"
+  let t_type_invariant = t_kind "type-invariant" "Type invariant"
+  let t_global_invariant = t_kind "global-invariant" "Global invariant"
+
+  let t_axiomatic = t_kind "axiomatic" "Axiomatic definitions"
+  let t_axiom = t_kind "axiom" "Logical axiom"
+  let t_lemma = t_kind "lemma" "Logical lemma"
+
+  let p_ext = Enum.prefix kinds ~prefix:"ext" ~var:"<clause>"
+      ~descr:(Md.plain "ACSL extension `<clause>`") ()
+
+  let p_loop_ext = Enum.prefix kinds ~prefix:"loop-ext" ~var:"<clause>"
+      ~descr:(Md.plain "ACSL loop extension `loop <clause>`") ()
+
+  let p_other = Enum.prefix kinds ~prefix:"prop" ~var:"<prop>"
+      ~descr:(Md.plain "Plugin Specific properties") ()
+
+  open Property
+
+  let rec tag = function
+    | IPPredicate { ip_kind } ->
+      begin match ip_kind with
+        | PKRequires _ -> t_requires
+        | PKAssumes _ -> t_assumes
+        | PKEnsures(_,Normal) -> t_ensures
+        | PKEnsures(_,Exits) -> t_exits
+        | PKEnsures(_,Breaks) -> t_breaks
+        | PKEnsures(_,Continues) -> t_continues
+        | PKEnsures(_,Returns) -> t_returns
+        | PKTerminates -> t_terminates
+      end
+    | IPExtended { ie_ext={ ext_name } } -> Enum.instance p_ext ext_name
+    | IPAxiomatic _ -> t_axiomatic
+    | IPAxiom _ -> t_axiom
+    | IPLemma _ -> t_lemma
+    | IPBehavior _ -> t_behavior
+    | IPComplete _ -> t_complete
+    | IPDisjoint _ -> t_disjoint
+    | IPCodeAnnot { ica_ca={ annot_content } } ->
+      begin match annot_content with
+        | AAssert _ -> t_assert
+        | AStmtSpec _ -> t_code_contract
+        | AInvariant(_,false,_) -> t_code_invariant
+        | AInvariant(_,true,_) -> t_loop_invariant
+        | AVariant _ -> t_loop_variant
+        | AAssigns _ -> t_loop_assigns
+        | AAllocation _ -> t_loop_allocates
+        | APragma _ -> t_loop_pragma
+        | AExtended(_,_,{ext_name}) -> Enum.instance p_loop_ext ext_name
+      end
+    | IPAllocation _ -> t_allocates
+    | IPAssigns _ -> t_assigns
+    | IPFrom _ -> t_froms
+    | IPDecrease _ -> t_decreases
+    | IPReachable _ -> t_reachable
+    | IPPropertyInstance { ii_ip } -> tag ii_ip
+    | IPTypeInvariant _ -> t_type_invariant
+    | IPGlobalInvariant _ -> t_global_invariant
+    | IPOther { io_name } -> Enum.instance p_other io_name
+
+  let data = Enum.publish kinds ~tag ()
+  let () = Request.dictionary kinds
+
+  include (val data : S with type t = Property.t)
+end
+
+let register_propkind ~name ~kind ?label ~descr () =
+  let open PropKind in
+  let prefix = match kind with
+    | `Clause -> p_ext
+    | `Loop -> p_loop_ext
+    | `Other -> p_other
+  in ignore @@ Enum.extends kinds prefix ~name ?label ~descr ()
+
+(* -------------------------------------------------------------------------- *)
+(* --- Property Status                                                    --- *)
+(* -------------------------------------------------------------------------- *)
+
+module PropStatus =
+struct
+
+  let status = Enum.dictionary ~page
+      ~name:"propstatus" ~title:"Status"
+      ~descr:(Md.plain "Property Status (consolidated)") ()
+
+  let t_status value name ?label descr =
+    Enum.tag status ~name
+      ?label:(Extlib.opt_map Md.plain label)
+      ~descr:(Md.plain descr) ~value ()
+
+  open Property_status.Feedback
+
+  let t_unknown =
+    t_status Unknown "unknown" "Unknown status"
+  let t_never_tried =
+    t_status Never_tried "never_tried"
+      ~label:"Never tried" "Unknown status (never tried)"
+  let t_inconsistent =
+    t_status Inconsistent "inconsistent" "Inconsistent status"
+  let t_valid =
+    t_status Valid "valid" "Valid property"
+  let t_valid_under_hyp =
+    t_status Valid_under_hyp "valid_under_hyp"
+      ~label:"Valid (?)" "Valid (under hypotheses)"
+  let t_considered_valid =
+    t_status Considered_valid "considered_valid"
+      ~label:"Valid (!)" "Valid (external assumption)"
+  let t_invalid =
+    t_status Invalid "invalid" "Invalid property (counter example found)"
+  let t_invalid_under_hyp =
+    t_status Invalid_under_hyp "invalid_under_hyp"
+      ~label:"Invalid (?)" "Invalid property (under hypotheses)"
+  let t_invalid_but_dead =
+    t_status Invalid_but_dead "invalid_but_dead"
+      ~label:"Invalid (✝)" "Dead property (but invalid)"
+  let t_valid_but_dead =
+    t_status Valid_but_dead "valid_but_dead"
+      ~label:"Valid (✝)" "Dead property (but valid)"
+  let t_unknown_but_dead =
+    t_status Unknown_but_dead "unknown_but_dead"
+      ~label:"Unknown (✝)" "Dead property (but unknown)"
+
+  let tag = function
+    | Valid -> t_valid
+    | Invalid -> t_invalid
+    | Unknown -> t_unknown
+    | Never_tried -> t_never_tried
+    | Valid_under_hyp -> t_valid_under_hyp
+    | Valid_but_dead -> t_valid_but_dead
+    | Considered_valid -> t_considered_valid
+    | Invalid_under_hyp -> t_invalid_under_hyp
+    | Invalid_but_dead -> t_invalid_but_dead
+    | Unknown_but_dead -> t_unknown_but_dead
+    | Inconsistent -> t_inconsistent
+
+  let data = Enum.publish status ~tag ()
+  let () = Request.dictionary status
+
+  include (val data : S with type t = Property_status.Feedback.t)
+end
+
+(* -------------------------------------------------------------------------- *)
+(* --- Property Model                                                     --- *)
+(* -------------------------------------------------------------------------- *)
+
+let model = States.model ()
+
+let () = States.column ~model ~name:"descr"
+    ~descr:(Md.plain "Description")
+    ~data:(module Jstring)
+    ~get:(fun ip -> Format.asprintf "%a" Property.pretty ip) ()
+
+let () = States.column ~model ~name:"kind"
+    ~descr:(Md.plain "Kind")
+    ~data:(module PropKind)
+    ~get:(fun ip -> ip) ()
+
+let () = States.column ~model ~name:"status"
+    ~descr:(Md.plain "Status")
+    ~data:(module PropStatus)
+    ~get:(Property_status.Feedback.get) ()
+
+let () = States.column ~model ~name:"function"
+    ~descr:(Md.plain "Function")
+    ~data:(module Kf.Joption) ~get:Property.get_kf ()
+
+let () = States.column ~model ~name:"kinstr"
+    ~descr:(Md.plain "Instruction")
+    ~data:(module Ki) ~get:Property.get_kinstr ()
+
+let () = States.column ~model ~name:"source"
+    ~descr:(Md.plain "Position")
+    ~data:(module LogSource)
+    ~get:(fun ip -> Property.location ip |> fst) ()
+
+let array =
+  States.register_array
+    ~page
+    ~name:"kernel.properties"
+    ~descr:(Md.plain "Registered Properties")
+    ~key:(fun ip -> Kernel_ast.Marker.create (PIP ip))
+    ~iter:(Property_status.iter)
+    ~add_update_hook:Property_status.register_property_add_hook
+    ~add_remove_hook:Property_status.register_property_remove_hook
+    model
+
+let reload () = States.reload array
+
+(* -------------------------------------------------------------------------- *)
diff --git a/src/plugins/server/kernel_properties.mli b/src/plugins/server/kernel_properties.mli
new file mode 100644
index 0000000000000000000000000000000000000000..8ecc7d4e9017265141dc77c57f2c338ebbe2c968
--- /dev/null
+++ b/src/plugins/server/kernel_properties.mli
@@ -0,0 +1,36 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  This file is part of Frama-C.                                         *)
+(*                                                                        *)
+(*  Copyright (C) 2007-2020                                               *)
+(*    CEA (Commissariat à l'énergie atomique et aux énergies              *)
+(*         alternatives)                                                  *)
+(*                                                                        *)
+(*  you can redistribute it and/or modify it under the terms of the GNU   *)
+(*  Lesser General Public License as published by the Free Software       *)
+(*  Foundation, version 2.1.                                              *)
+(*                                                                        *)
+(*  It is distributed in the hope that it will be useful,                 *)
+(*  but WITHOUT ANY WARRANTY; without even the implied warranty of        *)
+(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *)
+(*  GNU Lesser General Public License for more details.                   *)
+(*                                                                        *)
+(*  See the GNU Lesser General Public License version 2.1                 *)
+(*  for more details (enclosed in the file licenses/LGPLv2.1).            *)
+(*                                                                        *)
+(**************************************************************************)
+
+(** Kernel Property Status *)
+
+(** Documentation of ACSL extensions for [propkind] server data. *)
+val register_propkind :
+  name:string ->
+  kind:[`Clause | `Loop | `Other] ->
+  ?label:Markdown.text ->
+  descr:Markdown.text ->
+  unit -> unit
+
+(** Trigger a full reload for the table of property status. *)
+val reload : unit -> unit
+
+(* -------------------------------------------------------------------------- *)
diff --git a/src/plugins/server/main.ml b/src/plugins/server/main.ml
index 74f61571346d82bc6927142bc7ed3c87723070ae..7a82d0176eea15c6e5071ae1845962ed633c98ee 100644
--- a/src/plugins/server/main.ml
+++ b/src/plugins/server/main.ml
@@ -59,6 +59,8 @@ type 'a request = [
   | `Poll
   | `Request of 'a * string * json
   | `Kill of 'a
+  | `SigOn of string
+  | `SigOff of string
   | `Shutdown
 ]
 
@@ -66,6 +68,7 @@ type 'a response = [
   | `Data of 'a * json
   | `Error of 'a * string
   | `Killed of 'a
+  | `Signal of string
   | `Rejected of 'a
 ]
 
@@ -85,16 +88,21 @@ type 'a process = {
   mutable killed : bool ;
 }
 
+module Sigs = Set.Make(String)
+
+(* Server with request identifier (RqId) of type ['a] *)
 type 'a server = {
-  polling : int ;
-  pretty : Format.formatter -> 'a -> unit ;
-  equal : 'a -> 'a -> bool ;
-  fetch : unit -> 'a message option ;
-  q_in : 'a process Queue.t ;
-  q_out : 'a response Stack.t ;
-  mutable daemon : Db.daemon option ;
-  mutable shutdown : bool ;
-  mutable running : 'a process option ;
+  pretty : Format.formatter -> 'a -> unit ; (* RqId printer *)
+  equal : 'a -> 'a -> bool ; (* RqId equality *)
+  polling : int ; (* server polling, in milliseconds *)
+  fetch : unit -> 'a message option ; (* fetch some client message *)
+  q_in : 'a process Queue.t ; (* queue of pending jobs *)
+  q_out : 'a response Queue.t ; (* queue of pending responses *)
+  mutable daemon : Db.daemon option ; (* Db.yield daemon *)
+  mutable s_active : Sigs.t ; (* signals the client is listening to *)
+  mutable s_signal : Sigs.t ; (* emitted signals since last synchro *)
+  mutable shutdown : bool ; (* server has been asked to shut down *)
+  mutable running : 'a process option ; (* currently running EXEC request *)
 }
 
 exception Killed
@@ -107,13 +115,15 @@ let pp_request pp fmt (r : _ request) =
   match r with
   | `Poll -> Format.fprintf fmt "Poll"
   | `Shutdown -> Format.fprintf fmt "Shutdown"
-  | `Kill id -> Format.fprintf fmt "Kill %a" pp id
+  | `SigOn sg -> Format.fprintf fmt "Signal %S : on" sg
+  | `SigOff sg -> Format.fprintf fmt "Signal %S : off" sg
+  | `Kill id -> Format.fprintf fmt "Kill [%a]" pp id
   | `Request(id,request,data) ->
     if Senv.debug_atleast 2 then
-      Format.fprintf fmt "@[<hov 2>Request %s:%a@ %a@]"
-        request pp id Data.pretty data
+      Format.fprintf fmt "@[<hov 2>Request [%a] %s@ %a@]"
+        pp id request Data.pretty data
     else
-      Format.fprintf fmt "Request %s:%a" request pp id
+      Format.fprintf fmt "Request [%a] %s" pp id request
 
 let pp_process pp fmt (p : _ process) =
   Format.fprintf fmt "Execute %s:%a" p.request pp p.id
@@ -123,12 +133,9 @@ let pp_response pp fmt (r : _ response) =
   | `Error(id,err) -> Format.fprintf fmt "Error %a: %s" pp id err
   | `Rejected id -> Format.fprintf fmt "Rejected %a" pp id
   | `Killed id -> Format.fprintf fmt "Killed %a" pp id
+  | `Signal sg -> Format.fprintf fmt "Signal %S" sg
   | `Data(id,data) ->
-    if Senv.debug_atleast 2 then
-      Format.fprintf fmt "@[<hov 2>Response %a@ %a@]"
-        pp id Data.pretty data
-    else
-      Format.fprintf fmt "Response %a" pp id
+    Format.fprintf fmt "@[<hov 2>Replies [%a]@ %a@]" pp id Data.pretty data
 
 (* -------------------------------------------------------------------------- *)
 (* --- Request Handling                                                   --- *)
@@ -153,7 +160,7 @@ let delayed process =
   else None
 
 let execute server ?yield proc =
-  Senv.debug "%a" (pp_process server.pretty) proc ;
+  Senv.debug ~level:2 "%a" (pp_process server.pretty) proc ;
   let resp = match yield with
     | Some yield when proc.yield ->
       Db.with_progress
@@ -162,8 +169,35 @@ let execute server ?yield proc =
         yield run proc
     | _ -> run proc
   in
-  Senv.debug "%a" (pp_response server.pretty) resp ;
-  Stack.push resp server.q_out
+  Senv.debug ~level:2 "%a" (pp_response server.pretty) resp ;
+  Queue.push resp server.q_out
+
+(* -------------------------------------------------------------------------- *)
+(* --- Signals                                                            --- *)
+(* -------------------------------------------------------------------------- *)
+
+type signal = string
+let signals = Hashtbl.create 32
+let signal s =
+  if Hashtbl.mem signals s then
+    ( Server_parameters.failure "Signal '%s' already registered" s ; "" )
+  else
+    ( Hashtbl.add signals s [] ; s )
+
+let () = Hashtbl.add signals "" []
+
+let on_signal s callback =
+  let ds = Hashtbl.find signals s in
+  Hashtbl.replace signals s (callback :: ds)
+
+let notify s a =
+  match Hashtbl.find signals s with
+  | ds -> List.iter (fun f -> f a) ds
+  | exception Not_found -> ()
+
+let nop _s = ()
+let emitter = ref nop
+let emit s = !emitter s
 
 (* -------------------------------------------------------------------------- *)
 (* --- Processing Requests                                                --- *)
@@ -182,9 +216,19 @@ let process_request (server : 'a server) (request : 'a request) : unit =
     begin
       Extlib.may kill_exec server.running ;
       Queue.clear server.q_in ;
-      Stack.clear server.q_out ;
+      Queue.clear server.q_out ;
       server.shutdown <- true ;
     end
+  | `SigOn sg ->
+    begin
+      server.s_active <- Sigs.add sg server.s_active ;
+      notify sg true ;
+    end
+  | `SigOff sg ->
+    begin
+      server.s_active <- Sigs.remove sg server.s_active ;
+      notify sg false ;
+    end
   | `Kill id ->
     begin
       let set_killed = kill_request server.equal id in
@@ -196,7 +240,7 @@ let process_request (server : 'a server) (request : 'a request) : unit =
       match find request with
       | None ->
         Senv.debug "Rejected %a" server.pretty id ;
-        Stack.push (`Rejected id) server.q_out
+        Queue.push (`Rejected id) server.q_out
       | Some( `GET , handler ) ->
         let proc = { id ; request ; handler ; data ;
                      yield = false ; killed = false } in
@@ -223,28 +267,41 @@ let communicate server =
       try List.iter (process_request server) message.requests ; None
       with exn -> Some exn in (* re-raised after message reply *)
     let pool = ref [] in
-    Stack.iter (fun r -> pool := r :: !pool) server.q_out ;
-    Stack.clear server.q_out ;
+    Queue.iter (fun r -> pool := r :: !pool) server.q_out ;
+    Queue.clear server.q_out ;
+    server.s_active <- Sigs.empty ;
     message.callback !pool ;
     Extlib.may raise error ; true
 
 (* -------------------------------------------------------------------------- *)
-(* --- Yielding                                                           --- *)
+(* --- Yielding & Signaling                                               --- *)
 (* -------------------------------------------------------------------------- *)
 
 let do_yield server () =
   Extlib.may raise_if_killed server.running ;
   ignore ( communicate server )
 
+let do_signal server s =
+  if Sigs.mem s server.s_active && not (Sigs.mem s server.s_signal) then
+    begin
+      server.s_signal <- Sigs.add s server.s_signal ;
+      Queue.push (`Signal s) server.q_out ;
+    end
+
 (* -------------------------------------------------------------------------- *)
 (* --- One Step Process                                                   --- *)
 (* -------------------------------------------------------------------------- *)
 
-let process server =
-  if Queue.is_empty server.q_in then
-    communicate server
+let rec fetch_exec q =
+  if Queue.is_empty q then None
   else
-    let proc = Queue.pop server.q_in in
+    let e = Queue.pop q in
+    if e.killed then fetch_exec q else Some e
+
+let process server =
+  match fetch_exec server.q_in with
+  | None -> communicate server
+  | Some proc ->
     server.running <- Some proc ;
     try
       execute server ~yield:(do_yield server) proc ;
@@ -264,7 +321,7 @@ let kill () = raise Killed
 
 let daemons = ref []
 let on callback = daemons := !daemons @ [ callback ]
-let signal activity =
+let set_active activity =
   List.iter (fun f -> try f activity with _ -> ()) !daemons
 
 let create ~pretty ?(equal=(=)) ~fetch () =
@@ -272,7 +329,9 @@ let create ~pretty ?(equal=(=)) ~fetch () =
   {
     fetch ; polling ; equal ; pretty ;
     q_in = Queue.create () ;
-    q_out = Stack.create () ;
+    q_out = Queue.create () ;
+    s_active = Sigs.empty ;
+    s_signal = Sigs.empty ;
     daemon = None ;
     running = None ;
     shutdown = false ;
@@ -283,6 +342,7 @@ let create ~pretty ?(equal=(=)) ~fetch () =
 (* -------------------------------------------------------------------------- *)
 
 let start server =
+  emitter := do_signal server ;
   match server.daemon with
   | Some _ -> ()
   | None ->
@@ -295,10 +355,11 @@ let start server =
           (do_yield server)
       in
       server.daemon <- Some daemon ;
-      signal true ;
+      set_active true ;
     end
 
 let stop server =
+  emitter := nop ;
   match server.daemon with
   | None -> ()
   | Some daemon ->
@@ -306,10 +367,11 @@ let stop server =
       Senv.feedback "Server disabled." ;
       server.daemon <- None ;
       Db.off_progress daemon ;
-      signal false ;
+      set_active false ;
     end
 
 let foreground server =
+  emitter := do_signal server ;
   match server.daemon with
   | None -> ()
   | Some daemon ->
@@ -327,9 +389,9 @@ let run server =
     ( (* TODO: catch-break to be removed once Why3 signal handler is fixed *)
       Sys.catch_break true
     ) ;
-    Senv.feedback "Server running." ;
     foreground server ;
-    signal true ;
+    set_active true ;
+    Senv.feedback "Server running." ;
     begin
       try
         while not server.shutdown do
@@ -339,12 +401,12 @@ let run server =
       with Sys.Break -> () (* Ctr+C, just leave the loop normally *)
     end;
     Senv.feedback "Server shutdown." ;
-    signal false ;
-  with
-  | Killed -> ()
-  | exn ->
+    emitter := nop ;
+    set_active false ;
+  with exn ->
     Senv.feedback "Server interruped (fatal error)." ;
-    signal false ;
+    emitter := nop ;
+    set_active false ;
     raise exn
 
 (* -------------------------------------------------------------------------- *)
diff --git a/src/plugins/server/main.mli b/src/plugins/server/main.mli
index 531149ff71164a0a2bbd05ddf7e928af766db806..eec63fefa2bfe2d5debac3a3db51c5f15f1f1269 100644
--- a/src/plugins/server/main.mli
+++ b/src/plugins/server/main.mli
@@ -35,7 +35,14 @@ val pp_kind : Format.formatter -> kind -> unit
 
 val register : kind -> string -> (json -> json) -> unit
 val find : string -> (kind * (json -> json)) option
-val exec : string -> json -> json (** @raises Not_found if not registered *)
+val exec : string -> json -> json (** @raises Not_found if not registered. *)
+
+(* -------------------------------------------------------------------------- *)
+(** {2 Signals Registry} *)
+(* -------------------------------------------------------------------------- *)
+
+type signal
+val signal : string -> signal
 
 (* -------------------------------------------------------------------------- *)
 (** {2 Server Main Process} *)
@@ -47,6 +54,8 @@ type 'a request = [
   | `Poll
   | `Request of 'a * string * json
   | `Kill of 'a
+  | `SigOn of string
+  | `SigOff of string
   | `Shutdown
 ]
 
@@ -57,6 +66,7 @@ type 'a response = [
   | `Error of 'a * string
   | `Killed of 'a
   | `Rejected of 'a
+  | `Signal of string
 ]
 
 (** A paired request-response message.
@@ -68,15 +78,10 @@ type 'a message = {
 
 type 'a server
 
-(**
-   Run a server with the provided low-level network primitives to
-   actually exchange data.
-
-   The function does not return until the server is explicitely
-   Shutdown. Logs are monitored unless [~logs:false] is specified.
+(** Run a server with the provided low-level network primitives to actually
+    exchange data. Logs are monitored unless [~logs:false] is specified.
 
-   Default equality is the standard `(=)` one.
-*)
+    Default equality is the standard `(=)` one. *)
 val create :
   pretty:(Format.formatter -> 'a -> unit) ->
   ?equal:('a -> 'a -> bool) ->
@@ -84,25 +89,34 @@ val create :
   unit -> 'a server
 
 (** Run the server forever.
-    The function will not return until the server is shut down. *)
+    The function will {i not} return until the server is actually shut down. *)
 val run : 'a server -> unit
 
 (** Start the server in background.
-    The function returns immediately
-    after installing a daemon that accepts GET requests received by
-    the server on calls to `Db.yield()`.
-*)
+
+    The function returns immediately after installing a daemon that accepts GET
+    requests received by the server on calls to [Db.yield()]. *)
 val start : 'a server -> unit
 
 (** Stop the server if it is running in background. *)
 val stop : 'a server -> unit
 
-(** Kills the currently running request. Actually raises an exception. *)
+(** Kill the currently running request by raising an exception. *)
 val kill : unit -> 'a
 
+(** Emit the server signal to the client. *)
+val emit : signal -> unit
+
+(** Register a callback on signal listening.
+
+    The callback is invoked with [true] on [SIGON] command
+    and [false] on [SIGOFF].
+*)
+val on_signal : signal -> (bool -> unit) -> unit
+
 (** Register a callback to listen for server activity.
-    All callbacks would be executed in their order of registration.
-    They shall {i never} raise any exception. *)
+    All callbacks are executed in their order of registration.
+    Callbacks shall {i never} raise any exception. *)
 val on : (bool -> unit) -> unit
 
 (* -------------------------------------------------------------------------- *)
diff --git a/src/plugins/server/request.ml b/src/plugins/server/request.ml
index ba522a19fec051169c772e9d0c66043f4a18972e..cb0de2eb1945f2321397535d0c48b6e741d5545c 100644
--- a/src/plugins/server/request.ml
+++ b/src/plugins/server/request.ml
@@ -81,6 +81,31 @@ let check_page page name =
     Senv.warning ~wkey:wkind
       "Request '%s' shall not be published in protocol pages" name
 
+let page_prefix page =
+  match Doc.chapter page with
+  | `Kernel -> "kernel"
+  | `Plugin plugin -> plugin
+  | `Protocol -> "protocol"
+
+(* -------------------------------------------------------------------------- *)
+(* --- Signals                                                            --- *)
+(* -------------------------------------------------------------------------- *)
+
+type signal = Main.signal
+
+let signal ~page ~name ~descr  ?(details=[]) () =
+  let open Markdown in
+  check_name name ;
+  check_page page name ;
+  let title =  Printf.sprintf "`SIG` %s" name in
+  let index = [ Printf.sprintf "%s (`SIGNAL`)" name ] in
+  let contents = [ Block [Text descr] ; Block details] in
+  let _ = Doc.publish ~page ~name ~title ~index ~contents () in
+  Main.signal name
+
+let emit = Main.emit
+let on_signal = Main.on_signal
+
 (* -------------------------------------------------------------------------- *)
 (* --- Multiple Fields Requests                                           --- *)
 (* -------------------------------------------------------------------------- *)
@@ -136,14 +161,14 @@ let doc_input (type a) (input : a rq_input) =
   match input with
   | Pnone -> assert false
   | Pdata _ -> []
-  | Pfields fs -> [Syntax.fields ~title:"Input" (List.rev fs)]
+  | Pfields fs -> [Syntax.fields ~title:"Input Params" (List.rev fs)]
 
 (* json output syntax *)
 let doc_output (type b) (output : b rq_output) =
   match output with
   | Rnone -> assert false
   | Rdata _ -> []
-  | Rfields fs -> [Syntax.fields ~title:"Output" (List.rev fs)]
+  | Rfields fs -> [Syntax.fields ~title:"Output Params" (List.rev fs)]
 
 (* -------------------------------------------------------------------------- *)
 (* --- Multi-Parameters Requests                                          --- *)
@@ -185,7 +210,11 @@ let param (type a b) (s : (unit,b) signature) ~name ~descr
     ?default (input : a input) : a param =
   let module D = (val input) in
   let syntax = if default = None then D.syntax else Syntax.option D.syntax in
-  let fd = Syntax.{ name ; syntax ; descr } in
+  let fd = Syntax.{
+      fd_name = name ;
+      fd_syntax = syntax ;
+      fd_descr = descr ;
+    } in
   s.input <- Pfields (fd :: fds_input s) ;
   fun rq ->
     try D.of_json (Fmap.find name rq.param)
@@ -197,7 +226,11 @@ let param (type a b) (s : (unit,b) signature) ~name ~descr
 let param_opt (type a b) (s : (unit,b) signature) ~name ~descr
     (input : a input) : a option param =
   let module D = (val input) in
-  let fd = Syntax.{ name ; syntax = Syntax.option D.syntax ; descr } in
+  let fd = Syntax.{
+      fd_name = name ;
+      fd_syntax = Syntax.option D.syntax ;
+      fd_descr = descr ;
+    } in
   s.input <- Pfields (fd :: fds_input s) ;
   fun rq ->
     try Some(D.of_json (Fmap.find name rq.param))
@@ -218,7 +251,11 @@ let fds_output s : Syntax.field list =
 let result (type a b) (s : (a,unit) signature) ~name ~descr
     ?default (output : b output) : b result =
   let module D = (val output) in
-  let fd = Syntax.{ name ; syntax = D.syntax ; descr } in
+  let fd = Syntax.{
+      fd_name = name ;
+      fd_syntax = D.syntax ;
+      fd_descr = descr ;
+    } in
   s.output <- Rfields (fd :: fds_output s) ;
   begin
     match default with
@@ -230,7 +267,11 @@ let result (type a b) (s : (a,unit) signature) ~name ~descr
 let result_opt (type a b) (s : (a,unit) signature) ~name ~descr
     (output : b output) : b option result =
   let module D = (val output) in
-  let fd = Syntax.{ name ; syntax = option D.syntax ; descr } in
+  let fd = Syntax.{
+      fd_name = name ;
+      fd_syntax = option D.syntax ;
+      fd_descr = descr ;
+    } in
   s.output <- Rfields (fd :: fds_output s) ;
   fun rq opt ->
     match opt with None -> () | Some v ->
@@ -297,20 +338,15 @@ let register_sig (type a b) (s : (a,b) signature) (process : rq -> a -> b) =
   let skind = Main.string_of_kind s.kind in
   let title =  Printf.sprintf "`%s` %s" skind s.name in
   let index = [ Printf.sprintf "%s (`%s`)" s.name skind ] in
-  let header = [ plain "Input", Center; plain "Output", Center] in
-  let content =
-    [[ Syntax.text @@ sy_input s.input ;
-       Syntax.text @@ sy_output s.output ]]
-  in
-  let synopsis = Table { caption=None ; header; content } in
-  let description =
-    [ Block [Text s.descr ] ; synopsis ; Block s.details] @
-    doc_input s.input @
-    doc_output s.output
-  in
-  let _ =
-    Doc.publish ~page:s.page ~name:s.name ~title ~index description []
+  let input =
+    Syntax.define (plain "Input") (Syntax.text @@ sy_input s.input) in
+  let output =
+    Syntax.define (plain "Output") (Syntax.text @@ sy_output s.output) in
+  let contents =
+    Block ( Text s.descr :: input :: output :: s.details ) ::
+    ( doc_input s.input @ doc_output s.output )
   in
+  let _ = Doc.publish ~page:s.page ~name:s.name ~title ~index ~contents () in
   Main.register s.kind s.name processor ;
   s.defined <- true
 
@@ -323,4 +359,15 @@ let register ~page ~kind ~name ~descr ?details ~input ~output process =
     (signature ~page ~kind ~name ~descr ?details ~input ~output ())
     (fun _rq v -> process v)
 
+let dictionary (d : 'a Data.Enum.dictionary) =
+  let name = Data.Enum.name d in
+  let page = Data.Enum.page d in
+  let descr = Markdown.plain "Returns all tags registered for" @
+              Data.Enum.syntax d in
+  register ~kind:`GET ~page
+    ~name:(Printf.sprintf "%s.dictionary.%s" (page_prefix page) name) ~descr
+    ~input:(module Data.Junit)
+    ~output:(module Data.Tag.Jlist)
+    (fun () -> Data.Enum.tags d)
+
 (* -------------------------------------------------------------------------- *)
diff --git a/src/plugins/server/request.mli b/src/plugins/server/request.mli
index 2c4347f76a9a564d6f42031d1dd506f016f6a92f..d3c120adf9e0fc348095180d1ae9b5bd00ae3743 100644
--- a/src/plugins/server/request.mli
+++ b/src/plugins/server/request.mli
@@ -44,6 +44,35 @@ end
 type 'a input = (module Input with type t = 'a)
 type 'b output = (module Output with type t = 'b)
 
+(** {2 Signals}
+
+    A signal is a one-way message from server to client for indicating that
+    something happened. To avoid unecessary noise, the client must be registered
+    on each signal it is interested in. The client will then send a proper get
+    requests to the server to retrieve the updated data.
+
+    As a matter of fact, update events are much more frequent than what a
+    typical GUI client can handle. Hence, signal emissions can not carry data and
+    are grouped (or « debounced ») until the next server response to client. *)
+
+(** The type of registered signals. *)
+type signal
+
+(** Register a server signal. The signal [name] must be unique. *)
+val signal :
+  page:Doc.page ->
+  name:string ->
+  descr:Markdown.text ->
+  ?details:Markdown.block ->
+  unit -> signal
+
+(** Emit the signal to the client. *)
+val emit : signal -> unit
+
+(** Callback invoked each time the client is starting or stopping
+    to listen to the given signal. *)
+val on_signal : signal -> (bool -> unit) -> unit
+
 (** {2 Simple Requests Registration} *)
 
 (** Register a simple request of type [(a -> b)].
@@ -162,8 +191,8 @@ val register_sig : ('a,'b) signature -> (rq -> 'a -> 'b) -> unit
 *)
 
 
-(** Named input parameter. If a default value is provided,
-    the JSON input field becomes optional. Otherwized, it is required. *)
+(** Named input parameter. If a default value is provided, the JSON input field
+    becomes optional. Otherwized, it is required. *)
 val param : (unit,'b) signature ->
   name:string ->
   descr:Markdown.text ->
@@ -176,10 +205,9 @@ val param_opt : (unit,'b) signature ->
   descr:Markdown.text ->
   'a input -> 'a option param
 
-(** Named output parameter. If a default value is provided,
-    the JSON output field is initialized with it.
-    Otherwized, it shall be set at each invocation of the request processing
-    funciton. *)
+(** Named output parameter. If a default value is provided, the JSON output
+    field is initialized with it. Otherwise, it shall be set at each invocation
+    of the request processing funciton. *)
 val result : ('a,unit) signature ->
   name:string ->
   descr:Markdown.text ->
@@ -192,4 +220,10 @@ val result_opt : ('a,unit) signature ->
   descr:Markdown.text ->
   'b output -> 'b option result
 
+(** {2 Exporting Dictionaries} *)
+
+(** Register a [GET] request [dictionary.<name>] to retrieve all tags registered
+    in the dictionary. *)
+val dictionary : 'a Data.Enum.dictionary -> unit
+
 (* -------------------------------------------------------------------------- *)
diff --git a/src/plugins/server/server_zmq.ml b/src/plugins/server/server_zmq.ml
index c736e542210a519a518e8e0f06f09684b4f2777b..a870c27440c867fc4722eb5a36d5fc42a5354274 100644
--- a/src/plugins/server/server_zmq.ml
+++ b/src/plugins/server/server_zmq.ml
@@ -98,6 +98,8 @@ let rec decode = function
   | ("GET"|"SET"|"EXEC")::id::request::data :: w ->
     `Request(id,request,jdecode data) :: decode w
   | "KILL"::id:: w -> `Kill id :: decode w
+  | "SIGON" :: sg :: w -> `SigOn sg :: decode w
+  | "SIGOFF" :: sg :: w -> `SigOff sg :: decode w
   | "POLL" :: w -> `Poll :: decode w
   | "SHUTDOWN" :: _ -> [`Shutdown]
   | cmd::_ -> raise (WrongEncoding cmd)
@@ -108,6 +110,7 @@ let rec encode = function
   | `Error(id,msg) :: w -> "ERROR" :: id :: msg :: encode w
   | `Killed id :: w -> "KILLED" :: id :: encode w
   | `Rejected id :: w -> "REJECTED" :: id :: encode w
+  | `Signal sg :: w -> "SIGNAL" :: sg :: encode w
   | [] -> []
 
 (* -------------------------------------------------------------------------- *)
diff --git a/src/plugins/server/share/protocol/server.md b/src/plugins/server/share/protocol/server.md
index 1e479798cef4eba9c42ebf5412a5886e77e6feae..8d308a85a4d36f59e3c33607d981eec88fc2bd41 100644
--- a/src/plugins/server/share/protocol/server.md
+++ b/src/plugins/server/share/protocol/server.md
@@ -52,6 +52,26 @@ To summarize:
 | `SET`   | - | - | fast, side-effects |
 | `EXEC`  | - | ✓ | resource demanding |
 
+## Server Signals
+
+In response to a logical requests, the server might also emit _signals_
+to the client. However, since a lot of signals might be emitted, the server
+must be aware of which signals the client is listening to.
+Signal are identified by unique strings.
+
+The server and client can exchange two special commands to manage signals:
+
+| Command | Issued by | Effect                      |
+|:--------|:---------:|:----------------------------|
+| `SIGON s` | client | start listening to signal `<s>` |
+| `SIGOFF s` | client | stop listening to signal `<s>` |
+| `SIGNAL s` | server | signal `<s>` has been emitted  |
+
+When one or many requests emit some signal `<s>` several times,
+the client would be notified only once per cycle of data exchange.
+Signals will be notified in addition to responses or logical requests
+or server polling.
+
 ## Transport Messages
 
 From the entry points layer, the asynchronous behavior of the Server makes
@@ -70,10 +90,12 @@ a list of _commands_:
 
 | Commands | Parameters | Description |
 |:--------:|:----------:|:------------|
-| `POLL` | - | Ask for pending responses, if any |
+| `POLL` | - | Ask for pending responses and signals, if any |
 | `GET` | `id,request,data` | En-queue the given GET request |
 | `SET` | `id,request,data` | En-queue the given SET request |
 | `EXEC` | `id,request,data` | En-queue the given EXEC request |
+| `SIGON` | `id` | Start listening to the given signal |
+| `SIGOFF` | `id` | Stop listening to the given signal |
 | `KILL` | `id` | Cancel the given request or interrupt its execution |
 | `SHUTDOWN` | - | Makes the server to stop running |
 
@@ -84,6 +106,7 @@ of _replies_, listed in table below:
 |:--------:|:----------:|:------------|
 | `DATA` | `id,data` | Response data from the identified request |
 | `ERROR` | `id,message` | Error message from the identified request |
+| `SIGNAL` | `id` | The identified signal has been emitted since last exchange |
 | `KILLED` | `id` | The identified request has been killed or interrupted |
 | `REJECTED` | `id` | The identified request was not registered on the Server |
 
diff --git a/src/plugins/server/share/protocol/server_zmq.md b/src/plugins/server/share/protocol/server_zmq.md
index f510a31888e81e509a0635425bf6a9ef3cbf75e2..67999c67a5900e251f1340f539d1c447dad6b0a7 100644
--- a/src/plugins/server/share/protocol/server_zmq.md
+++ b/src/plugins/server/share/protocol/server_zmq.md
@@ -24,11 +24,13 @@ of each command is a single string identifying the command:
 | Commands | Parts | Part 1 | Part 2 | Part 3 | Part 4 |
 |:--------|:-----:|:-------|:-------|:-------|:-------|
 | `POLL()` | 1    | `"POLL"` | | | |
-| `GET(id,request,data)` | 4 | `"GET"` | id | request | data |
-| `SET(id,request,data)` | 4 | `"SET"` | id | request | data |
+| `GET(id,request,data)`  | 4 | `"GET"` | id | request | data |
+| `SET(id,request,data)`  | 4 | `"SET"` | id | request | data |
 | `EXEC(id,request,data)` | 4 | `"EXEC"` | id | request | data |
-| `KILL(id)` | 2 | `"KILL"` | id | | |
-| `SHUTDOWN` | 1 | `"SHUTDOWN"` | | | |
+| `SIGON(id)`  | 2 | `"SIGON"`  | id | | |
+| `SIGOFF(id)` | 2 | `"SIGOFF"` | id | | |
+| `KILL(id)`   | 2 | `"KILL"`   | id | | |
+| `SHUTDOWN`   | 1 | `"SHUTDOWN"` | | | |
 
 ## Output Message Format
 
@@ -42,6 +44,7 @@ of each reply is a finel string identifying the reply:
 | `ERROR(id,message)` | 4 | `"ERROR"` | id | message |
 | `KILLED(id)` | 2 | `"KILLED"` | id | |
 | `REJECTED(id)` | 2 | `"REJECTED"` | id | |
+| `SIGNAL(id)`   | 2 | `"SIGNAL"` | id | |
 | (special) | 2 | `"WRONG"` | message | |
 | (special) | 1 | `"NONE"` | | |
 
diff --git a/src/plugins/server/states.ml b/src/plugins/server/states.ml
new file mode 100644
index 0000000000000000000000000000000000000000..8b28add10a63c80483aed19e9434ab0fe29660c2
--- /dev/null
+++ b/src/plugins/server/states.ml
@@ -0,0 +1,331 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  This file is part of Frama-C.                                         *)
+(*                                                                        *)
+(*  Copyright (C) 2007-2020                                               *)
+(*    CEA (Commissariat à l'énergie atomique et aux énergies              *)
+(*         alternatives)                                                  *)
+(*                                                                        *)
+(*  you can redistribute it and/or modify it under the terms of the GNU   *)
+(*  Lesser General Public License as published by the Free Software       *)
+(*  Foundation, version 2.1.                                              *)
+(*                                                                        *)
+(*  It is distributed in the hope that it will be useful,                 *)
+(*  but WITHOUT ANY WARRANTY; without even the implied warranty of        *)
+(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *)
+(*  GNU Lesser General Public License for more details.                   *)
+(*                                                                        *)
+(*  See the GNU Lesser General Public License version 2.1                 *)
+(*  for more details (enclosed in the file licenses/LGPLv2.1).            *)
+(*                                                                        *)
+(**************************************************************************)
+
+open Data
+
+type 'a callback = ('a -> unit) -> unit
+
+let install signal hook = function
+  | None -> ()
+  | Some add_hook ->
+    let once = ref true in
+    let install ok =
+      if ok && !once then
+        begin
+          once := false ;
+          add_hook hook ;
+        end
+    in Request.on_signal signal install
+
+let install_emit signal add_hook =
+  install signal (fun () -> Request.emit signal) add_hook
+
+(* -------------------------------------------------------------------------- *)
+(* --- Values                                                             --- *)
+(* -------------------------------------------------------------------------- *)
+
+let register_value (type a) ~page ~name ~descr ?(details=[])
+    ~(output : a Request.output) ~get
+    ?(add_hook : unit callback option) ()
+  =
+  let open Markdown in
+  let title =  Printf.sprintf "`VALUE` %s" name in
+  let index = [ Printf.sprintf "%s (`VALUE`)" name ] in
+  let contents = [ Block [Text descr] ; Block details] in
+  let h = Doc.publish ~page ~name ~title ~index ~contents () in
+  let signal = Request.signal ~page ~name:(name ^ ".sig")
+      ~descr:(plain "Signal for value " @ href h) () in
+  Request.register ~page ~kind:`GET ~name:(name ^ ".get")
+    ~descr:(plain "Getter for value " @ href h)
+    ~input:(module Junit) ~output get ;
+  install_emit signal add_hook ;
+  signal
+
+(* -------------------------------------------------------------------------- *)
+(* --- States                                                             --- *)
+(* -------------------------------------------------------------------------- *)
+
+let register_state (type a) ~page ~name ~descr ?(details=[])
+    ~(data : a data) ~get ~set
+    ?(add_hook : unit callback option) () =
+  let open Markdown in
+  let title =  Printf.sprintf "`STATE` %s" name in
+  let index = [ Printf.sprintf "%s (`STATE`)" name ] in
+  let contents = [ Block [Text descr] ; Block details] in
+  let h = Doc.publish ~page ~name ~title ~index ~contents () in
+  let signal = Request.signal ~page ~name:(name ^ ".sig")
+      ~descr:(plain "Signal for state " @ href h) () in
+  Request.register ~page ~kind:`GET ~name:(name ^ ".get")
+    ~descr:(plain "Getter for state " @ href h)
+    ~input:(module Junit) ~output:(module (val data)) get ;
+  Request.register ~page ~kind:`SET ~name:(name ^ ".set")
+    ~descr:(plain "Setter for state " @ href h)
+    ~input:(module (val data)) ~output:(module Junit) set ;
+  install_emit signal add_hook ;
+  signal
+
+(* -------------------------------------------------------------------------- *)
+(* --- Model Signature                                                    --- *)
+(* -------------------------------------------------------------------------- *)
+
+type 'a column = Syntax.field * ('a -> json)
+
+type 'a model = 'a column list ref
+
+let model () = ref []
+
+let column (type a b) ~(model : a model) ~name ~descr
+    ~(data: b Request.output) ~(get : a -> b) () =
+  let module D = (val data) in
+  if name = "key" || name = "index" then
+    raise (Invalid_argument "Server.States.column: invalid name") ;
+  if List.exists (fun (fd,_) -> fd.Syntax.fd_name = name) !model then
+    raise (Invalid_argument "Server.States.column: duplicate name") ;
+  let fd = Syntax.{
+      fd_name = name ;
+      fd_syntax = D.syntax ;
+      fd_descr = descr ;
+    } in
+  model := (fd , fun a -> D.to_json (get a)) :: !model
+
+module Kmap = Map.Make(String)
+
+(* -------------------------------------------------------------------------- *)
+(* --- Model Content                                                      --- *)
+(* -------------------------------------------------------------------------- *)
+
+type 'a update = Remove | Add of 'a
+type 'a content = {
+  mutable cleared : bool ;
+  mutable updates : 'a update Kmap.t ;
+}
+
+type 'a array = {
+  signal : Request.signal ;
+  key : 'a -> string ;
+  iter : ('a -> unit) -> unit ;
+  getter : (string * ('a -> json)) list ;
+  (* [LC+JS]
+     The two following fields allow to keep an array in sync
+     with the current project and still have a polymorphic data type. *)
+  mutable current : 'a content option ; (* fast access to current project *)
+  projects : (string , 'a content) Hashtbl.t ; (* indexed by project *)
+}
+
+let synchronize array =
+  begin
+    Project.register_after_set_current_hook
+      ~user_only:false (fun _ -> array.current <- None) ;
+    let cleanup p =
+      Hashtbl.remove array.projects (Project.get_unique_name p) in
+    Project.register_before_remove_hook cleanup ;
+    Project.register_todo_before_clear cleanup ;
+    Request.on_signal array.signal
+      (fun _ ->
+         array.current <- None ;
+         Hashtbl.clear array.projects ;
+      );
+  end
+
+let content array =
+  match array.current with
+  | Some w -> w
+  | None ->
+    let prj = Project.(current () |> get_unique_name) in
+    let content =
+      try Hashtbl.find array.projects prj
+      with Not_found ->
+        let w = {
+          cleared = true ;
+          updates = Kmap.empty ;
+        } in
+        Hashtbl.add array.projects prj w ; w
+    in array.current <- Some content ; content
+
+let reload array =
+  let m = content array in
+  m.cleared <- true ;
+  m.updates <- Kmap.empty ;
+  Request.emit array.signal
+
+let update array k =
+  let m = content array in
+  if not m.cleared then
+    begin
+      m.updates <- Kmap.add (array.key k) (Add k) m.updates ;
+      Request.emit array.signal ;
+    end
+
+let remove array k =
+  let m = content array in
+  if not m.cleared then
+    begin
+      m.updates <- Kmap.add (array.key k) Remove m.updates ;
+      Request.emit array.signal ;
+    end
+
+let signal array = array.signal
+
+(* -------------------------------------------------------------------------- *)
+(* --- Fetch Model Updates                                                --- *)
+(* -------------------------------------------------------------------------- *)
+
+type buffer = {
+  reload : bool ;
+  mutable capacity : int ;
+  mutable pending : int ;
+  mutable removed : string list ;
+  mutable updated : json list ;
+}
+
+let add_entry buffer cols key v =
+  let fjs = List.fold_left (fun fjs (fd,to_json) ->
+      try (fd , to_json v) :: fjs
+      with Not_found -> fjs
+    ) [] cols in
+  buffer.updated <- `Assoc( ("key", `String key):: fjs) :: buffer.updated ;
+  buffer.capacity <- pred buffer.capacity
+
+let remove_entry buffer key =
+  buffer.removed <- key :: buffer.removed ;
+  buffer.capacity <- pred buffer.capacity
+
+let update_entry buffer cols key = function
+  | Remove -> remove_entry buffer key
+  | Add v -> add_entry buffer cols key v
+
+let fetch array n =
+  let m = content array in
+  let reload = m.cleared in
+  let buffer = {
+    reload ;
+    capacity = n ;
+    pending = 0 ;
+    removed = [] ;
+    updated = [] ;
+  } in
+  begin
+    if reload then
+      begin
+        m.cleared <- false ;
+        array.iter
+          begin fun v ->
+            let key = array.key v in
+            if buffer.capacity > 0 then
+              add_entry buffer array.getter key v
+            else
+              ( m.updates <- Kmap.add key (Add v) m.updates ;
+                buffer.pending <- succ buffer.pending ) ;
+          end ;
+      end
+    else
+      m.updates <- Kmap.filter
+          begin fun key upd ->
+            if buffer.capacity > 0 then
+              ( update_entry buffer array.getter key upd ; false )
+            else
+              ( buffer.pending <- succ buffer.pending ; true )
+          end m.updates ;
+  end ;
+  buffer
+
+(* -------------------------------------------------------------------------- *)
+(* --- Signature Registry                                                 --- *)
+(* -------------------------------------------------------------------------- *)
+
+let register_array ~page ~name ~descr ?(details=[]) ~key
+    ~(iter : 'a callback)
+    ?(add_update_hook : 'a callback option)
+    ?(add_remove_hook : 'a callback option)
+    ?(add_reload_hook : unit callback option)
+    model =
+  let open Markdown in
+  let title =  Printf.sprintf "`ARRAY` %s" name in
+  let index = [ Printf.sprintf "%s (`ARRAY`)" name ] in
+  let columns = !model in
+  let contents = [
+    Block [Text descr] ;
+    Syntax.fields ~title:"Columns"
+      begin
+        Syntax.{
+          fd_name = "key" ;
+          fd_syntax = Syntax.ident ;
+          fd_descr = plain "entry identifier" ;
+        } :: List.rev (List.map fst columns)
+      end ;
+    Block details
+  ] in
+  let mref = Doc.publish ~page:page ~name:name ~title ~index ~contents () in
+  let signal = Request.signal ~page ~name:(name ^ ".sig")
+      ~descr:(plain "Signal for array " @ href mref) () in
+  let getter = List.map Syntax.(fun (fd,to_js) -> fd.fd_name , to_js) columns in
+  let array = {
+    key ; iter ; getter ; signal ;
+    current = None ; projects = Hashtbl.create 0
+  } in
+  let signature =
+    Request.signature ~kind:`GET ~page ~name:(name ^ ".fetch")
+      ~descr:(plain "Fetch updates for array " @ href mref)
+      ~input:(module Jint)
+      ~details:[
+        Text(plain
+               "Collect all entry updates since the last fetch.\n\
+                The number of fetched entries is limited to the\n\
+                provided integer. When `reload:true` is returned,\n\
+                _all_ previously received entries must be removed.")]
+      () in
+  let module Jentries =
+    (struct
+      include Jany
+      let syntax = Syntax.data "entry" mref
+    end) in
+  let set_reload = Request.result signature
+      ~name:"reload" ~descr:(plain "array fully reloaded")
+      (module Jbool) in
+  let set_removed = Request.result signature
+      ~name:"removed" ~descr:(plain "removed entries")
+      (module Jident.Jlist) in
+  let set_updated = Request.result signature
+      ~name:"updated" ~descr:(plain "updated entries")
+      (module Jlist(Jentries)) in
+  let set_pending = Request.result signature
+      ~name:"pending" ~descr:(plain "remaining entries to be fetched")
+      (module Jint) in
+  Request.register_sig signature
+    begin fun rq n ->
+      let buffer = fetch array n in
+      set_reload rq buffer.reload ;
+      set_removed rq buffer.removed ;
+      set_updated rq buffer.updated ;
+      set_pending rq buffer.pending ;
+    end ;
+  Request.register ~kind:`GET ~page ~name:(name ^ ".reload")
+    ~descr:(plain "Force full reload for array " @ href mref)
+    ~input:(module Junit) ~output:(module Junit)
+    (fun () -> reload array) ;
+  synchronize array ;
+  install signal (update array) add_update_hook ;
+  install signal (remove array) add_remove_hook ;
+  install signal (fun () -> reload array) add_reload_hook ;
+  array
+
+(* -------------------------------------------------------------------------- *)
diff --git a/src/plugins/server/states.mli b/src/plugins/server/states.mli
new file mode 100644
index 0000000000000000000000000000000000000000..00dabb46631f210d561c48e4d168e9dbd87bee7b
--- /dev/null
+++ b/src/plugins/server/states.mli
@@ -0,0 +1,135 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  This file is part of Frama-C.                                         *)
+(*                                                                        *)
+(*  Copyright (C) 2007-2020                                               *)
+(*    CEA (Commissariat à l'énergie atomique et aux énergies              *)
+(*         alternatives)                                                  *)
+(*                                                                        *)
+(*  you can redistribute it and/or modify it under the terms of the GNU   *)
+(*  Lesser General Public License as published by the Free Software       *)
+(*  Foundation, version 2.1.                                              *)
+(*                                                                        *)
+(*  It is distributed in the hope that it will be useful,                 *)
+(*  but WITHOUT ANY WARRANTY; without even the implied warranty of        *)
+(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *)
+(*  GNU Lesser General Public License for more details.                   *)
+(*                                                                        *)
+(*  See the GNU Lesser General Public License version 2.1                 *)
+(*  for more details (enclosed in the file licenses/LGPLv2.1).            *)
+(*                                                                        *)
+(**************************************************************************)
+
+(** Synchronized values between Server and Client *)
+
+type 'a callback = ('a -> unit) -> unit
+
+(** Register a (projectified) value and generates the associated signal and
+    request:
+    - Signal [<name>.sig] is emitted on value updates;
+    - GET Request [<name>.get] returns the current value.
+
+    If provided, the [~add_hook] option is used to register a hook to notify the
+    server of value updates. The hook will be installed only once the client
+    starts to listen for value updates.
+
+    Inside {b Ivette} you can use the [States.useSyncValue(id)] hook to
+    synchronize with this value.
+*)
+val register_value :
+  page:Doc.page ->
+  name:string ->
+  descr:Markdown.text ->
+  ?details:Markdown.block ->
+  output:'a Request.output ->
+  get:(unit -> 'a) ->
+  ?add_hook:(unit callback) ->
+  unit -> Request.signal
+
+(** Register a (projectified) state and generates the associated signal and
+    requests:
+    - Signal [<name>.sig] is emitted on value updates;
+    - GET Request [<name>.get] returns the current value;
+    - SET Request [<name>.set] modifies the server value.
+
+    If provided, the [~add_hook] option is used to register a hook to notify the
+    server of value updates. The hook will be installed only once the client
+    starts to listen for value updates.
+
+    Inside {b Ivette} you can use the [States.useSyncState(id)] hook to
+    synchronize with this state.
+*)
+val register_state :
+  page:Doc.page ->
+  name:string ->
+  descr:Markdown.text ->
+  ?details:Markdown.block ->
+  data:'a Data.data ->
+  get:(unit -> 'a) ->
+  set:('a -> unit) ->
+  ?add_hook:(unit callback) ->
+  unit -> Request.signal
+
+type 'a model (** Columns array model *)
+
+(** Creates an empty array model. *)
+val model : unit -> 'a model
+
+(** Populate an array model with a new field.
+    Columns with name `"id"` and `"_index"` are reserved for internal use. *)
+val column :
+  model:'a model ->
+  name:string ->
+  descr:Markdown.text ->
+  data:('b Request.output) ->
+  get:('a -> 'b) ->
+  unit -> unit
+
+(** Synchronized array state. *)
+type 'a array
+
+(** Mark the array to be fully reloaded. *)
+val reload : 'a array -> unit
+
+(** Mark an array entry as updated. *)
+val update : 'a array -> 'a -> unit
+
+(** Mark an array entry as removed. *)
+val remove : 'a array -> 'a -> unit
+
+(** Get the signal associated with the array. *)
+val signal : 'a array -> Request.signal
+
+(** Register everything necessary to synchronize an array with
+    the client:
+    - Signal [<name>.sig] is emitted on array updates;
+    - GET Request [<name>.fetch] is registered to get updates;
+    - GET Request [<name>.reload] is registered to trigger a full reload.
+
+    The [~key] parameter is used to identify array entries, and used to fill
+    the reserved column ["id"] of entries.
+
+    Columns added to the model after registration are {i not} taken into
+    account.
+
+    If provided, the [~add_xxx_hook] options are used to register hooks
+    to notify the server of corresponding array updates.
+    Each hook will be installed only once the client starts to listen for array
+    updates.
+
+    Inside {b Ivette} you can obtain the entries in sync by using the
+    [States.useSyncArray()] hook.
+*)
+val register_array :
+  page:Doc.page ->
+  name:string ->
+  descr:Markdown.text ->
+  ?details:Markdown.block ->
+  key:('a -> string) ->
+  iter:('a callback) ->
+  ?add_update_hook:('a callback) ->
+  ?add_remove_hook:('a callback) ->
+  ?add_reload_hook:(unit callback) ->
+  'a model -> 'a array
+
+(* -------------------------------------------------------------------------- *)
diff --git a/src/plugins/server/syntax.ml b/src/plugins/server/syntax.ml
index 727d520370b2a9a5d4c7ec5a8eac0cd534c46842..06abe8c24a54612a466b8d5212054e6b640f9010 100644
--- a/src/plugins/server/syntax.ml
+++ b/src/plugins/server/syntax.ml
@@ -60,7 +60,10 @@ let text { text } = text
 let protect a =
   if a.atomic then a.text else Markdown.(plain "(" @ a.text @ plain ")")
 
-let publish ~page ~name ~descr ~synopsis ?(details = []) () =
+let define left right =
+  Markdown.(Block_quote [Block[Text ( left @ plain ":=" @ right )]])
+
+let publish ~page ~name ~descr ~synopsis ?(details = []) ?generated () =
   check_name name ;
   check_page page name ;
   let id = Printf.sprintf "data-%s" name in
@@ -68,22 +71,26 @@ let publish ~page ~name ~descr ~synopsis ?(details = []) () =
   let index = [ Printf.sprintf "%s (`DATA`)" name ] in
   let dref = Doc.href page id in
   let dlink = Markdown.href ~text:(Markdown.emph name) dref in
-  let syntax = Markdown.(glue [
-      plain "<" ; dlink ; plain ">" ; plain ":=" ; synopsis.text ]) in
-  let content = Markdown.(Block ( text descr @ text syntax ) :: details) in
-  let _href = Doc.publish ~page ~name:id ~title ~index content [] in
-  atom dlink
+  let data = Markdown.(plain "<" @ dlink @ plain ">") in
+  let contents = Markdown.(Block(
+      [ Text descr ; define data synopsis.text ]
+    )) :: details in
+  let _href = Doc.publish ~page ~name:id ~title ~index ~contents ?generated ()
+  in atom dlink
+
+(* -------------------------------------------------------------------------- *)
 
-let unit = atom @@ Markdown.plain "-"
+let unit = atom @@ Markdown.code "null"
 let any = atom @@ Markdown.emph "any"
 let int = atom @@ Markdown.emph "int"
 let ident = atom @@ Markdown.emph "ident"
 let string = atom @@ Markdown.emph "string"
 let number = atom @@ Markdown.emph "number"
 let boolean = atom @@ Markdown.emph "boolean"
+let data name dref = atom @@ Markdown.href ~text:(Markdown.emph name) dref
 
 let escaped name =
-  Markdown.code (Printf.sprintf "'%s'" @@ String.escaped name)
+  Markdown.code (Printf.sprintf "\"%s\"" @@ String.escaped name)
 
 let tag name = atom @@ escaped name
 let array a = atom @@ Markdown.(code "[" @ protect a @ code  ", … ]")
@@ -100,30 +107,52 @@ let union ts = flow @@ Markdown.(glue ~sep:(plain "|") (List.map protect ts))
 
 let option t = atom @@ Markdown.(protect t @ code "?")
 
-let field (a,t) = Markdown.( escaped a @ code ":" @ t.text )
+(* -------------------------------------------------------------------------- *)
+
+type tag = {
+  tag_name : string ;
+  tag_label : Markdown.text ;
+  tag_descr : Markdown.text ;
+}
+
+let tags ?(title="Tag") (tgs : tag list) =
+  let open Markdown in
+  let header = [
+    plain title, Left;
+    plain "Value", Left;
+    plain "Description", Left
+  ] in
+  let row tg = [ tg.tag_label ; escaped tg.tag_name ; tg.tag_descr ] in
+  Markdown.Table {
+    caption = None ; header ; content = List.map row tgs ;
+  }
+
+(* -------------------------------------------------------------------------- *)
+
+let mfield (a,t) = Markdown.( escaped a @ code ":" @ t.text )
 
 let record fds =
   let fields =
     if fds = [] then Markdown.plain "…" else
-      Markdown.(glue ~sep:(code ";") (List.map field fds))
+      Markdown.(glue ~sep:(code ";") (List.map mfield fds))
   in atom @@ Markdown.(code "{" @ fields @ code "}")
 
 type field = {
-  name : string ;
-  syntax : t ;
-  descr : Markdown.text ;
+  fd_name : string ;
+  fd_syntax : t ;
+  fd_descr : Markdown.text ;
 }
 
-let fields ~title (fds : field list) =
+let fields ?(title="Field") (fds : field list) =
   let open Markdown in
   let header = [
     plain title, Left;
     plain "Format", Center;
     plain "Description", Left
   ] in
-  let column f = [ code f.name ; f.syntax.text ; f.descr ] in
+  let row f = [ escaped f.fd_name ; f.fd_syntax.text ; f.fd_descr ] in
   Markdown.Table {
-    caption = None ; header ; content = List.map column fds ;
+    caption = None ; header ; content = List.map row fds ;
   }
 
 (* -------------------------------------------------------------------------- *)
diff --git a/src/plugins/server/syntax.mli b/src/plugins/server/syntax.mli
index d2537edf19d77b2b70d9339c86223056c722eaaa..4f7ef4525cd795e1d947c13279a6c9523c9ce512 100644
--- a/src/plugins/server/syntax.mli
+++ b/src/plugins/server/syntax.mli
@@ -33,7 +33,10 @@ val text : t -> Markdown.text
     the description block. *)
 val publish :
   page:Doc.page -> name:string -> descr:Markdown.text ->
-  synopsis:t -> ?details:Markdown.elements -> unit -> t
+  synopsis:t ->
+  ?details:Markdown.elements ->
+  ?generated:(unit -> Markdown.elements) ->
+  unit -> t
 
 val unit : t
 val any : t
@@ -49,11 +52,27 @@ val tuple : t list -> t
 val union : t list -> t
 val option : t -> t
 val record : (string * t) list -> t
+val data : string -> Markdown.href -> t
 
-type field = { name : string ; syntax : t ; descr : Markdown.text }
+type tag = {
+  tag_name : string ;
+  tag_label : Markdown.text ;
+  tag_descr : Markdown.text ;
+}
 
-(** Builds a table with fields column named with [~title]
-    (shall be capitalized) *)
-val fields : title:string -> field list -> Markdown.element
+(** Syntactic definition: LEFT := RIGHT *)
+val define : Markdown.text -> Markdown.text -> Markdown.block_element
+
+(** Builds a table with tags description.
+    The [~title] is applied to the tag name column
+    (shall be capitalized, defaults to ["Tag"]). *)
+val tags : ?title:string -> tag list -> Markdown.element
+
+type field = { fd_name : string ; fd_syntax : t ; fd_descr : Markdown.text }
+
+(** Builds a table with fields description.
+    The [~title] is applied to the field name column
+    (shall be capitalized, defaults to ["Field"]). *)
+val fields : ?title:string -> field list -> Markdown.element
 
 (* -------------------------------------------------------------------------- *)