diff --git a/CHANGES.md b/CHANGES.md
index 364430572606cd30d4ec957c245a89078a2e1eb0..a5ee611aaa6f9cfb0727eca5584a8046b7c11519 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -7,6 +7,7 @@
  - Bump required compiler version to 4.10.2. (@ejgallego)
  - Fixed missing indentation in pretty-printing of goals. (#126,
    @corwin-of-amber)
+ - The long-awaited settings panel. (#12, @corwin-of-amber)
 
 # jsCoq 0.12.3 "at midday"
 ---------------------------------------
diff --git a/ui-css/coq-base.css b/ui-css/coq-base.css
index f9b2cb3ab50e6e376a3cfd8e40922d6d01b12b49..4f7c5ec0bec1642769ac2d94c2700e41a7e0403f 100644
--- a/ui-css/coq-base.css
+++ b/ui-css/coq-base.css
@@ -27,6 +27,7 @@ a, a:link, a:visited, a:active {
   margin-right: auto;
   margin-left: auto;
   overscroll-behavior: none;
+  --base-font-size: 11pt;
 }
 
 #ide-wrapper.layout-fixed {
@@ -70,7 +71,7 @@ a, a:link, a:visited, a:active {
 /* CodeMirror */
 
 .jscoq.CodeMirror {
-  font-size: 11pt;
+  font-size: var(--base-font-size);
   height: 100%;  /* auto-size to fit content */
   line-height: initial;
 }
@@ -138,7 +139,7 @@ body.jscoq-main .CodeMirror-linenumber {
   z-index: 20;  /* notice: CodeMirror-dialog is at index 15 */
 
   font-family: Helvetica, Geneva, Swiss, Arial, SunSans-Regular, sans-serif;
-  font-size: 11pt;
+  font-size: var(--base-font-size);
 }
 
 #panel-wrapper * {
@@ -212,8 +213,8 @@ body.jscoq-main .CodeMirror-linenumber {
 .flex-panel > .content {
   flex: 1 0 0;
   padding: 0 4px;
-  /*transition: opacity 0.5s ease-in-out;*/
-  font-family: monospace;
+  font-family: monospace, "Arial Unicode MS";
+  line-height: 1.2;  /* because some characters are too tall (due to alternative font glyph rendering) */
   overflow: auto;
   overscroll-behavior: contain;
 }
@@ -335,8 +336,8 @@ body.jscoq-main .CodeMirror-linenumber {
 
 #toolbar .exits.right {
   position: absolute;
-  top: 4px;
-  right: 4px;
+  top: 0px;
+  right: 0px;
   transition: opacity 620ms ease-in;
 }
 
@@ -345,22 +346,21 @@ body.jscoq-main .CodeMirror-linenumber {
   transition: opacity 120ms linear;
 }
 
-.link-to-github {
-  word-spacing: -2px; /* aren't we picky */
+#toolbar svg.app-menu-button {
+  height: 36px;
+  fill: #ccc;
 }
-
-.link-to-github::after {
-  content: "";
-  display: inline-block;
-  height: 25px;
-  width: 25px;
-  background: url(../ui-images/github.png);
-  background-size: 25px 25px;
-  background-repeat: no-repeat;
-  vertical-align: middle;
-  margin-left: .3em;
-  padding-bottom: 5px;
-  box-sizing: content-box;
+#toolbar svg.app-menu-button:hover {
+  fill: #777;
+  background-color: #f2f6f2;
+}
+#toolbar svg.app-menu-button.active {
+  background-color: #676b67;
+  fill: #f2f6f2;
+}
+#toolbar svg.app-menu-button.active:hover {
+  background-color: #757a75;
+  fill: #f2f6f2;
 }
 
 #buttons {
diff --git a/ui-css/coq-dark.css b/ui-css/coq-dark.css
index d9f095327fbb8e66dcf94b8bfb01d11abd4c93c6..8781319401c6fd87e3e8f6cfba1d0899f7272e3f 100644
--- a/ui-css/coq-dark.css
+++ b/ui-css/coq-dark.css
@@ -49,15 +49,38 @@
   opacity: 0.6;
 }
 
-.jscoq-theme-dark .link-to-github::after {
-  background-image: url(../ui-images/themes/dark/github.png);
-}
-
 .jscoq-theme-dark .flex-panel > .caption {
   background: #666;
   color: #ccc;
 }
 
+.jscoq-theme-dark #toolbar svg.app-menu-button {
+  fill: #ccc;
+}
+.jscoq-theme-dark #toolbar svg.app-menu-button:hover {
+  fill: #ddd;
+  background-color: #393b39;
+}
+.jscoq-theme-dark #toolbar svg.app-menu-button.active {
+  background-color: #656965;
+  fill: #f2f6f2;
+}
+.jscoq-theme-dark #toolbar svg.app-menu-button.active:hover {
+  background-color: #757a75;
+  fill: #f2f6f2;
+}
+
+.settings-panel[data-theme=dark] {
+  background: #555;
+  border-color: #888;
+  box-shadow: -2px 3px 6px 0px #fff2;
+  color: #ddd;
+}
+
+.settings-panel[data-theme=dark] .link-to-github::after {
+  background-image: url(../ui-images/themes/dark/github.png);
+}
+
 /* Code settings */
 /* Replaced by blackboard CM theme */
 /*.CodeMirror-gutters { background-color: #000; }*/
diff --git a/ui-css/settings.css b/ui-css/settings.css
new file mode 100644
index 0000000000000000000000000000000000000000..4c11ad18708e0686c08d3500e0b9f69e0d7c6568
--- /dev/null
+++ b/ui-css/settings.css
@@ -0,0 +1,218 @@
+
+div.settings-panel {
+  position: absolute;
+  right: 0;
+  top: 35px;
+  z-index: 99;
+  background: white;
+  border: 1px solid black;
+  min-width: 200px;
+  font-family: Helvetica, Geneva, Swiss, Arial, SunSans-Regular, sans-serif;
+  font-size: 11pt;
+  padding: .75em .75em .2em .75em;
+  box-shadow: -2px 3px 6px 0px #3332;
+  outline: none !important;
+}
+
+div.settings-panel div.setting {
+  line-height: 1.75em;
+}
+
+div.settings-panel .switch {
+  float: right;
+}
+div.settings-panel input.switch {
+  height: 1.75em;
+  margin: 0;
+}
+
+div.settings-panel div.links {
+  margin-top: .2em;
+  text-align: right;
+}
+
+.link-to-github {
+  word-spacing: -2px; /* aren't we picky */
+}
+
+.link-to-github::after,
+.link-to-zulip::after {
+  content: "";
+  display: inline-block;
+  height: 25px;
+  width: 25px;
+  background-size: 25px 25px;
+  background-repeat: no-repeat;
+  vertical-align: middle;
+  margin-left: .3em;
+  padding-bottom: 5px;
+  box-sizing: content-box;
+}
+
+.link-to-github::after {
+  background-image: url(../ui-images/github.png);
+}
+
+.link-to-zulip::after {
+  background-image: url(../ui-images/zulip-logo.png);
+  background-size: 20px 22px;
+  background-position: 3px 2px;
+  width: 23px;
+}
+
+/* Light switch (https://codepen.io/marcusconnor/pen/QJNvMa) */
+
+.rocker, .rocker *, .rocker *::before, .rocker *::after {
+  box-sizing: border-box;
+  margin:0;
+  padding:0;
+}
+
+/* Switch starts here */
+.rocker {
+  display: inline-block;
+  position: relative;
+  /*
+  SIZE OF SWITCH
+  ==============
+  All sizes are in em - therefore
+  changing the font-size here
+  will change the size of the switch.
+  See .rocker-small below as example.
+  */
+  font-size: 2em;
+  font-weight: bold;
+  text-align: center;
+  text-transform: uppercase;
+  color: #888;
+  width: 7em;
+  height: 4em;
+  overflow: hidden;
+  border-bottom: 0.5em solid #eee;
+}
+
+.rocker-small {
+  font-size: 0.35em; /* Sizes the switch */
+  height: 4.15em; /* had to change this for small font size */
+  margin: 0;
+}
+
+.rocker-small .switch-left,
+.rocker-small .switch-right {
+  transition: 0.1s;  /* shorter transition for small */
+}
+
+.rocker::before {
+  content: "";
+  position: absolute;
+  top: 0.5em;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  background-color: #999;
+  border: 0.5em solid #eee;
+  border-bottom: 0;
+}
+
+.rocker input {
+  opacity: 0;
+  width: 0;
+  height: 0;
+}
+
+.switch-left,
+.switch-right {
+  cursor: pointer;
+  position: absolute;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  height: 2.5em;
+  width: 3em;
+  transition: 0.2s;
+}
+
+.switch-left {
+  height: 2.4em;
+  width: 2.75em;
+  left: 0.85em;
+  bottom: 0.4em;
+  background-color: #ddd;
+  transform: rotate(15deg) skewX(15deg);
+}
+
+.switch-right {
+  right: 0.5em;
+  bottom: 0;
+  background-color: /*#bd5757;*/ #535;
+  color: #fff;
+}
+
+.switch-left::before,
+.switch-right::before {
+  content: "";
+  position: absolute;
+  width: 0.4em;
+  height: 2.45em;
+  bottom: -0.45em;
+  background-color: #ccc;
+  transform: skewY(-65deg);
+}
+
+.switch-left::before {
+  left: -0.4em;
+}
+
+.switch-right::before {
+  right: -0.375em;
+  background-color: transparent;
+  transform: skewY(65deg);
+}
+
+input:checked + .switch-left {
+  background-color: #eed3aa;
+  color: #880;
+  bottom: 0px;
+  left: 0.5em;
+  height: 2.5em;
+  width: 3em;
+  transform: rotate(0deg) skewX(0deg);
+}
+
+input:checked + .switch-left::before {
+  background-color: transparent;
+  width: 3.0833em;
+}
+
+input:checked + .switch-left + .switch-right {
+  background-color: #ddd;
+  color: #888;
+  bottom: 0.4em;
+  right: 0.8em;
+  height: 2.4em;
+  width: 2.75em;
+  transform: rotate(-15deg) skewX(-15deg);
+}
+
+input:checked + .switch-left + .switch-right::before {
+  background-color: #ccc;
+}
+
+/* Keyboard Users */
+/*
+input:focus + .switch-left {
+  color: #333;
+}
+
+input:checked:focus + .switch-left {
+  color: #fff;
+}
+
+input:focus + .switch-left + .switch-right {
+  color: #fff;
+}
+
+input:checked:focus + .switch-left + .switch-right {
+  color: #333;
+}
+*/
diff --git a/ui-images/zulip-logo.png b/ui-images/zulip-logo.png
new file mode 100644
index 0000000000000000000000000000000000000000..842091c933411298c360d02a54240edb40cb9fac
Binary files /dev/null and b/ui-images/zulip-logo.png differ
diff --git a/ui-js/addon/company-coq.js b/ui-js/addon/company-coq.js
index 7596ce4698e5dda914d4c3ecadf35a9b2ab00e2f..acf2fc3b80db55d4955eba421fc406717b4ab9fb 100644
--- a/ui-js/addon/company-coq.js
+++ b/ui-js/addon/company-coq.js
@@ -41,8 +41,19 @@ class Markup {
     attach(cm) {
         this.cm = cm;
         this.work = new WorkQueue(cm);
-        cm.on('change', (cm, change_obj) => this._flush(change_obj));
-        cm.on('renderLine', (cm, ln, el) => this._rescan(ln));
+        this._on = {
+            change: (cm, change_obj) => this._flush(change_obj),
+            renderLine: (cm, ln, el) => this._rescan(ln)
+        };
+        cm.on('change', this._on.change);
+        cm.on('renderLine', this._on.renderLine);
+        this.applyToDocument();
+    }
+
+    detach() {
+        this.clearAll();
+        this.cm.off('change', this._on.change);
+        this.cm.off('renderLine', this._on.renderLine);
     }
 
     applyToDocument() {
@@ -87,6 +98,11 @@ class Markup {
             this.cm.display.maxLine.lineNo() === line) this._maxLineHack();
     }
 
+    clearAll() {
+        for (let mark of this.cm.getAllMarks().filter(m => m.owner == this))
+            mark.clear();
+    }
+
     clearFromLine(line) {
         var from = {line, ch: 0},
             to = {line, ch: this.cm.getLine(line).length};
@@ -226,6 +242,9 @@ class WorkQueue {
         this.tasks.push(task); 
         if (!this.primed) this._engage();
     }
+    clear() {
+        this.tasks.splice();
+    }
     _engage() {
         this.primed = true;
         requestAnimationFrame(() => this.cm.operation(() => {
@@ -254,7 +273,13 @@ class AutoComplete {
     }
 
     attach(cm) {
-        cm.on('change', (cm, evt) => this.senseContext(cm, evt));
+        this.cm = cm;
+        this._on = {change: (cm, evt) => this.senseContext(cm, evt)};
+        cm.on('change', this._on.change);
+    }
+
+    detach() {
+        this.cm.off('change', this._on.change);
     }
 
     lemmaHint(cm, options) { return this.hint(cm, options, ['keywords', 'lemmas']); }
@@ -414,7 +439,13 @@ class ObserveIdentifier {
     }
 
     attach(cm) {
-        cm.on('cursorActivity', (cm, evt) => this.underCursor(cm));
+        this.cm = cm;
+        this._on = {cursorActivity: (cm, evt) => this.underCursor(cm)};
+        cm.on('cursorActivity', this._on.cursorActivity);
+    }
+
+    detach() {
+        this.cm.off('cursorActivity', this._on.cursorActivity);
     }
 
     underCursor(cm) {
@@ -547,6 +578,14 @@ class CompanyCoq {
         return this;
     }
 
+    detach() {
+        this.markup.detach();
+        this.completion.detach();
+        this.observe.detach();
+
+        return this;
+    }
+
     static hint(cm, options) {
         if (cm.company_coq)
             return cm.company_coq.completion.getCompletions(cm, options);
@@ -578,15 +617,28 @@ class CompanyCoq {
         ObserveIdentifier.instance.vocab = vocab;
         CodeMirror.CompanyCoq = CompanyCoq;
 
-        CodeMirror.defineInitHook(cm => {
-            if (cm.options.mode['company-coq'])
-                cm.company_coq = new CompanyCoq().attach(cm);
-        });
+        CodeMirror.defineInitHook(cm =>
+            CompanyCoq.configure(cm, cm.options));
 
         CodeMirror.registerGlobalHelper("hint", "company-coq",
             (mode, cm) => mode.name === 'coq' && !!cm.company_coq, 
             CompanyCoq.hint);
     }
+
+    static configure(cm, options) {
+        switch (options.mode && options.mode['company-coq']) {
+        case true:
+            if (!cm.company_coq)
+                cm.company_coq = new CompanyCoq().attach(cm);
+            break;
+        case false:
+            if (cm.company_coq) {
+                cm.company_coq.detach();
+                cm.company_coq = undefined;
+            }
+            break;
+        }
+    }
 }
 
 
diff --git a/ui-js/cm-provider.js b/ui-js/cm-provider.js
index 00a9130b52796af5711f3b3e1cf8336df8f3c82d..0870072410eb18663662c338f88b4a3f8a284ca8 100644
--- a/ui-js/cm-provider.js
+++ b/ui-js/cm-provider.js
@@ -106,6 +106,13 @@ class CmCoqProvider {
         return editor;
     }
 
+    configure(options) {
+        if (options.theme) {
+            this.editor.setOption('theme', options.theme);
+        }
+        CodeMirror.CompanyCoq.configure(this.editor, options);
+    }
+
     trackLineCount() {
         this.lineCount = this.editor.lineCount();
         this.editor.on('change', ev => {
diff --git a/ui-js/coq-layout-classic.js b/ui-js/coq-layout-classic.js
index d9828977d9a653f1e9cfccf6a08f39352d205667..c992f511cf24bb7b1c0b1c74ca2d7748364d1f63 100644
--- a/ui-js/coq-layout-classic.js
+++ b/ui-js/coq-layout-classic.js
@@ -47,7 +47,11 @@ class CoqLayoutClassic {
         <button name="reset"       alt="Reset worker"               title="Reset worker"></button>
       </span>
       <div class="exits right">
-        <a href="https://github.com/jscoq/jscoq" class="link-to-github"></a>
+        <svg class="app-menu-button" viewBox="0 0 80 80">
+            <circle cx="40" cy="24" r="5"></circle>
+            <circle cx="40" cy="40" r="5"></circle>
+            <circle cx="40" cy="56" r="5"></circle>
+        </svg>    
       </div> <!-- /.exits -->
     </div> <!-- /#toolbar -->
     <div class="flex-container">
@@ -97,9 +101,6 @@ class CoqLayoutClassic {
         this.panel.id = 'panel-wrapper';
         this.panel.innerHTML = this.html({base_path: options.base_path, ...params});
 
-        if (options.theme)
-            this.panel.classList.add(`jscoq-theme-${options.theme}`);
-
         this.ide.appendChild(this.panel);
 
         // UI setup.
@@ -107,6 +108,8 @@ class CoqLayoutClassic {
         this.query    = this.panel.querySelector('#query-panel');
         this.packages = this.panel.querySelector('#packages-panel');
         this.buttons  = this.panel.querySelector('#buttons');
+        this.menubtn  = this.panel.querySelector('.app-menu-button');
+        this.settings = new SettingsPanel();
 
         var flex_container = this.panel.getElementsByClassName('flex-container')[0];
         flex_container.addEventListener('click', evt => { this.panelClickHandler(evt); });
@@ -120,15 +123,39 @@ class CoqLayoutClassic {
         this.onAction = evt => {};
         this.buttons.addEventListener('click', evt => this.onAction(evt));
 
+        this.menubtn.addEventListener('mousedown', () =>
+            this.settings.toggle());
+        this.settings.active.observe(active =>
+            this.menubtn.classList.toggle('active', active));
+
         // Configure log
         this.log_levels = ['Error', 'Warning', 'Notice', 'Info', 'Debug']
         $(this.panel).find('select[name=msg_filter]')
-            .change(ev => this.filterLog(parseInt(ev.target.value)));
+            .on('change', ev => this.filterLog(parseInt(ev.target.value)));
         this.filterLog(3); // Info
 
+        this.configure(options);
+
+        this._setupSettings();
         this._preloadImages();
     }
 
+    /**
+     * Configure or re-configure UI based on CoqManager options.
+     * @param {object} options 
+     */
+    configure(options) {
+        if (options.theme) {
+            this.panel.classList.remove(...this.panel.classList);
+            this.panel.classList.add(`jscoq-theme-${options.theme}`);
+        }
+        this.settings.configure({
+            theme: options.theme,
+            company: options.editor && options.editor.mode &&
+                     options.editor.mode['company-coq']
+        });
+    }
+
     show() {
         this.ide.classList.add('goals-active');
         this.ide.classList.remove('toggled');
@@ -182,13 +209,6 @@ class CoqLayoutClassic {
         return this.outline = outline_pane[0];
     }
 
-    createOutline() {
-        var outline_pane = $('<div>').attr('id', 'outline-pane');
-        $(this.ide).prepend(outline_pane);
-        requestAnimationFrame(() => $(this.ide).addClass('outline-active'));
-        return this.outline = outline_pane[0];
-    }
-
     /**
      * Shows a notice in the main goal pane (reserved for important messages,
      * such as during startup).
@@ -331,6 +351,15 @@ class CoqLayoutClassic {
         }
     }
 
+    /**
+     * Set up hooks for when user changes settings.
+     */
+    _setupSettings() {
+        this.settings.model.theme.observe(theme => {
+            this.configure({theme});
+        });
+    }
+
     /**
      * Auxiliary function to improve UX by preloading images.
      */
diff --git a/ui-js/coq-manager.js b/ui-js/coq-manager.js
index 527d812a5cb84d2bc8bc6f1c72540cd0136435c2..5fdffae27d3b8628dedd5fec988aba8656c6c631 100644
--- a/ui-js/coq-manager.js
+++ b/ui-js/coq-manager.js
@@ -148,6 +148,11 @@ class ProviderContainer {
         return new Promise(resolve => setTimeout(resolve, 0));
     }
 
+    configure(options) {
+        for (let snippet of this.snippets)
+            snippet.configure(options);
+    }
+
     // Get the next candidate and mark it.
     getNext(prev, until) {
 
@@ -294,7 +299,7 @@ class CoqManager {
         }
 
         // Setup the Coq statement provider.
-        this.provider = this.setupProvider(elems);
+        this.provider = this._setupProvider(elems);
 
         // Setup the Panel UI.
         this.layout = new CoqLayoutClassic(this.options, {kb: this.keyTooltips()});
@@ -306,7 +311,8 @@ class CoqManager {
             if (this.coq) this.layout.onToggle = () => {};
         };
 
-        this.setupDragDrop();
+        this._setupSettings();
+        this._setupDragDrop();
 
         // Setup pretty printer for feedback and goals
         this.pprint = new FormatPrettyPrint();
@@ -351,7 +357,7 @@ class CoqManager {
     }
 
     // Provider setup
-    setupProvider(elems) {
+    _setupProvider(elems) {
 
         var provider = new ProviderContainer(elems, this.options);
 
@@ -394,7 +400,23 @@ class CoqManager {
         return provider;
     }
 
-    setupDragDrop() {
+    /**
+     * Set up hooks for when user changes settings.
+     */
+     _setupSettings() {
+        const editorThemes = {'light': 'default', 'dark': 'blackboard'};
+        this.layout.settings.model.theme.observe(theme => {
+            /* this might take some time (do async like renumber?) */
+            this.provider.configure({theme: editorThemes[theme]});
+        });
+        this.layout.settings.model.company.observe(enable => {
+            this.provider.configure({mode: {'company-coq': enable}});
+            this.company_coq = this.contextual_info.company_coq =
+                enable ? new CodeMirror.CompanyCoq() : undefined;
+        });
+    }
+
+    _setupDragDrop() {
         $(this.layout.ide).on('dragover', (evt) => {
             evt.preventDefault();
             evt.originalEvent.dataTransfer.dropEffect = 'link';
diff --git a/ui-js/jscoq-loader.js b/ui-js/jscoq-loader.js
index 87434c5b261938963d4dbd71f1e04f9fef39c195..deed1882a88ab38e4e39390dd982e73cd028b0ec 100644
--- a/ui-js/jscoq-loader.js
+++ b/ui-js/jscoq-loader.js
@@ -65,6 +65,7 @@ var loadJsCoq, JsCoq;
         loadCss(base_path + 'ui-css/coq-base');
         loadCss(base_path + 'ui-css/coq-light');
         loadCss(base_path + 'ui-css/coq-dark');
+        loadCss(base_path + 'ui-css/settings');
 
         var files = [node_modules_path + 'codemirror/lib/codemirror',
                      node_modules_path + 'codemirror/keymap/emacs',
@@ -81,6 +82,7 @@ var loadJsCoq, JsCoq;
                      base_path + 'ui-js/addon/company-coq',
                      base_path + 'ui-js/cm-provider',
                      base_path + 'ui-js/format-pprint',
+                     base_path + 'ui-js/settings',
                      base_path + 'ui-js/coq-packages',
                      base_path + 'ui-js/coq-layout-classic',
                      base_path + 'ui-js/coq-manager'];
diff --git a/ui-js/settings.js b/ui-js/settings.js
new file mode 100644
index 0000000000000000000000000000000000000000..ce92c665a19174050edb3fb1f019f929996371fe
--- /dev/null
+++ b/ui-js/settings.js
@@ -0,0 +1,116 @@
+
+class SettingsPanel {
+    html() {
+        return `
+        <div class="settings-panel" tabindex="0">
+            <div>
+                <label for="settings--theme">
+                    <div class="setting">
+                        Light switch
+                        <div class="switch rocker rocker-small">
+                            <input id="settings--theme" type="checkbox" checked>
+                            <span class="switch-left">ON</span>
+                            <span class="switch-right">OFF</span>
+                        </div>
+                    </div>
+                </label>
+                <label for="settings--company">
+                    <div class="setting">
+                        Enable company-coq
+                        <input id="settings--company" type="checkbox" class="switch">
+                    </div>
+                </label>
+            </div>
+            <div class="links">
+                <a href="https://coq.zulipchat.com/#narrow/stream/256336-jsCoq" title="Zulip" class="link-to-zulip"></a>
+                <a href="https://github.com/jscoq/jscoq" title="GitHub" class="link-to-github"></a>
+            </div>
+        </div>
+        `;
+    }
+
+    constructor(model) {
+        this.el = $(this.html());
+        this.model = model || new CoqSettings();
+        this.active = new ReactiveVar(false);
+
+        this.el.find('#settings--theme').on('change', ev => {
+            this.model.theme.value = ev.target.checked ? 'light' : 'dark';
+        });
+        this.el.find('#settings--company').on('change', ev => {
+            this.model.company.value = ev.target.checked;
+        });
+        // clickaway trick
+        this.el.on('blur', ev => {
+            if (this.el.has(ev.originalEvent.relatedTarget).length)
+                setTimeout(() => this.el[0].focus(), 1);
+            else
+                this.hide();
+        });
+    }
+
+    configure(options) {
+        var v;
+        if (v = options.theme) {
+            this.el.attr('data-theme', v);
+            this.model.theme.value = v;
+            this.el.find('#settings--theme').attr('checked', v == 'light');
+        }
+        if ((v = options.company) !== undefined) {
+            this.model.theme.company = v;
+            this.el.find('#settings--company').attr('checked', v);
+        }
+    }
+
+    show() {
+        if (this.el.parent().length == 0)
+            $(document.body).append(this.el);
+        this.el.show();
+        this.active.value = true;
+        setTimeout(() => this.el[0].focus(), 10); // avoid race between panel and button
+    }
+
+    hide() {
+        this.active.value = false;
+        this.el.hide();
+    }
+
+    toggle() {
+        if (this.isVisible()) this.hide();
+        else this.show();
+        return this.isVisible();
+    }
+
+    isVisible() {
+        return this.el.is(':visible');
+    }
+}
+
+
+class CoqSettings {
+    constructor() {
+        this.theme = new ReactiveVar('light');
+        this.company = new ReactiveVar(true);
+    }
+}
+
+class ReactiveVar {
+    constructor(value) {
+        this._value = value;
+        this.observers = [];
+    }
+
+    get value() { return this._value; }
+    set value(val) {
+        if (val !== this._value) {  // do not trigger for nothing
+            this._value = val;
+            for (let o of this.observers) {
+                o(val);
+            }
+        }
+    }
+
+    observe(callback) {
+        this.observers.push(callback);
+    }
+}
\ No newline at end of file