diff --git a/.gitignore b/.gitignore
index b512c09..b3aed83 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,3 @@
-node_modules
\ No newline at end of file
+node_modules
+rename.sh
+data-persistence.json
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 0000000..ab42783
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,17 @@
+{
+    // Use IntelliSense to learn about possible attributes.
+    // Hover to view descriptions of existing attributes.
+    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+    "version": "0.2.0",
+    "configurations": [
+        {
+            "type": "pwa-node",
+            "request": "launch",
+            "name": "Launch Program",
+            "skipFiles": [
+                "/**"
+            ],
+            "program": "${workspaceFolder}/index.js"
+        }
+    ]
+}
\ No newline at end of file
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..0a04128
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,165 @@
+                   GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.  
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+  This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+  0. Additional Definitions.
+
+  As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+  "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+  An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+  A "Combined Work" is a work produced by combining or linking an
+Application with the Library.  The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+  The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+  The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+  1. Exception to Section 3 of the GNU GPL.
+
+  You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+  2. Conveying Modified Versions.
+
+  If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+   a) under this License, provided that you make a good faith effort to
+   ensure that, in the event an Application does not supply the
+   function or data, the facility still operates, and performs
+   whatever part of its purpose remains meaningful, or
+
+   b) under the GNU GPL, with none of the additional permissions of
+   this License applicable to that copy.
+
+  3. Object Code Incorporating Material from Library Header Files.
+
+  The object code form of an Application may incorporate material from
+a header file that is part of the Library.  You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+   a) Give prominent notice with each copy of the object code that the
+   Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the object code with a copy of the GNU GPL and this license
+   document.
+
+  4. Combined Works.
+
+  You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+   a) Give prominent notice with each copy of the Combined Work that
+   the Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the Combined Work with a copy of the GNU GPL and this license
+   document.
+
+   c) For a Combined Work that displays copyright notices during
+   execution, include the copyright notice for the Library among
+   these notices, as well as a reference directing the user to the
+   copies of the GNU GPL and this license document.
+
+   d) Do one of the following:
+
+       0) Convey the Minimal Corresponding Source under the terms of this
+       License, and the Corresponding Application Code in a form
+       suitable for, and under terms that permit, the user to
+       recombine or relink the Application with a modified version of
+       the Linked Version to produce a modified Combined Work, in the
+       manner specified by section 6 of the GNU GPL for conveying
+       Corresponding Source.
+
+       1) Use a suitable shared library mechanism for linking with the
+       Library.  A suitable mechanism is one that (a) uses at run time
+       a copy of the Library already present on the user's computer
+       system, and (b) will operate properly with a modified version
+       of the Library that is interface-compatible with the Linked
+       Version.
+
+   e) Provide Installation Information, but only if you would otherwise
+   be required to provide such information under section 6 of the
+   GNU GPL, and only to the extent that such information is
+   necessary to install and execute a modified version of the
+   Combined Work produced by recombining or relinking the
+   Application with a modified version of the Linked Version. (If
+   you use option 4d0, the Installation Information must accompany
+   the Minimal Corresponding Source and Corresponding Application
+   Code. If you use option 4d1, you must provide the Installation
+   Information in the manner specified by section 6 of the GNU GPL
+   for conveying Corresponding Source.)
+
+  5. Combined Libraries.
+
+  You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+   a) Accompany the combined library with a copy of the same work based
+   on the Library, uncombined with any other library facilities,
+   conveyed under the terms of this License.
+
+   b) Give prominent notice with the combined library that part of it
+   is a work based on the Library, and explaining where to find the
+   accompanying uncombined form of the same work.
+
+  6. Revised Versions of the GNU Lesser General Public License.
+
+  The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+  Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+  If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/README.MD b/README.MD
index 54bcd5a..05f05d0 100644
--- a/README.MD
+++ b/README.MD
@@ -1,10 +1,5 @@
 # openCountdown
 
 # ToDo
-- [X] Pausing functions
-- [X] Presets
-- [X] time on countdown page
-- [X] one-way messaging
-- [ ] Better UI
-- [X] Progress bar
 - [P] Endpoint docs
+- [ ] Better WS frames
\ No newline at end of file
diff --git a/helpers.js b/helpers.js
new file mode 100644
index 0000000..7ffcad1
--- /dev/null
+++ b/helpers.js
@@ -0,0 +1,32 @@
+/**
+ * Converts a string into a boolean
+ * @param {String} stringBoolean 
+ * @returns Boolean if valid or -1 otherwise
+ */
+function convertStringBooleanToBoolean(stringBoolean) {
+  if (stringBoolean === 'true') {
+    return true;
+  } else if(stringBoolean === 'false') {
+    return false;
+  } else {
+      return(-1)
+  }
+}
+
+/**
+ * Wraps convertStringBooleanToBoolean to be used with express. Will respond with an error if the boolean is invalid. Else dataObj will be set to the converted boolean value.
+ * @param {String} stringBoolean An input string given by the user
+ * @param {*} res Expresses response object
+ * @param {*} dataObj The data to manipulate
+ */
+
+function wrapBooleanConverter(stringBoolean, res) {
+  const temp = convertStringBooleanToBoolean(stringBoolean);
+  if(temp == -1) {
+    res.json({ status: "error", message: "Invalid boolean value" });
+  } else {
+    return(temp);
+  }
+}
+
+module.exports = { convertStringBooleanToBoolean, wrapBooleanConverter };
\ No newline at end of file
diff --git a/index.js b/index.js
index 3597f74..f0a484d 100644
--- a/index.js
+++ b/index.js
@@ -1,7 +1,10 @@
 const express = require("express");
 const fs = require("fs");
 const bodyParser = require("body-parser");
+const ws = require('ws');
+const helper = require("./helpers.js");
 
+console.log("Preparing server...");
 const app = express();
 
 app.use(express.static("static"));
@@ -15,31 +18,72 @@ app.use(
   })
 );
 
+let loadedData = {}
+
+if (fs.existsSync("data-persistence.json")) {
+  const loadedDataRaw = fs.readFileSync("data-persistence.json", "utf8");
+  loadedData = JSON.parse(loadedDataRaw);
+} else {
+  console.warn("Unable to load persistent data");
+}
+
 currentState = {
   mode: "clock",
   countdownGoal: new Date().getTime(),
   showMilliSeconds: true,
   defaultFullScreen: true,
   timeAmountInital: 0,
-  timerRunState: true,
+  timerRunState: false,
   pauseMoment: 0,
   showTimeOnCountdown: true,
   message: "",
   showMessage: false,
   messageAppearTime: 0,
   showProgressbar: true,
-  colorSegments: {20000: "#FFAE00", 5000: "#ff0000", "START": "yellow"},
+  colorSegments: { 40000: "yellow", 20000: "#FFAE00", 5000: "#ff0000", "START": "green" },
   textColors: {},
   srvTime: 0,
   enableColoredText: true,
-  debug: false
+  debug: false,
+  sessionToken: Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15),
 };
 
+const dataToBeWritten = {};
+
+currentState = Object.assign({}, currentState, loadedData);
 currentState.textColors = currentState.colorSegments
 
 
+
+const wsServer = new ws.Server({ noServer: true });
+wsServer.on('connection', socket => {
+  socket.on('message', function incoming(data) {
+    if (data.toString() == "new client") {
+      updatedData()
+    }
+  });
+});
+
+wsServer.broadcast = function broadcast(data) {
+  wsServer.clients.forEach(function each(client) {
+    // The data is coming in correctly
+    // console.log(data);
+    client.send(data);
+  });
+};
+
+let updatey = undefined;
+
+function updatedData() {
+  currentState.srvTime = new Date().getTime()
+  wsServer.broadcast(JSON.stringify(currentState));
+  clearTimeout(updatey);
+  setTimeout(updatedData, 5000);
+}
+
+console.log("Preparing routes...");
 app.get("/", function (req, res) {
-  const data = fs.readFileSync("templates/adminPanel.html", "utf8");
+  const data = fs.readFileSync("templates/newAdminPanel.html", "utf8");
   res.send(data);
 });
 
@@ -53,78 +97,155 @@ app.get("/api/v1/data", function (req, res) {
   res.json(currentState);
 });
 
+app.get("/api/v1/system", function (req, res) {
+  const tempPkgFile = fs.readFileSync("package.json", "utf8");
+  const tempPkgObj = JSON.parse(tempPkgFile);
+  const systemData = {
+    uptime: process.uptime(),
+    memoryUsage: process.memoryUsage(),
+    cpuUsage: process.cpuUsage(),
+    platform: process.platform,
+    arch: process.arch,
+    nodeVersion: process.version,
+    nodePath: process.execPath,
+    nodeArgv: process.argv,
+    nodeExecArgv: process.execArgv,
+    nodeCwd: process.cwd(),
+    nodeEnv: process.env,
+    nodeConfig: process.config,
+    nodeTitle: process.title,
+    systemVersion: tempPkgObj.version
+  }
+  res.json(systemData);
+});
+
+
 app.get("/api/v1/set/mode", function (req, res) {
   currentState.mode = req.query.mode;
+  updatedData()
   res.json({ status: "ok" });
 });
 
 app.get("/api/v1/set/layout/showMillis", function (req, res) {
-  currentState.showMilliSeconds = (req.query.show === 'true');
-  res.json({ status: "ok" });
+  const resy = helper.wrapBooleanConverter(req.query.show, res)
+  if (resy != undefined) {
+    currentState.showMilliSeconds = resy;
+    if (req.query.persist === 'true') {
+      dataToBeWritten.showMilliSeconds = currentState.showMilliSeconds
+    }
+    res.json({ status: "ok" });
+  }
+  updatedData()
+
 });
 
 app.get("/api/v1/set/timerGoal", function (req, res) {
   currentState.countdownGoal = new Date(parseInt(req.query.time)).getTime(); // ToDO error handling
   res.json({ status: "ok" });
+  updatedData()
 });
 
 app.get("/api/v1/set/addMillisToTimer", function (req, res) {
   currentState.timeAmountInital = req.query.time;
   currentState.countdownGoal = new Date().getTime() + parseInt(req.query.time)
+  currentState.pauseMoment = new Date().getTime();
   res.json({ status: "ok" });
-}); 
+  updatedData()
+});
 
 app.get("/api/v1/ctrl/timer/pause", function (req, res) {
   currentState.timerRunState = false;
   currentState.pauseMoment = new Date().getTime();
   res.json({ status: "ok" });
+  updatedData()
 });
 
 app.get("/api/v1/ctrl/timer/play", function (req, res) {
-  currentState.timerRunState = true
-  currentState.countdownGoal += new Date().getTime() - currentState.pauseMoment;
+
+  if (currentState.timerRunState == false) {
+    currentState.timerRunState = true
+    currentState.countdownGoal += new Date().getTime() - currentState.pauseMoment;
+  }
   res.json({ status: "ok" });
+  updatedData()
 });
 
 app.get("/api/v1/ctrl/timer/restart", function (req, res) {
   currentState.countdownGoal = new Date().getTime() + parseInt(currentState.timeAmountInital)
   res.json({ status: "ok" });
+  updatedData()
 });
 
 app.get("/api/v1/set/layout/showTime", function (req, res) {
-  currentState.showTimeOnCountdown = (req.query.show === 'true');
-  res.json({ status: "ok" });
+  const resy = helper.wrapBooleanConverter(req.query.show, res)
+  if (resy != undefined) {
+    currentState.showTimeOnCountdown = resy;
+    if (req.query.persist === 'true') {
+      dataToBeWritten.showTimeOnCountdown = currentState.showTimeOnCountdown
+    }
+    res.json({ status: "ok" });
+  }
+  updatedData()
 });
 
 app.get("/api/v1/set/progressbar/show", function (req, res) {
   currentState.showProgressbar = (req.query.show === 'true');
+  if (req.query.persist === 'true') {
+    dataToBeWritten.showProgressbar = currentState.showProgressbar
+  }
   res.json({ status: "ok" });
+  updatedData()
 });
 
 app.get("/api/v1/set/progressbar/colors", function (req, res) {
   try {
-    currentState.colorSegments = JSON.parse(req.query.colors);
-    res.json({ status: "ok" });
-  } catch (error) {
-    res.json({ status: "error", message: error });
-  }
-});
-
-app.get("/api/v1/set/text/colors", function (req, res) {
-  try {
-    if(req.query.copy === "true"){
-      currentState.textColors = currentState.colorSegments;
-    } else {
-      currentState.textColors = JSON.parse(req.query.colors);
+    let data = req.query.colors
+    if (req.query.isBase64 === "true") {
+      data = atob(data)
+    }
+    currentState.colorSegments = JSON.parse(data);
+    if (req.query.persist === 'true') {
+      dataToBeWritten.colorSegments = currentState.colorSegments
     }
     res.json({ status: "ok" });
   } catch (error) {
     res.json({ status: "error", message: error });
+    console.error(error)
   }
+  updatedData()
+});
+
+app.get("/api/v1/set/text/colors", function (req, res) {
+  try {
+    if (req.query.copy === "true") {
+      currentState.textColors = currentState.colorSegments
+      res.json({ status: "ok" });
+    } else {
+      let data = req.query.colors
+      if (req.query.isBase64 === "true") {
+        data = atob(data)
+      }
+      console.debug(data)
+      currentState.textColors = JSON.parse(data);
+      if (req.query.persist === 'true') {
+        dataToBeWritten.textColors = currentState.textColors
+      }
+    }
+    res.json({ status: "ok" });
+  } catch (error) {
+    res.json({ status: "error", message: error });
+    console.error(error)
+  }
+  updatedData()
 });
 
 app.get("/api/v1/set/text/enableColoring", function (req, res) {
   currentState.enableColoredText = (req.query.enable === 'true');
+  if (req.query.persist === 'true') {
+    dataToBeWritten.enableColoredText = currentState.enableColoredText
+  }
+  res.json({ status: "ok" });
+  updatedData()
 });
 
 app.get("/api/v1/ctrl/message/show", function (req, res) {
@@ -132,16 +253,82 @@ app.get("/api/v1/ctrl/message/show", function (req, res) {
   currentState.showMessage = true
   currentState.messageAppearTime = new Date().getTime()
   res.json({ status: "ok" });
+  updatedData()
 });
 
 app.get("/api/v1/debug", function (req, res) {
   currentState.debug = (req.query.enable === 'true');
   res.json({ status: "ok" });
+  updatedData()
 });
 
 app.get("/api/v1/ctrl/message/hide", function (req, res) {
   currentState.showMessage = false
   res.json({ status: "ok" });
+  updatedData()
 });
 
-app.listen(3005);
+app.get("/api/v1/storage/commit", function (req, res) {
+  const tempString = JSON.stringify(dataToBeWritten);
+  try {
+    fs.writeFileSync("data-persistence.json", tempString);
+    res.json({ status: "ok" });
+  } catch (error) {
+    res.json({ status: "error", reason: error });
+  }
+  updatedData()
+});
+
+
+app.get("/api/v1/storage/delete", function (req, res) {
+  if (req.query.delete === "true") {
+    if (fs.existsSync("data-persistence.json")) {
+      fs.unlinkSync("data-persistence.json");
+      res.json({ status: "ok" });
+    } else {
+      res.json({ status: "error", reason: "No persistence data was found" });
+    }
+
+  } else {
+
+  } res.json({ status: "error", reason: "Missing delete argument" });
+  updatedData()
+});
+
+app.use(function(req, res, next) {
+  res.status(404);
+
+  // respond with html page
+  if (req.accepts('html')) {
+    const data = fs.readFileSync("templates/errorPages/404.html", "utf8");
+    res.status(404)
+    res.send(data);
+    return;
+  }
+
+  // respond with json
+  if (req.accepts('json')) {
+    res.json({ error: 'Not found' });
+    return;
+  }
+
+  // default to plain-text. send()
+  res.type('txt').send('Not found');
+});
+
+
+
+console.log("Starting server...");
+const port = 3005
+const server = app.listen(port);
+server.on('upgrade', (request, socket, head) => {
+  wsServer.handleUpgrade(request, socket, head, socket => {
+    wsServer.emit('connection', socket, request);
+  });
+});
+
+
+
+console.info("Server running on port " + port);
+console.info("Visit localhost:" + port + "/timer for the timer page");
+console.info("Visit localhost:" + port + " for the admin page");
diff --git a/package-lock.json b/package-lock.json
index e86fe82..0f8f883 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,17 +1,36 @@
 {
   "name": "opencountdown",
-  "version": "1.0.0",
+  "version": "1.0.1",
   "lockfileVersion": 2,
   "requires": true,
   "packages": {
     "": {
       "name": "opencountdown",
-      "version": "1.0.0",
-      "license": "ISC",
+      "version": "1.0.1",
+      "license": "LGPL-3.0",
       "dependencies": {
         "body-parser": "^1.19.2",
-        "countdown": "^2.6.0",
-        "express": "^4.17.3"
+        "bootstrap": "^5.1.3",
+        "bootstrap-duration-picker": "^2.1.3",
+        "bootstrap-icons": "^1.8.1",
+        "darkreader": "^4.9.44",
+        "express": "^4.17.3",
+        "flatpickr": "^4.6.11",
+        "jquery": "^3.6.0",
+        "js-cookie": "^3.0.1",
+        "less": "^3.13",
+        "mdbootstrap": "^4.20.0",
+        "ws": "^8.5.0"
+      }
+    },
+    "node_modules/@popperjs/core": {
+      "version": "2.11.2",
+      "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.2.tgz",
+      "integrity": "sha512-92FRmppjjqz29VMJ2dn+xdyXZBrMlE42AV6Kq6BwjWV7CNUW1hs2FtxSNLQE+gJhaZ6AAmYuO9y8dshhcBl7vA==",
+      "peer": true,
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/popperjs"
       }
     },
     "node_modules/accepts": {
@@ -51,6 +70,31 @@
         "node": ">= 0.8"
       }
     },
+    "node_modules/bootstrap": {
+      "version": "5.1.3",
+      "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.1.3.tgz",
+      "integrity": "sha512-fcQztozJ8jToQWXxVuEyXWW+dSo8AiXWKwiSSrKWsRB/Qt+Ewwza+JWoLKiTuQLaEPhdNAJ7+Dosc9DOIqNy7Q==",
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/bootstrap"
+      },
+      "peerDependencies": {
+        "@popperjs/core": "^2.10.2"
+      }
+    },
+    "node_modules/bootstrap-duration-picker": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/bootstrap-duration-picker/-/bootstrap-duration-picker-2.1.3.tgz",
+      "integrity": "sha1-vxctw2psm4lQBpYqLYGIwzYPsmU="
+    },
+    "node_modules/bootstrap-icons": {
+      "version": "1.8.1",
+      "resolved": "https://registry.npmjs.org/bootstrap-icons/-/bootstrap-icons-1.8.1.tgz",
+      "integrity": "sha512-IXUqislddPJfwq6H+2nTkHyr9epO9h6u1AG0OZCx616w+TgzeoCjfmI3qJMQqt1J586gN2IxzB4M99Ip4sTZ1w==",
+      "engines": {
+        "node": ">=10"
+      }
+    },
     "node_modules/bytes": {
       "version": "3.1.2",
       "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
@@ -91,10 +135,25 @@
       "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
       "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
     },
-    "node_modules/countdown": {
-      "version": "2.6.0",
-      "resolved": "https://registry.npmjs.org/countdown/-/countdown-2.6.0.tgz",
-      "integrity": "sha1-Z3+446nUzE52QVkBuiU7UYrzQXc="
+    "node_modules/copy-anything": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz",
+      "integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==",
+      "dependencies": {
+        "is-what": "^3.14.1"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/mesqueeb"
+      }
+    },
+    "node_modules/darkreader": {
+      "version": "4.9.44",
+      "resolved": "https://registry.npmjs.org/darkreader/-/darkreader-4.9.44.tgz",
+      "integrity": "sha512-Mrt5s5eMaFC24Tfi2nNsXpYIfzWK/RUzJxgk0Ysqokvk9lBCn/pkUTyQGYeQbU/U3aKZEYXXqsxZjNfiLoA6QQ==",
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/darkreader/donate"
+      }
     },
     "node_modules/debug": {
       "version": "2.6.9",
@@ -130,6 +189,18 @@
         "node": ">= 0.8"
       }
     },
+    "node_modules/errno": {
+      "version": "0.1.8",
+      "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz",
+      "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==",
+      "optional": true,
+      "dependencies": {
+        "prr": "~1.0.1"
+      },
+      "bin": {
+        "errno": "cli.js"
+      }
+    },
     "node_modules/escape-html": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
@@ -200,6 +271,11 @@
         "node": ">= 0.8"
       }
     },
+    "node_modules/flatpickr": {
+      "version": "4.6.11",
+      "resolved": "https://registry.npmjs.org/flatpickr/-/flatpickr-4.6.11.tgz",
+      "integrity": "sha512-/rnbE/hu5I5zndLEyYfYvqE4vPDvI5At0lFcQA5eOPfjquZLcQ0HMKTL7rv5/+DvbPM3/vJcXpXjB/DjBh+1jw=="
+    },
     "node_modules/forwarded": {
       "version": "0.2.0",
       "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
@@ -216,6 +292,12 @@
         "node": ">= 0.6"
       }
     },
+    "node_modules/graceful-fs": {
+      "version": "4.2.9",
+      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz",
+      "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==",
+      "optional": true
+    },
     "node_modules/http-errors": {
       "version": "1.8.1",
       "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz",
@@ -242,6 +324,18 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/image-size": {
+      "version": "0.5.5",
+      "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz",
+      "integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=",
+      "optional": true,
+      "bin": {
+        "image-size": "bin/image-size.js"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
     "node_modules/inherits": {
       "version": "2.0.4",
       "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
@@ -255,6 +349,66 @@
         "node": ">= 0.10"
       }
     },
+    "node_modules/is-what": {
+      "version": "3.14.1",
+      "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz",
+      "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA=="
+    },
+    "node_modules/jquery": {
+      "version": "3.6.0",
+      "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.0.tgz",
+      "integrity": "sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw=="
+    },
+    "node_modules/js-cookie": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.1.tgz",
+      "integrity": "sha512-+0rgsUXZu4ncpPxRL+lNEptWMOWl9etvPHc/koSRp6MPwpRYAhmk0dUG00J4bxVV3r9uUzfo24wW0knS07SKSw==",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/less": {
+      "version": "3.13.1",
+      "resolved": "https://registry.npmjs.org/less/-/less-3.13.1.tgz",
+      "integrity": "sha512-SwA1aQXGUvp+P5XdZslUOhhLnClSLIjWvJhmd+Vgib5BFIr9lMNlQwmwUNOjXThF/A0x+MCYYPeWEfeWiLRnTw==",
+      "dependencies": {
+        "copy-anything": "^2.0.1",
+        "tslib": "^1.10.0"
+      },
+      "bin": {
+        "lessc": "bin/lessc"
+      },
+      "engines": {
+        "node": ">=6"
+      },
+      "optionalDependencies": {
+        "errno": "^0.1.1",
+        "graceful-fs": "^4.1.2",
+        "image-size": "~0.5.0",
+        "make-dir": "^2.1.0",
+        "mime": "^1.4.1",
+        "native-request": "^1.0.5",
+        "source-map": "~0.6.0"
+      }
+    },
+    "node_modules/make-dir": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
+      "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
+      "optional": true,
+      "dependencies": {
+        "pify": "^4.0.1",
+        "semver": "^5.6.0"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/mdbootstrap": {
+      "version": "4.20.0",
+      "resolved": "https://registry.npmjs.org/mdbootstrap/-/mdbootstrap-4.20.0.tgz",
+      "integrity": "sha512-eIxaYsvHct2BKqkJWphh6j7nFQXHh9NjbbN7a+dctfrVsKaydNW5XpKIxmq2tDSb9+XYGm9hK7hMTlHgeV6wdw=="
+    },
     "node_modules/media-typer": {
       "version": "0.3.0",
       "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
@@ -311,6 +465,12 @@
       "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
       "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
     },
+    "node_modules/native-request": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/native-request/-/native-request-1.1.0.tgz",
+      "integrity": "sha512-uZ5rQaeRn15XmpgE0xoPL8YWqcX90VtCFglYwAgkvKM5e8fog+vePLAhHxuuv/gRkrQxIeh5U3q9sMNUrENqWw==",
+      "optional": true
+    },
     "node_modules/negotiator": {
       "version": "0.6.3",
       "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
@@ -343,6 +503,15 @@
       "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
       "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
     },
+    "node_modules/pify": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
+      "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
+      "optional": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
     "node_modules/proxy-addr": {
       "version": "2.0.7",
       "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
@@ -355,6 +524,12 @@
         "node": ">= 0.10"
       }
     },
+    "node_modules/prr": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
+      "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=",
+      "optional": true
+    },
     "node_modules/qs": {
       "version": "6.9.7",
       "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz",
@@ -412,6 +587,15 @@
       "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
       "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
     },
+    "node_modules/semver": {
+      "version": "5.7.1",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+      "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+      "optional": true,
+      "bin": {
+        "semver": "bin/semver"
+      }
+    },
     "node_modules/send": {
       "version": "0.17.2",
       "resolved": "https://registry.npmjs.org/send/-/send-0.17.2.tgz",
@@ -459,6 +643,15 @@
       "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
       "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
     },
+    "node_modules/source-map": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+      "optional": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
     "node_modules/statuses": {
       "version": "1.5.0",
       "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
@@ -475,6 +668,11 @@
         "node": ">=0.6"
       }
     },
+    "node_modules/tslib": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+      "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
+    },
     "node_modules/type-is": {
       "version": "1.6.18",
       "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
@@ -510,9 +708,35 @@
       "engines": {
         "node": ">= 0.8"
       }
+    },
+    "node_modules/ws": {
+      "version": "8.5.0",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz",
+      "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==",
+      "engines": {
+        "node": ">=10.0.0"
+      },
+      "peerDependencies": {
+        "bufferutil": "^4.0.1",
+        "utf-8-validate": "^5.0.2"
+      },
+      "peerDependenciesMeta": {
+        "bufferutil": {
+          "optional": true
+        },
+        "utf-8-validate": {
+          "optional": true
+        }
+      }
     }
   },
   "dependencies": {
+    "@popperjs/core": {
+      "version": "2.11.2",
+      "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.2.tgz",
+      "integrity": "sha512-92FRmppjjqz29VMJ2dn+xdyXZBrMlE42AV6Kq6BwjWV7CNUW1hs2FtxSNLQE+gJhaZ6AAmYuO9y8dshhcBl7vA==",
+      "peer": true
+    },
     "accepts": {
       "version": "1.3.8",
       "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
@@ -544,6 +768,22 @@
         "type-is": "~1.6.18"
       }
     },
+    "bootstrap": {
+      "version": "5.1.3",
+      "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.1.3.tgz",
+      "integrity": "sha512-fcQztozJ8jToQWXxVuEyXWW+dSo8AiXWKwiSSrKWsRB/Qt+Ewwza+JWoLKiTuQLaEPhdNAJ7+Dosc9DOIqNy7Q==",
+      "requires": {}
+    },
+    "bootstrap-duration-picker": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/bootstrap-duration-picker/-/bootstrap-duration-picker-2.1.3.tgz",
+      "integrity": "sha1-vxctw2psm4lQBpYqLYGIwzYPsmU="
+    },
+    "bootstrap-icons": {
+      "version": "1.8.1",
+      "resolved": "https://registry.npmjs.org/bootstrap-icons/-/bootstrap-icons-1.8.1.tgz",
+      "integrity": "sha512-IXUqislddPJfwq6H+2nTkHyr9epO9h6u1AG0OZCx616w+TgzeoCjfmI3qJMQqt1J586gN2IxzB4M99Ip4sTZ1w=="
+    },
     "bytes": {
       "version": "3.1.2",
       "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
@@ -572,10 +812,18 @@
       "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
       "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
     },
-    "countdown": {
-      "version": "2.6.0",
-      "resolved": "https://registry.npmjs.org/countdown/-/countdown-2.6.0.tgz",
-      "integrity": "sha1-Z3+446nUzE52QVkBuiU7UYrzQXc="
+    "copy-anything": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz",
+      "integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==",
+      "requires": {
+        "is-what": "^3.14.1"
+      }
+    },
+    "darkreader": {
+      "version": "4.9.44",
+      "resolved": "https://registry.npmjs.org/darkreader/-/darkreader-4.9.44.tgz",
+      "integrity": "sha512-Mrt5s5eMaFC24Tfi2nNsXpYIfzWK/RUzJxgk0Ysqokvk9lBCn/pkUTyQGYeQbU/U3aKZEYXXqsxZjNfiLoA6QQ=="
     },
     "debug": {
       "version": "2.6.9",
@@ -605,6 +853,15 @@
       "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
       "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
     },
+    "errno": {
+      "version": "0.1.8",
+      "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz",
+      "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==",
+      "optional": true,
+      "requires": {
+        "prr": "~1.0.1"
+      }
+    },
     "escape-html": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
@@ -666,6 +923,11 @@
         "unpipe": "~1.0.0"
       }
     },
+    "flatpickr": {
+      "version": "4.6.11",
+      "resolved": "https://registry.npmjs.org/flatpickr/-/flatpickr-4.6.11.tgz",
+      "integrity": "sha512-/rnbE/hu5I5zndLEyYfYvqE4vPDvI5At0lFcQA5eOPfjquZLcQ0HMKTL7rv5/+DvbPM3/vJcXpXjB/DjBh+1jw=="
+    },
     "forwarded": {
       "version": "0.2.0",
       "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
@@ -676,6 +938,12 @@
       "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
       "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
     },
+    "graceful-fs": {
+      "version": "4.2.9",
+      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz",
+      "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==",
+      "optional": true
+    },
     "http-errors": {
       "version": "1.8.1",
       "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz",
@@ -696,6 +964,12 @@
         "safer-buffer": ">= 2.1.2 < 3"
       }
     },
+    "image-size": {
+      "version": "0.5.5",
+      "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz",
+      "integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=",
+      "optional": true
+    },
     "inherits": {
       "version": "2.0.4",
       "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
@@ -706,6 +980,52 @@
       "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
       "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
     },
+    "is-what": {
+      "version": "3.14.1",
+      "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz",
+      "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA=="
+    },
+    "jquery": {
+      "version": "3.6.0",
+      "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.0.tgz",
+      "integrity": "sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw=="
+    },
+    "js-cookie": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.1.tgz",
+      "integrity": "sha512-+0rgsUXZu4ncpPxRL+lNEptWMOWl9etvPHc/koSRp6MPwpRYAhmk0dUG00J4bxVV3r9uUzfo24wW0knS07SKSw=="
+    },
+    "less": {
+      "version": "3.13.1",
+      "resolved": "https://registry.npmjs.org/less/-/less-3.13.1.tgz",
+      "integrity": "sha512-SwA1aQXGUvp+P5XdZslUOhhLnClSLIjWvJhmd+Vgib5BFIr9lMNlQwmwUNOjXThF/A0x+MCYYPeWEfeWiLRnTw==",
+      "requires": {
+        "copy-anything": "^2.0.1",
+        "errno": "^0.1.1",
+        "graceful-fs": "^4.1.2",
+        "image-size": "~0.5.0",
+        "make-dir": "^2.1.0",
+        "mime": "^1.4.1",
+        "native-request": "^1.0.5",
+        "source-map": "~0.6.0",
+        "tslib": "^1.10.0"
+      }
+    },
+    "make-dir": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
+      "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
+      "optional": true,
+      "requires": {
+        "pify": "^4.0.1",
+        "semver": "^5.6.0"
+      }
+    },
+    "mdbootstrap": {
+      "version": "4.20.0",
+      "resolved": "https://registry.npmjs.org/mdbootstrap/-/mdbootstrap-4.20.0.tgz",
+      "integrity": "sha512-eIxaYsvHct2BKqkJWphh6j7nFQXHh9NjbbN7a+dctfrVsKaydNW5XpKIxmq2tDSb9+XYGm9hK7hMTlHgeV6wdw=="
+    },
     "media-typer": {
       "version": "0.3.0",
       "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
@@ -744,6 +1064,12 @@
       "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
       "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
     },
+    "native-request": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/native-request/-/native-request-1.1.0.tgz",
+      "integrity": "sha512-uZ5rQaeRn15XmpgE0xoPL8YWqcX90VtCFglYwAgkvKM5e8fog+vePLAhHxuuv/gRkrQxIeh5U3q9sMNUrENqWw==",
+      "optional": true
+    },
     "negotiator": {
       "version": "0.6.3",
       "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
@@ -767,6 +1093,12 @@
       "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
       "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
     },
+    "pify": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
+      "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
+      "optional": true
+    },
     "proxy-addr": {
       "version": "2.0.7",
       "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
@@ -776,6 +1108,12 @@
         "ipaddr.js": "1.9.1"
       }
     },
+    "prr": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
+      "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=",
+      "optional": true
+    },
     "qs": {
       "version": "6.9.7",
       "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz",
@@ -807,6 +1145,12 @@
       "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
       "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
     },
+    "semver": {
+      "version": "5.7.1",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+      "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+      "optional": true
+    },
     "send": {
       "version": "0.17.2",
       "resolved": "https://registry.npmjs.org/send/-/send-0.17.2.tgz",
@@ -850,6 +1194,12 @@
       "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
       "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
     },
+    "source-map": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+      "optional": true
+    },
     "statuses": {
       "version": "1.5.0",
       "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
@@ -860,6 +1210,11 @@
       "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
       "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="
     },
+    "tslib": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+      "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
+    },
     "type-is": {
       "version": "1.6.18",
       "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
@@ -883,6 +1238,12 @@
       "version": "1.1.2",
       "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
       "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
+    },
+    "ws": {
+      "version": "8.5.0",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz",
+      "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==",
+      "requires": {}
     }
   }
 }
diff --git a/package.json b/package.json
index 33a91c8..0026266 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "opencountdown",
-  "version": "1.0.0",
+  "version": "1.0.1",
   "description": "An opensource countdown",
   "main": "index.js",
   "scripts": {
@@ -10,7 +10,16 @@
   "license": "LGPL-3.0",
   "dependencies": {
     "body-parser": "^1.19.2",
-    "countdown": "^2.6.0",
-    "express": "^4.17.3"
+    "bootstrap": "^5.1.3",
+    "bootstrap-duration-picker": "^2.1.3",
+    "bootstrap-icons": "^1.8.1",
+    "darkreader": "^4.9.44",
+    "express": "^4.17.3",
+    "flatpickr": "^4.6.11",
+    "jquery": "^3.6.0",
+    "js-cookie": "^3.0.1",
+    "less": "^3.13",
+    "mdbootstrap": "^4.20.0",
+    "ws": "^8.5.0"
   }
 }
diff --git a/static/bootstrap-duration-picker b/static/bootstrap-duration-picker
new file mode 120000
index 0000000..3b83664
--- /dev/null
+++ b/static/bootstrap-duration-picker
@@ -0,0 +1 @@
+../node_modules/bootstrap-duration-picker/dist/
\ No newline at end of file
diff --git a/static/bootstrap/dist b/static/bootstrap/dist
new file mode 120000
index 0000000..8fc3423
--- /dev/null
+++ b/static/bootstrap/dist
@@ -0,0 +1 @@
+../../node_modules/bootstrap/dist
\ No newline at end of file
diff --git a/static/coloris/coloris.min.css b/static/coloris/coloris.min.css
new file mode 100644
index 0000000..1e50bd4
--- /dev/null
+++ b/static/coloris/coloris.min.css
@@ -0,0 +1 @@
+.clr-picker{display:none;flex-wrap:wrap;position:absolute;width:200px;z-index:1000;border-radius:10px;background-color:#fff;justify-content:space-between;box-shadow:0 0 5px rgba(0,0,0,.05),0 5px 20px rgba(0,0,0,.1);-moz-user-select:none;-webkit-user-select:none;user-select:none}.clr-picker.clr-open{display:flex}.clr-gradient{position:relative;width:100%;height:100px;margin-bottom:15px;border-radius:3px 3px 0 0;background-image:linear-gradient(rgba(0,0,0,0),#000),linear-gradient(90deg,#fff,currentColor);cursor:pointer}.clr-marker{position:absolute;width:12px;height:12px;margin:-6px 0 0 -6px;border:1px solid #fff;border-radius:50%;background-color:currentColor;cursor:pointer}.clr-picker input[type=range]::-webkit-slider-runnable-track{width:100%;height:8px}.clr-picker input[type=range]::-webkit-slider-thumb{width:8px;height:8px;-webkit-appearance:none}.clr-picker input[type=range]::-moz-range-track{width:100%;height:8px;border:0}.clr-picker input[type=range]::-moz-range-thumb{width:8px;height:8px;border:0}.clr-hue{background-image:linear-gradient(to right,red 0,#ff0 16.66%,#0f0 33.33%,#0ff 50%,#00f 66.66%,#f0f 83.33%,red 100%)}.clr-alpha,.clr-hue{position:relative;width:calc(100% - 40px);height:8px;margin:5px 20px;border-radius:4px}.clr-alpha span{display:block;height:100%;width:100%;border-radius:inherit;background-image:linear-gradient(90deg,rgba(0,0,0,0),currentColor)}.clr-alpha input,.clr-hue input{position:absolute;width:calc(100% + 16px);height:16px;left:-8px;top:-4px;margin:0;background-color:transparent;opacity:0;cursor:pointer;appearance:none;-webkit-appearance:none}.clr-alpha div,.clr-hue div{position:absolute;width:16px;height:16px;left:0;top:50%;margin-left:-8px;transform:translateY(-50%);border:2px solid #fff;border-radius:50%;background-color:currentColor;box-shadow:0 0 1px #888;pointer-events:none}.clr-alpha div:before{content:'';position:absolute;height:100%;width:100%;left:0;top:0;border-radius:50%;background-color:currentColor}.clr-format{display:none;order:1;width:calc(100% - 40px);margin:0 20px 20px}.clr-segmented{display:flex;position:relative;width:100%;margin:0;padding:0;border:1px solid #ddd;border-radius:15px;box-sizing:border-box;color:#999;font-size:12px}.clr-segmented input,.clr-segmented legend{position:absolute;width:100%;height:100%;margin:0;padding:0;border:0;left:0;top:0;opacity:0;pointer-events:none}.clr-segmented label{flex-grow:1;padding:4px 0;text-align:center;cursor:pointer}.clr-segmented label:first-of-type{border-radius:10px 0 0 10px}.clr-segmented label:last-of-type{border-radius:0 10px 10px 0}.clr-segmented input:checked+label{color:#fff;background-color:#666}.clr-swatches{order:2;width:calc(100% - 32px);margin:0 16px}.clr-swatches div{display:flex;flex-wrap:wrap;padding-bottom:12px;justify-content:center}.clr-swatches button{position:relative;width:20px;height:20px;margin:0 4px 6px 4px;border:0;border-radius:50%;color:inherit;text-indent:-1000px;white-space:nowrap;overflow:hidden;cursor:pointer}.clr-swatches button:after{content:'';display:block;position:absolute;width:100%;height:100%;left:0;top:0;border-radius:inherit;background-color:currentColor;box-shadow:inset 0 0 0 1px rgba(0,0,0,.1)}input.clr-color{order:1;width:calc(100% - 80px);height:32px;margin:15px 20px 20px 0;padding:0 10px;border:1px solid #ddd;border-radius:16px;color:#444;background-color:#fff;font-family:sans-serif;font-size:14px;text-align:center;box-shadow:none}input.clr-color:focus{outline:0;border:1px solid #1e90ff}.clr-clear{display:none;order:2;height:24px;margin:0 20px 20px auto;padding:0 20px;border:0;border-radius:12px;color:#fff;background-color:#666;font-family:inherit;font-size:12px;font-weight:400;cursor:pointer}.clr-preview{position:relative;width:32px;height:32px;margin:15px 0 20px 20px;border:0;border-radius:50%;overflow:hidden;cursor:pointer}.clr-preview:after,.clr-preview:before{content:'';position:absolute;height:100%;width:100%;left:0;top:0;border:1px solid #fff;border-radius:50%}.clr-preview:after{border:0;background-color:currentColor;box-shadow:inset 0 0 0 1px rgba(0,0,0,.1)}.clr-alpha div,.clr-color,.clr-hue div,.clr-marker{box-sizing:border-box}.clr-field{display:inline-block;position:relative;color:transparent}.clr-field button{position:absolute;width:30px;height:100%;right:0;top:50%;transform:translateY(-50%);border:0;color:inherit;text-indent:-1000px;white-space:nowrap;overflow:hidden;pointer-events:none}.clr-field button:after{content:'';display:block;position:absolute;width:100%;height:100%;left:0;top:0;border-radius:inherit;background-color:currentColor;box-shadow:inset 0 0 1px rgba(0,0,0,.5)}.clr-alpha,.clr-alpha div,.clr-field button,.clr-preview:before,.clr-swatches button{background-image:repeating-linear-gradient(45deg,#aaa 25%,transparent 25%,transparent 75%,#aaa 75%,#aaa),repeating-linear-gradient(45deg,#aaa 25%,#fff 25%,#fff 75%,#aaa 75%,#aaa);background-position:0 0,4px 4px;background-size:8px 8px}.clr-marker:focus{outline:0}.clr-keyboard-nav .clr-alpha input:focus+div,.clr-keyboard-nav .clr-hue input:focus+div,.clr-keyboard-nav .clr-marker:focus,.clr-keyboard-nav .clr-segmented input:focus+label{outline:0;box-shadow:0 0 0 2px #1e90ff,0 0 2px 2px #fff}.clr-picker[data-alpha=false] .clr-alpha{display:none}.clr-picker[data-minimal=true]{padding-top:16px}.clr-picker[data-minimal=true] .clr-alpha,.clr-picker[data-minimal=true] .clr-color,.clr-picker[data-minimal=true] .clr-gradient,.clr-picker[data-minimal=true] .clr-hue,.clr-picker[data-minimal=true] .clr-preview{display:none}.clr-dark{background-color:#444}.clr-dark .clr-segmented{border-color:#777}.clr-dark .clr-swatches button:after{box-shadow:inset 0 0 0 1px rgba(255,255,255,.3)}.clr-dark input.clr-color{color:#fff;border-color:#777;background-color:#555}.clr-dark input.clr-color:focus{border-color:#1e90ff}.clr-dark .clr-preview:after{box-shadow:inset 0 0 0 1px rgba(255,255,255,.5)}.clr-dark .clr-alpha,.clr-dark .clr-alpha div,.clr-dark .clr-preview:before,.clr-dark .clr-swatches button{background-image:repeating-linear-gradient(45deg,#666 25%,transparent 25%,transparent 75%,#888 75%,#888),repeating-linear-gradient(45deg,#888 25%,#444 25%,#444 75%,#888 75%,#888)}.clr-picker.clr-polaroid{border-radius:6px;box-shadow:0 0 5px rgba(0,0,0,.1),0 5px 30px rgba(0,0,0,.2)}.clr-picker.clr-polaroid:before{content:'';display:block;position:absolute;width:16px;height:10px;left:20px;top:-10px;border:solid transparent;border-width:0 8px 10px 8px;border-bottom-color:currentColor;box-sizing:border-box;color:#fff;filter:drop-shadow(0 -4px 3px rgba(0,0,0,.1));pointer-events:none}.clr-picker.clr-polaroid.clr-dark:before{color:#444}.clr-picker.clr-polaroid.clr-left:before{left:auto;right:20px}.clr-picker.clr-polaroid.clr-top:before{top:auto;bottom:-10px;transform:rotateZ(180deg)}.clr-polaroid .clr-gradient{width:calc(100% - 20px);height:120px;margin:10px;border-radius:3px}.clr-polaroid .clr-alpha,.clr-polaroid .clr-hue{width:calc(100% - 30px);height:10px;margin:6px 15px;border-radius:5px}.clr-polaroid .clr-alpha div,.clr-polaroid .clr-hue div{box-shadow:0 0 5px rgba(0,0,0,.2)}.clr-polaroid .clr-format{width:calc(100% - 20px);margin:0 10px 15px}.clr-polaroid .clr-swatches{width:calc(100% - 12px);margin:0 6px}.clr-polaroid .clr-swatches div{padding-bottom:10px}.clr-polaroid .clr-swatches button{width:22px;height:22px}.clr-polaroid input.clr-color{width:calc(100% - 60px);margin:10px 10px 15px 0}.clr-polaroid .clr-clear{margin:0 10px 15px auto}.clr-polaroid .clr-preview{margin:10px 0 15px 10px}.clr-picker.clr-large{width:275px}.clr-large .clr-gradient{height:150px}.clr-large .clr-swatches button{width:22px;height:22px}
\ No newline at end of file
diff --git a/static/coloris/coloris.min.js b/static/coloris/coloris.min.js
new file mode 100644
index 0000000..f6a773f
--- /dev/null
+++ b/static/coloris/coloris.min.js
@@ -0,0 +1,6 @@
+/*!
+ * Copyright (c) 2021 Momo Bassit.
+ * Licensed under the MIT License (MIT)
+ * https://github.com/mdbassit/Coloris
+ */
+!function(d,p,s){var h,f,v,c,u,y,i,b,l,g,m,w,k,x,a=p.createElement("canvas").getContext("2d"),E={r:0,g:0,b:0,h:0,s:0,v:0,a:1},L={el:"[data-coloris]",parent:null,theme:"default",themeMode:"light",wrap:!0,margin:2,format:"hex",formatToggle:!1,swatches:[],swatchesOnly:!1,alpha:!0,focusInput:!0,autoClose:!1,clearButton:{show:!1,label:"Clear"},a11y:{open:"Open color picker",close:"Close color picker",marker:"Saturation: {s}. Brightness: {v}.",hueSlider:"Hue slider",alphaSlider:"Opacity slider",input:"Color value field",format:"Color format",swatch:"Color swatch",instruction:"Saturation and brightness selector. Use up, down, left and right arrow keys to select."}};function o(e){if("object"==typeof e)for(var t in e)switch(t){case"el":S(e.el),!1!==e.wrap&&T(e.el);break;case"parent":L.parent=p.querySelector(e.parent),L.parent&&L.parent.appendChild(h);break;case"themeMode":L.themeMode=e.themeMode,"auto"===e.themeMode&&d.matchMedia&&d.matchMedia("(prefers-color-scheme: dark)").matches&&(L.themeMode="dark");case"theme":e.theme&&(L.theme=e.theme),h.className="clr-picker clr-"+L.theme+" clr-"+L.themeMode;break;case"margin":e.margin*=1,L.margin=(isNaN(e.margin)?L:e).margin;break;case"wrap":e.el&&e.wrap&&T(e.el);break;case"formatToggle":N("clr-format").style.display=e.formatToggle?"block":"none",e.formatToggle&&(L.format="auto");break;case"swatches":Array.isArray(e.swatches)&&function(){var a=[];e.swatches.forEach(function(e,t){a.push(''+e+" ")}),a.length&&(N("clr-swatches").innerHTML=""+a.join("")+"
")}();break;case"swatchesOnly":L.swatchesOnly=!!e.swatchesOnly,h.setAttribute("data-minimal",L.swatchesOnly),L.swatchesOnly&&(L.autoClose=!0);break;case"alpha":L.alpha=!!e.alpha,h.setAttribute("data-alpha",L.alpha);break;case"clearButton":var a="none";e.clearButton.show&&(a="block"),e.clearButton.label&&(i.innerHTML=e.clearButton.label),i.style.display=a;break;case"a11y":var l,r=e.a11y,o=!1;if("object"==typeof r)for(var n in r)r[n]&&L.a11y[n]&&(L.a11y[n]=r[n],o=!0);o&&(l=N("clr-open-label"),a=N("clr-swatch-label"),l.innerHTML=L.a11y.open,a.innerHTML=L.a11y.swatch,u.setAttribute("aria-label",L.a11y.close),b.setAttribute("aria-label",L.a11y.hueSlider),g.setAttribute("aria-label",L.a11y.alphaSlider),y.setAttribute("aria-label",L.a11y.input),f.setAttribute("aria-label",L.a11y.instruction));default:L[t]=e[t]}}function S(e){O(p,"click",e,function(e){var t=L.parent,a=e.target.getBoundingClientRect(),l=d.scrollY,r={left:!1,top:!1},o={x:0,y:0},n=a.x,i=l+a.y+a.height+L.margin;w=e.target,x=w.value,k=function(e){e=e.substring(0,3).toLowerCase();return"rgb"!==e&&"hsl"!==e?"hex":e}(x),h.classList.add("clr-open");var c,s=h.offsetWidth,u=h.offsetHeight;t?(c=d.getComputedStyle(t),e=parseFloat(c.marginTop),c=parseFloat(c.borderTopWidth),(o=t.getBoundingClientRect()).y+=c+l,n-=o.x,i-=o.y,n+s>t.clientWidth&&(n+=a.width-s,r.left=!0),i+u>t.clientHeight-e&&(i-=a.height+u+2*L.margin,r.top=!0),i+=t.scrollTop):(n+s>p.documentElement.clientWidth&&(n+=a.width-s,r.left=!0),i+u-l>p.documentElement.clientHeight&&(i=l+a.y-u-L.margin,r.top=!0)),h.classList.toggle("clr-left",r.left),h.classList.toggle("clr-top",r.top),h.style.left=n+"px",h.style.top=i+"px",v={width:f.offsetWidth,height:f.offsetHeight,x:h.offsetLeft+f.offsetLeft+o.x,y:h.offsetTop+f.offsetTop+o.y},A(x),L.focusInput&&y.focus({preventScroll:!0})}),O(p,"input",e,function(e){var t=e.target.parentNode;t.classList.contains("clr-field")&&(t.style.color=e.target.value)})}function T(e){p.querySelectorAll(e).forEach(function(e){var t,a=e.parentNode;a.classList.contains("clr-field")||((t=p.createElement("div")).innerHTML=' ',a.insertBefore(t,e),t.setAttribute("class","clr-field"),t.style.color=e.value,t.appendChild(e))})}function n(e){w&&(e&&x!==w.value&&(w.value=x,w.dispatchEvent(new Event("input",{bubbles:!0}))),x!==w.value&&w.dispatchEvent(new Event("change",{bubbles:!0})),h.classList.remove("clr-open"),L.focusInput&&w.focus({preventScroll:!0}),w=null)}function A(e){var t=function(e){var t;a.fillStyle="#000",a.fillStyle=e,(e=/^((rgba)|rgb)[\D]+([\d.]+)[\D]+([\d.]+)[\D]+([\d.]+)[\D]*?([\d.]+|$)/i.exec(a.fillStyle))?(t={r:+e[3],g:+e[4],b:+e[5],a:+e[6]}).a=+t.a.toFixed(2):(e=a.fillStyle.replace("#","").match(/.{2}/g).map(function(e){return parseInt(e,16)}),t={r:e[0],g:e[1],b:e[2],a:1});return t}(e),e=function(e){var t=e.r/255,a=e.g/255,l=e.b/255,r=s.max(t,a,l),o=s.min(t,a,l),n=r-o,i=r,c=0,o=0;n&&(r===t&&(c=(a-l)/n),r===a&&(c=2+(l-t)/n),r===l&&(c=4+(t-a)/n),r&&(o=n/r));return{h:(c=s.floor(60*c))<0?c+360:c,s:s.round(100*o),v:s.round(100*i),a:e.a}}(t);M(e.s,e.v),H(t,e),b.value=e.h,h.style.color="hsl("+e.h+", 100%, 50%)",l.style.left=e.h/360*100+"%",c.style.left=v.width*e.s/100+"px",c.style.top=v.height-v.height*e.v/100+"px",g.value=100*e.a,m.style.left=100*e.a+"%"}function r(e){w&&(w.value=void 0!==e?e:y.value,w.dispatchEvent(new Event("input",{bubbles:!0})))}function C(e,t){e={h:+b.value,s:e/v.width*100,v:100-t/v.height*100,a:g.value/100},t=function(e){var t=e.s/100,a=e.v/100,l=t*a,r=e.h/60,o=l*(1-s.abs(r%2-1)),n=a-l;l+=n,o+=n;t=s.floor(r)%6,a=[l,o,n,n,o,l][t],r=[o,l,l,o,n,n][t],t=[n,n,o,l,l,o][t];return{r:s.round(255*a),g:s.round(255*r),b:s.round(255*t),a:e.a}}(e);M(e.s,e.v),H(t,e),r()}function M(e,t){var a=L.a11y.marker;e=+e.toFixed(1),t=+t.toFixed(1),a=(a=a.replace("{s}",e)).replace("{v}",t),c.setAttribute("aria-label",a)}function t(e){var t={pageX:((a=e).changedTouches?a.changedTouches[0]:a).pageX,pageY:(a.changedTouches?a.changedTouches[0]:a).pageY},a=t.pageX-v.x,t=t.pageY-v.y;L.parent&&(t+=L.parent.scrollTop),a=a<0?0:a>v.width?v.width:a,t=t<0?0:t>v.height?v.height:t,c.style.left=a+"px",c.style.top=t+"px",C(a,t),e.preventDefault(),e.stopPropagation()}function H(e,t){void 0===t&&(t={});var a,l,r=L.format;for(a in e=void 0===e?{}:e)E[a]=e[a];for(l in t)E[l]=t[l];var o,n=function(e){var t=e.r.toString(16),a=e.g.toString(16),l=e.b.toString(16),r="";e.r<16&&(t="0"+t);e.g<16&&(a="0"+a);e.b<16&&(l="0"+l);L.alpha&&e.a<1&&(e=255*e.a|0,r=e.toString(16),e<16&&(r="0"+r));return"#"+t+a+l+r}(E),i=n.substring(0,7);switch(c.style.color=i,m.parentNode.style.color=i,m.style.color=n,u.style.color=n,f.style.display="none",f.offsetHeight,f.style.display="",m.nextElementSibling.style.display="none",m.nextElementSibling.offsetHeight,m.nextElementSibling.style.display="","mixed"===r?r=1===E.a?"hex":"rgb":"auto"===r&&(r=k),r){case"hex":y.value=n;break;case"rgb":y.value=(o=E,L.alpha&&1!==o.a?"rgba("+o.r+", "+o.g+", "+o.b+", "+o.a+")":"rgb("+o.r+", "+o.g+", "+o.b+")");break;case"hsl":y.value=(o=function(e){var t,a=e.v/100,l=a*(1-e.s/100/2);0
'+L.a11y.format+' Hex RGB HSL  
'+L.clearButton.label+' '+L.a11y.open+' '+L.a11y.swatch+" ",p.body.appendChild(h),f=N("clr-color-area"),c=N("clr-color-marker"),i=N("clr-clear"),u=N("clr-color-preview"),y=N("clr-color-value"),b=N("clr-hue-slider"),l=N("clr-hue-marker"),g=N("clr-alpha-slider"),m=N("clr-alpha-marker"),S(L.el),T(L.el),O(h,"mousedown",function(e){h.classList.remove("clr-keyboard-nav"),e.stopPropagation()}),O(f,"mousedown",function(e){O(p,"mousemove",t)}),O(f,"touchstart",function(e){p.addEventListener("touchmove",t,{passive:!1})}),O(c,"mousedown",function(e){O(p,"mousemove",t)}),O(c,"touchstart",function(e){p.addEventListener("touchmove",t,{passive:!1})}),O(y,"change",function(e){A(y.value),r()}),O(i,"click",function(e){r(""),n()}),O(u,"click",function(e){r(),n()}),O(p,"click",".clr-format input",function(e){k=e.target.value,H(),r()}),O(h,"click",".clr-swatches button",function(e){A(e.target.textContent),r(),L.autoClose&&n()}),O(p,"mouseup",function(e){p.removeEventListener("mousemove",t)}),O(p,"touchend",function(e){p.removeEventListener("touchmove",t)}),O(p,"mousedown",function(e){h.classList.remove("clr-keyboard-nav"),n()}),O(p,"keydown",function(e){"Escape"===e.key?n(!0):"Tab"===e.key&&h.classList.add("clr-keyboard-nav")}),O(p,"click",".clr-field button",function(e){e.target.nextElementSibling.dispatchEvent(new Event("click",{bubbles:!0}))}),O(c,"keydown",function(e){var t={ArrowUp:[0,-1],ArrowDown:[0,1],ArrowLeft:[-1,0],ArrowRight:[1,0]};-1!==Object.keys(t).indexOf(e.key)&&(!function(e,t){e=+c.style.left.replace("px","")+e,t=+c.style.top.replace("px","")+t,c.style.left=e+"px",c.style.top=t+"px",C(e,t)}.apply(void 0,t[e.key]),e.preventDefault())}),O(f,"click",t),O(b,"input",e),O(g,"input",B)})}(window,document,Math);
\ No newline at end of file
diff --git a/static/colorpicker/css b/static/colorpicker/css
new file mode 120000
index 0000000..3cf966e
--- /dev/null
+++ b/static/colorpicker/css
@@ -0,0 +1 @@
+../../node_modules/bootstrap-colorpicker/dist/css
\ No newline at end of file
diff --git a/static/colorpicker/js b/static/colorpicker/js
new file mode 120000
index 0000000..cc23749
--- /dev/null
+++ b/static/colorpicker/js
@@ -0,0 +1 @@
+../../node_modules/bootstrap-colorpicker/dist/js
\ No newline at end of file
diff --git a/static/css/bootstrap-icons.css b/static/css/bootstrap-icons.css
new file mode 120000
index 0000000..996baca
--- /dev/null
+++ b/static/css/bootstrap-icons.css
@@ -0,0 +1 @@
+../../node_modules/bootstrap-icons/font/bootstrap-icons.css
\ No newline at end of file
diff --git a/static/css/errorPage/style.css b/static/css/errorPage/style.css
new file mode 100644
index 0000000..27a7671
--- /dev/null
+++ b/static/css/errorPage/style.css
@@ -0,0 +1,86 @@
+html, body {
+    height: 100%;
+    width: 100%;
+    margin: 0;
+    padding: 0;
+}
+
+.container {
+    height: 100%;
+    width: 100%;
+    background-color: #0B161E;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+    font-family: 'Segoe UI';
+    text-align: center;
+}
+
+.color-foreground {
+    color: #474849 !important;
+}
+
+.text-shadow {
+    text-shadow: 1px 1px 2px;
+}
+
+.rainbow {
+    -webkit-animation:rainbow 16s infinite;
+    -ms-animation:rainbow 16s infinite;
+    -o-animation:rainbow 16s infinite;
+    animation:rainbow 16s infinite;
+}
+
+@-webkit-keyframes rainbow {
+0% {color: #ff0000;}
+10% {color: #ff8000;}
+20% {color: #ffff00;}
+30% {color: #80ff00;}
+40% {color: #00ff00;}
+50% {color: #00ff80;}
+60% {color: #00ffff;}
+70% {color: #0080ff;}
+80% {color: #0000ff;}
+90% {color: #8000ff;}
+100% {color: #ff0080;}
+}
+@-ms-keyframes rainbow {
+0% {color: #ff0000;}
+10% {color: #ff8000;}
+20% {color: #ffff00;}
+30% {color: #80ff00;}
+40% {color: #00ff00;}
+50% {color: #00ff80;}
+60% {color: #00ffff;}
+70% {color: #0080ff;}
+80% {color: #0000ff;}
+90% {color: #8000ff;}
+100% {color: #ff0080;}
+}
+@-o-keyframes rainbow {
+0% {color: #ff0000;}
+10% {color: #ff8000;}
+20% {color: #ffff00;}
+30% {color: #80ff00;}
+40% {color: #00ff00;}
+50% {color: #00ff80;}
+60% {color: #00ffff;}
+70% {color: #0080ff;}
+80% {color: #0000ff;}
+90% {color: #8000ff;}
+100% {color: #ff0080;}
+}
+@keyframes rainbow {
+0% {color: #ff0000;}
+10% {color: #ff8000;}
+20% {color: #ffff00;}
+30% {color: #80ff00;}
+40% {color: #00ff00;}
+50% {color: #00ff80;}
+60% {color: #00ffff;}
+70% {color: #0080ff;}
+80% {color: #0000ff;}
+90% {color: #8000ff;}
+100% {color: #ff0080;}
+}
\ No newline at end of file
diff --git a/static/css/errorPage/styles.less b/static/css/errorPage/styles.less
new file mode 100644
index 0000000..b2732f5
--- /dev/null
+++ b/static/css/errorPage/styles.less
@@ -0,0 +1,62 @@
+.glitch-wrapper {
+  height: 100%;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+
+.glitch {
+  @offset1: 2px;
+  @offset2: -2px;
+  @highlight1: #49FC00;
+  @highlight2: spin(@highlight1, 180);
+  
+  color: white;
+  font-size: 150px;
+  text-transform: upercase;
+  position: relative;
+  display: inline-block;
+  
+  &::before,
+  &::after  {
+    content: attr(data-text);
+    position: absolute;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+    background: #0B161E;
+  }
+  
+  &::before {
+    left: @offset1;
+    text-shadow: -2px 0 @highlight1;
+    clip: rect(24px, 550px, 90px, 0);
+    animation: glitch-anim-2 3s infinite linear alternate-reverse;
+  }
+  
+  &::after {
+    left: @offset2;
+    text-shadow: -2px 0 @highlight2;
+    clip: rect(85px, 550px, 140px, 0);
+    animation: glitch-anim 2.5s infinite linear alternate-reverse;
+  }
+}
+
+.glitch-frames (@n: 20, @index: 0) when (@index <= @n) {
+  @keyframeSel: percentage(@index/@n);
+  @rand1: unit(round(`Math.random()*150`),px);
+  @rand2: unit(round(`Math.random()*150`), px);
+  @{keyframeSel} {
+    clip: rect(@rand1, 9999px, @rand2, 0);
+  }
+  .glitch-frames(@n, (@index + 1));
+}
+
+@keyframes glitch-anim {
+  .glitch-frames(24);
+}
+
+@keyframes glitch-anim-2 {
+  .glitch-frames(30,2);
+}
\ No newline at end of file
diff --git a/static/css/fonts/bootstrap-icons.woff b/static/css/fonts/bootstrap-icons.woff
new file mode 120000
index 0000000..6a1958f
--- /dev/null
+++ b/static/css/fonts/bootstrap-icons.woff
@@ -0,0 +1 @@
+../../../node_modules/bootstrap-icons/font/fonts/bootstrap-icons.woff
\ No newline at end of file
diff --git a/static/css/fonts/bootstrap-icons.woff2 b/static/css/fonts/bootstrap-icons.woff2
new file mode 120000
index 0000000..90ff4c9
--- /dev/null
+++ b/static/css/fonts/bootstrap-icons.woff2
@@ -0,0 +1 @@
+../../../node_modules/bootstrap-icons/font/fonts/bootstrap-icons.woff2
\ No newline at end of file
diff --git a/static/css/mainStyle.css b/static/css/mainStyle.css
new file mode 100644
index 0000000..2ae1f08
--- /dev/null
+++ b/static/css/mainStyle.css
@@ -0,0 +1,129 @@
+body {
+    min-height: 100vh;
+    min-height: -webkit-fill-available;
+  }
+  
+  html {
+    height: -webkit-fill-available;
+  }
+  
+  main {
+    display: flex;
+    flex-wrap: nowrap;
+    height: 100vh !important;
+    height: -webkit-fill-available;
+    max-height: 100vh;
+    overflow-x: auto;
+    overflow-y: hidden;
+  }
+
+  .full .clr-field button {
+    width: 100%;
+    height: 100%;
+    border-radius: 5px;
+  }
+  
+  .b-example-divider {
+    flex-shrink: 0;
+    width: 1.5rem;
+    height: 100vh;
+    background-color: rgba(0, 0, 0, .1);
+    border: solid rgba(0, 0, 0, .15);
+    border-width: 1px 0;
+    box-shadow: inset 0 .5em 1.5em rgba(0, 0, 0, .1), inset 0 .125em .5em rgba(0, 0, 0, .15);
+  }
+  
+  .bi {
+    vertical-align: -.125em;
+    pointer-events: none;
+    fill: currentColor;
+  }
+  
+  .dropdown-toggle { outline: 0; }
+  
+  .nav-flush .nav-link {
+    border-radius: 0;
+  }
+  
+  .btn-toggle {
+    display: inline-flex;
+    align-items: center;
+    padding: .25rem .5rem;
+    font-weight: 600;
+    color: rgba(0, 0, 0, .65);
+    background-color: transparent;
+    border: 0;
+  }
+  .btn-toggle:hover,
+  .btn-toggle:focus {
+    color: rgba(0, 0, 0, .85);
+    background-color: #d2f4ea;
+  }
+  
+  .btn-toggle::before {
+    width: 1.25em;
+    line-height: 0;
+    content: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='rgba%280,0,0,.5%29' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M5 14l6-6-6-6'/%3e%3c/svg%3e");
+    transition: transform .35s ease;
+    transform-origin: .5em 50%;
+  }
+  
+  .btn-toggle[aria-expanded="true"] {
+    color: rgba(0, 0, 0, .85);
+  }
+  .btn-toggle[aria-expanded="true"]::before {
+    transform: rotate(90deg);
+  }
+  
+  .btn-toggle-nav a {
+    display: inline-flex;
+    padding: .1875rem .5rem;
+    margin-top: .125rem;
+    margin-left: 1.25rem;
+    text-decoration: none;
+  }
+  .btn-toggle-nav a:hover,
+  .btn-toggle-nav a:focus {
+    background-color: #d2f4ea;
+  }
+  
+  .scrollarea {
+    overflow-y: auto;
+  }
+  
+  .fw-semibold { font-weight: 600; }
+  .lh-tight { line-height: 1.25; }
+  
+
+  .hidden{
+      display: none;
+  }
+
+  .pageC {
+      padding: 20px;
+  }
+
+  
+placeholder {
+  border: red 2px dashed;
+  background-color: yellow;
+}
+
+pre {
+  overflow: auto;
+  word-wrap: normal;
+  white-space: pre;
+}
+
+.hidden {
+  display: none !important;
+}
+
+.trans {
+  transition: .5s
+}
+
+.helperTip {
+  opacity: 0.8;
+  font-size: small;
+}
\ No newline at end of file
diff --git a/static/css/smallerTextMod.css b/static/css/smallerTextMod.css
index 1b5f4ab..211158b 100644
--- a/static/css/smallerTextMod.css
+++ b/static/css/smallerTextMod.css
@@ -1,3 +1,3 @@
 body {
-    font-size:0.5em;
+    font-size:0.3em;
 }
\ No newline at end of file
diff --git a/static/css/styles.css b/static/css/styles.css
index 5800c6c..953a446 100644
--- a/static/css/styles.css
+++ b/static/css/styles.css
@@ -103,4 +103,11 @@ html, body {
 .blink {
 animation:fade 1000ms infinite;
 -webkit-animation:fade 1000ms infinite;
+}
+
+.connectionWarning{
+  background-color: red;
+  width: 100vw;
+  font-size: x-large;
+  font-family: sans-serif;
 }
\ No newline at end of file
diff --git a/static/flatpickr/dist b/static/flatpickr/dist
new file mode 120000
index 0000000..9c148d9
--- /dev/null
+++ b/static/flatpickr/dist
@@ -0,0 +1 @@
+../../node_modules/flatpickr/dist/
\ No newline at end of file
diff --git a/static/js/cookie.js b/static/js/cookie.js
new file mode 120000
index 0000000..9097170
--- /dev/null
+++ b/static/js/cookie.js
@@ -0,0 +1 @@
+../../node_modules/js-cookie/dist/js.cookie.min.js
\ No newline at end of file
diff --git a/static/js/countdown.js b/static/js/countdown.js
deleted file mode 120000
index 71edd1e..0000000
--- a/static/js/countdown.js
+++ /dev/null
@@ -1 +0,0 @@
-../../node_modules/countdown/countdown.js
\ No newline at end of file
diff --git a/static/js/darkreader.js b/static/js/darkreader.js
new file mode 120000
index 0000000..f810896
--- /dev/null
+++ b/static/js/darkreader.js
@@ -0,0 +1 @@
+../../node_modules/darkreader/darkreader.js
\ No newline at end of file
diff --git a/static/js/interface.js b/static/js/interface.js
index 827a688..4822c9e 100644
--- a/static/js/interface.js
+++ b/static/js/interface.js
@@ -1,4 +1,529 @@
-function convertTimeOffset(){
+function convertTimeOffset() {
     selTime = new Date().getTime() + document.getElementById('time2').valueAsNumber
     document.getElementById("time").value = selTime
 }
+
+
+function convertColorSegments(elementId) {
+    const raw = document.getElementById(elementId);
+    const segmentData = {}
+    for (elm in raw.children) {
+        if (raw.children[elm].nodeName == "TR") {
+            const child = raw.children[elm].children[1].children[0]
+            let timeVal = parseInt(raw.children[elm].children[0].children[0].value * 1000);
+            if (Number.isInteger(timeVal)) {
+                segmentData[timeVal] = child.style.color
+            } else {
+                segmentData["START"] = child.style.color
+            }
+        }
+    }
+    console.info(segmentData)
+    return (segmentData)
+}
+
+$(function () {
+    // $(".numVal").bind("DOMSubtreeModified", alert);
+
+    const modes = ["timer", "clock", "black", "test"]
+    let selectPresetTime = 0;
+
+    if (Cookies.get("interfaceColor") != undefined) {
+        const color = Cookies.get("interfaceColor");
+        $("#Mbtnradio" + (color))[0].checked = true
+        if (color == 1) {
+            DarkReader.disable()
+        } else if (color == 2) {
+            DarkReader.enable()
+        } else {
+            DarkReader.auto()
+        }
+    }
+
+    saveOption("/api/v1/system", function systemInfoLoader(event) {
+        const dataSystem = JSON.parse(event.target.response)
+        document.getElementById("nodejsVers").innerHTML = dataSystem.nodeVersion
+        document.getElementById("nodeSwVers").innerHTML = dataSystem.systemVersion
+
+        const tree2 = jsonview.create(dataSystem);
+        jsonview.render(tree2, document.getElementById("systemInfo"));
+        jsonview.expand(tree2);
+    })
+
+    $("#addRow").on("click", function (event) {
+        const tableEntryDom = document.getElementById("tableCopySource").cloneNode(true)
+        let temp = tableEntryDom.innerHTML
+        temp = temp.replace("#COLOR#", "gray");
+        temp = temp.replace("#bg-COLOR#", "gray");
+        temp = temp.replace("#VALUE#", 60); // BUG: Doesn't work but not too important
+        temp = temp.replace("timeValue-ID", Math.floor(Math.random() * 9999999))
+        tableEntryDom.innerHTML = temp;
+        tableEntryDom.firstChild.nextSibling.children[0].classList.add("timeDurPicker")
+        tableEntryDom.id = Math.floor(Math.random() * 9999999)
+        document.getElementById("colors1").appendChild(tableEntryDom)
+        $(".deleteRow1").on("click", function removeRowEntry(event) {
+            $(event.target).closest("tr").remove();
+        });
+        $('.timeDurPicker').durationPicker({
+            showSeconds: true,
+            showDays: false,
+            onChanged: function (newVal, test, val2) {
+                //   $('#duration-label2').text(newVal);
+                val2.days[0].parentElement.parentElement.parentElement.parentElement.children[1].value = newVal
+                //console.log(val2.days[0].parentElement.parentElement.parentElement.parentElement.children[1].value)
+                //console.log(val2.days[0].parentElement.parentElement.parentElement.parentElement.children[1])
+            }
+        });
+    });
+
+    $("#addRow2").on("click", function (event) {
+        const tableEntryDom = document.getElementById("tableCopySource").cloneNode(true)
+        let temp = tableEntryDom.innerHTML
+        temp = temp.replace("#COLOR#", "gray");
+        temp = temp.replace("#bg-COLOR#", "gray");
+        temp = temp.replace("#VALUE#", 60); // BUG: Doesn't work but not too important
+        temp = temp.replace("timeValue-ID", Math.floor(Math.random() * 9999999))
+        tableEntryDom.innerHTML = temp;
+        tableEntryDom.firstChild.nextSibling.children[0].classList.add("timeDurPicker")
+        tableEntryDom.id = Math.floor(Math.random() * 9999999)
+        document.getElementById("colors2").appendChild(tableEntryDom)
+        $(".deleteRow1").on("click", function removeRowEntry(event) {
+            $(event.target).closest("tr").remove();
+        });
+        $('.timeDurPicker').durationPicker({
+            showSeconds: true,
+            showDays: false,
+            onChanged: function (newVal, test, val2) {
+                //   $('#duration-label2').text(newVal);
+                val2.days[0].parentElement.parentElement.parentElement.parentElement.children[1].value = newVal
+            }
+        });
+    });
+
+
+
+    // Restore settings
+    saveOption("/api/v1/data", function (event, xmlHttp) {
+        const jsonResult = JSON.parse(xmlHttp.response)
+        const tree = jsonview.create(jsonResult);
+        jsonview.render(tree, document.getElementById("responeSnippet"));
+        jsonview.expand(tree);
+
+        // Restore mode radio
+        const currentModeInt = modes.indexOf(jsonResult.mode);
+        $("#btnradio" + (currentModeInt + 1))[0].checked = true
+
+        // Restore layout settings
+        $("#showTime")[0].checked = jsonResult.showTimeOnCountdown;
+        $("#showMillis")[0].checked = jsonResult.showMilliSeconds;
+        $("#progBarShow")[0].checked = jsonResult.showProgressbar;
+        $("#textColors")[0].checked = jsonResult.enableColoredText;
+        console.log(document.getElementById("tableCopySource"))
+
+        let tableEntryDom = document.getElementById("tableCopySource").cloneNode(true)
+        let currIndex = 0
+        for (item in jsonResult.colorSegments) {
+            tableEntryDom = document.getElementById("tableCopySource").cloneNode(true)
+            let temp = tableEntryDom.innerHTML
+            temp = temp.replace("#COLOR#", jsonResult.colorSegments[item]);
+            temp = temp.replace("#bg-COLOR#", jsonResult.colorSegments[item]);
+            temp = temp.replace("timeValue-ID", "timeValue-1C" + currIndex)
+
+            if (item != "START") {
+                temp = temp.replace("#VALUE#", item / 1000);
+                tableEntryDom.innerHTML = temp;
+                tableEntryDom.firstChild.nextSibling.children[0].classList.add("timeDurPicker")
+            } else {
+                tableEntryDom.innerHTML = temp;
+                tableEntryDom.children[2].children[0].children[0].disabled = true;
+                tableEntryDom.children[2].children[0].setAttribute("data-toggle", "tooltip")
+                tableEntryDom.children[2].children[0].setAttribute("data-placement", "right")// 'data-placement="right" title="Tooltip on right"'
+                tableEntryDom.children[2].children[0].setAttribute("title", "You cannot delete the start time")
+
+                tableEntryDom.children[0].innerHTML = '  Start'
+            }
+            tableEntryDom.id = "1C" + currIndex
+            tableEntryDom.style.display = "table-row"
+            document.getElementById("colors1").appendChild(tableEntryDom)
+            currIndex++;
+        }
+
+
+        // Text colors
+        currIndex = 0
+        for (item in jsonResult.textColors) {
+            tableEntryDom = document.getElementById("tableCopySource").cloneNode(true)
+            let temp = tableEntryDom.innerHTML
+            temp = temp.replace("#COLOR#", jsonResult.textColors[item]);
+            temp = temp.replace("#bg-COLOR#", jsonResult.textColors[item]);
+            temp = temp.replace("timeValue-ID", "timeValue-1C" + currIndex)
+
+            if (item != "START") {
+                temp = temp.replace("#VALUE#", item / 1000);
+                tableEntryDom.innerHTML = temp;
+                tableEntryDom.firstChild.nextSibling.children[0].classList.add("timeDurPicker")
+            } else {
+                tableEntryDom.innerHTML = temp;
+                tableEntryDom.children[2].children[0].children[0].disabled = true;
+                tableEntryDom.children[2].children[0].setAttribute("data-toggle", "tooltip")
+                tableEntryDom.children[2].children[0].setAttribute("data-placement", "right")// 'data-placement="right" title="Tooltip on right"'
+                tableEntryDom.children[2].children[0].setAttribute("title", "You cannot delete the start time")
+
+                tableEntryDom.children[0].innerHTML = '  Start'
+            }
+
+
+            // timeDurPicker
+            tableEntryDom.id = "1C" + currIndex
+            tableEntryDom.style.display = "table-row"
+            document.getElementById("colors2").appendChild(tableEntryDom)
+            currIndex++;
+        }
+
+
+        $('.timeDurPicker').durationPicker({
+            showSeconds: true,
+            showDays: false,
+            onChanged: function (newVal, test, val2) {
+                val2.days[0].parentElement.parentElement.parentElement.parentElement.children[1].value = newVal
+            }
+        });
+
+
+
+        //console.debug(jsonResult, currentModeInt)
+        $('.colorPicky').on('colorpickerChange', function (event) {
+            event.target.parentElement.style.backgroundColor = event.target.value
+        });
+
+        $(".deleteRow1").on("click", function removeRowEntry(event) {
+            $(event.target).closest("tr").remove();
+        });
+        $('[data-toggle="tooltip"]').tooltip({ container: "body" })
+    })
+
+    $("#copyColors").click(function CopyTextColors(event) {
+        event.target.innerHTML = '
'
+        saveOption("/api/v1/set/text/colors?copy=true", function finishCopyColors(event) {
+            setTimeout(function () {
+                document.getElementById("copyColors").innerHTML = "Copy from progressbar colors"
+            }, 500)
+
+        })
+    });
+
+
+    $("input[name='btnradio2']").click(function (event) {
+        const darkid = parseInt(event.currentTarget.id.replace("Mbtnradio", ""))
+        if (darkid == 1) {
+            DarkReader.disable()
+        } else if (darkid == 2) {
+            DarkReader.enable()
+        } else {
+            DarkReader.auto()
+        }
+        Cookies.set("interfaceColor", darkid)
+    });
+
+    // Presets
+    $(".pres").click(function (event) {
+        currentTime = parseInt(event.currentTarget.value)
+        $("#customValue").data("durationPicker").setValue(currentTime / 1000)
+    })
+
+
+    $(".goTimer").on("click", function (event) {
+        event.currentTarget.innerHTML = '
'
+        setTimeout(function () {
+            event.currentTarget.innerHTML = 'Go'
+        }, 200);
+
+        saveOption("/api/v1/set/addMillisToTimer?time=" + currentTime, function (ev) {
+        })
+    })
+
+    // Layout settings
+    $("#applyLayout").click(function (event) {
+        $("#applyLayout")[0].innerHTML = '
'
+
+        // Collect all data, build all paths
+        const allPathes = [];
+
+        const showTimeB = $("#showTime")[0].checked
+        const showMillisB = $("#showMillis")[0].checked
+        const progBarShowB = $("#progBarShow")[0].checked
+        const textColorsB = $("#textColors")[0].checked
+
+        const colors = convertColorSegments("colors1")
+        const colors2 = convertColorSegments("colors2")
+
+        allPathes.push("/api/v1/set/layout/showTime?show=" + showTimeB)
+        allPathes.push("/api/v1/set/layout/showMillis?show=" + showMillisB)
+        allPathes.push("/api/v1/set/progressbar/show?show=" + progBarShowB)
+        allPathes.push("/api/v1/set/text/enableColoring?enable=" + textColorsB)
+        allPathes.push("/api/v1/set/progressbar/colors?isBase64=true&colors=" + btoa(JSON.stringify(colors)))
+        allPathes.push("/api/v1/set/text/colors?isBase64=true&colors=" + btoa(JSON.stringify(colors2)))
+
+
+        for (pI in allPathes) {
+            const path = allPathes[pI];
+            saveOption(path, function (event) {
+                console.debug(event)
+            })
+        }
+
+        setTimeout(function () {
+            $("#applyLayout")[0].innerHTML = 'Apply settings'
+        }, 500)
+    })
+
+
+    $("#saveLayout").click(function (event) {
+        $("#saveLayout")[0].innerHTML = '
'
+
+        // Collect all data, build all paths3
+        const allPathes = [];
+
+        const showTimeB = $("#showTime")[0].checked
+        const showMillisB = $("#showMillis")[0].checked
+        const progBarShowB = $("#progBarShow")[0].checked
+        const textColorsB = $("#textColors")[0].checked
+
+        allPathes.push("/api/v1/set/layout/showTime?persist=true&show=" + showTimeB)
+        allPathes.push("/api/v1/set/layout/showMillis?persist=true&show=" + showMillisB)
+        allPathes.push("/api/v1/set/progressbar/show?persist=true&show=" + progBarShowB)
+        allPathes.push("/api/v1/set/text/enableColoring?persist=true&enable=" + textColorsB)
+
+        for (pI in allPathes) {
+            const path = allPathes[pI];
+            saveOption(path, function (event) {
+                console.debug(event)
+            })
+        }
+
+        saveOption("/api/v1/storage/commit", function (event) {
+            console.debug(event)
+            setTimeout(function () {
+                $("#saveLayout")[0].innerHTML = 'Save as startup settings (Layout only)'
+            }, 500)
+        })
+
+
+
+    })
+
+
+    function msToTime(s, data) {
+        var ms = s % 1000;
+        s = (s - ms) / 1000;
+        var secs = s % 60;
+        s = (s - secs) / 60;
+        var mins = s % 60;
+        var hrs = (s - mins) / 60;
+        let out = ""
+
+        return [ms, secs, mins, hrs];
+    }
+
+    //  Timer custom
+    let currentTime = 0;
+    $("#timerHourInc").click(function (event) {
+        currentTime += 3600000
+        const times = msToTime(currentTime)
+        $("#timerHoursV")[0].innerHTML = times[3];
+        $("#timerMinuteV")[0].innerHTML = times[2];
+        $("#timerSecondsV")[0].innerHTML = times[1];
+    })
+
+    $("#timerHourDec").click(function (event) {
+        if (currentTime >= 3600000) {
+            currentTime -= 3600000
+            const times = msToTime(currentTime)
+            $("#timerHoursV")[0].innerHTML = times[3];
+            $("#timerMinuteV")[0].innerHTML = times[2];
+            $("#timerSecondsV")[0].innerHTML = times[1];
+        }
+    })
+
+    $("#timerMinuteInc").click(function (event) {
+        currentTime += 60000
+        const times = msToTime(currentTime)
+        $("#timerHoursV")[0].innerHTML = times[3];
+        $("#timerMinuteV")[0].innerHTML = times[2];
+        $("#timerSecondsV")[0].innerHTML = times[1];
+    })
+    $("#timerMinuteDec").click(function (event) {
+        if (currentTime >= 60000) {
+            currentTime -= 60000
+            const times = msToTime(currentTime)
+            $("#timerHoursV")[0].innerHTML = times[3];
+            $("#timerMinuteV")[0].innerHTML = times[2];
+            $("#timerSecondsV")[0].innerHTML = times[1];
+        }
+    })
+    $("#timerSecondsInc").click(function (event) {
+        currentTime += 1000
+        const times = msToTime(currentTime)
+        $("#timerHoursV")[0].innerHTML = times[3];
+        $("#timerMinuteV")[0].innerHTML = times[2];
+        $("#timerSecondsV")[0].innerHTML = times[1];
+    })
+    $("#timerSecondsDec").click(function (event) {
+        if (currentTime >= 1000) {
+            currentTime -= 1000
+            const times = msToTime(currentTime)
+            $("#timerHoursV")[0].innerHTML = times[3];
+            $("#timerMinuteV")[0].innerHTML = times[2];
+            $("#timerSecondsV")[0].innerHTML = times[1];
+        }
+    })
+
+    $("input[name='btnradio']").click(function (event) {
+        $("#sendMessage")[0].innerHTML = '
'
+        let value = modes[parseInt(event.currentTarget.id.replace("btnradio", "")) - 1]
+        console.log(value, parseInt(event.currentTarget.id.replace("btnradio", "")))
+        saveOption("/api/v1/set/mode?mode=" + value, function (event) {
+            setTimeout(function () {
+                $("#sendMessage")[0].innerHTML = ' '
+            }, 200)
+
+        })
+    })
+
+
+    $("#sendMessage").click(function (event) {
+        $("#sendMessage")[0].innerHTML = '
'
+        let value = $("#messageContent").val()
+        saveOption("/api/v1/ctrl/message/show?msg=" + value, function (event) {
+            setTimeout(function () {
+                $("#sendMessage")[0].innerHTML = ' '
+            }, 200)
+
+        })
+    })
+
+    $("#ctrlRemoveMessage").click(function (event) {
+        $("#ctrlRemoveMessage")[0].innerHTML = '
'
+        saveOption("/api/v1/ctrl/message/hide", function (event) {
+            setTimeout(function () {
+                $("#ctrlRemoveMessage")[0].innerHTML = ' '
+            }, 200)
+
+        })
+    })
+
+    $("#funcPlay").click(function (event) {
+        $("#funcPlay")[0].innerHTML = '
'
+        saveOption("/api/v1/ctrl/timer/play", function (event) {
+            setTimeout(function () {
+                $("#funcPlay")[0].innerHTML = ' '
+            }, 200);
+        })
+    })
+
+
+    $("#funcPause").click(function (event) {
+        $("#funcPause")[0].innerHTML = '
'
+
+        saveOption("/api/v1/ctrl/timer/pause", function (event) {
+            setTimeout(function () {
+                $("#funcPause")[0].innerHTML = ' '
+            }, 200);
+        })
+    })
+
+    $("#funcRestart").click(function (event) {
+        $("#funcRestart")[0].innerHTML = '
'
+        saveOption("/api/v1/ctrl/timer/restart", function (event) {
+            setTimeout(function () {
+                $("#funcRestart")[0].innerHTML = ' '
+            }, 200)
+        })
+    })
+
+    $("#applyDebug").click(function (event) {
+        $("#applyDebug")[0].innerHTML = '
'
+        let value = $("#debugModeEnable")[0].checked
+        saveOption("/api/v1/debug?enable=" + value, function (event) {
+            setTimeout(function () {
+                $("#applyDebug")[0].innerHTML = "Apply settings"
+            }, 200)
+
+        })
+    })
+
+    $("a.nav-link").click(function (event) {
+        event.preventDefault();
+        $("a.nav-link").removeClass("active")
+
+        event.currentTarget.classList.add("active")
+
+        $(".pageC").addClass("hidden")
+        $("#" + event.target.href.split("#")[1]).removeClass("hidden")
+        // console.log(event.target.href.split("#")[1])
+    });
+    $("#customValue").durationPicker({
+        showSeconds: true,
+        showDays: false,
+        onChanged: function (newVal, test, val2) {
+            currentTime = newVal * 1000
+        }
+    })
+
+    flatty = flatpickr("#datetimetester", {
+        enableTime: true,
+        time_24hr: true,
+        dateFormat: "H:i d.m.Y",
+    });
+
+    $(".goTimeGoalCountdown").on("click", function handleCountdownToTime() {
+        const selectTime = flatty.selectedDates[0].getTime()
+        const timeDiff = selectTime - new Date().getTime()
+        $(".goTimeGoalCountdown")[0].innerHTML = '
'
+        saveOption("/api/v1/set/addMillisToTimer?time=" + timeDiff, function (ev) {
+            setTimeout(function () {
+                $(".goTimeGoalCountdown")[0].innerHTML = ' '
+            }, 200);
+        })
+    })
+});
+
+function saveOption(path, callback) {
+    var xmlHttp = new XMLHttpRequest();
+    xmlHttp.open("GET", path, true); // false for synchronous request
+    xmlHttp.send(null);
+    xmlHttp.onerror = function (e) {
+        console.log(e); // ToDo: Popup, etc.
+    };
+    xmlHttp.onloadend = function (event) {
+        callback(event, xmlHttp)
+    }
+}
+
+navStatus = true
+function openNav() {
+    document.getElementById("navbarToggleExternalContent").style.width = "250px";
+    document.getElementById("navbarToggleExternalContent").style.opacity = "1";
+    document.getElementById("navbarToggleExternalContent").style.display = "block";
+    document.getElementById("navbarToggleExternalContent").style.zIndex = 999999;
+    document.getElementById("pageCont").style.marginLeft = "0px";
+    navStatus = true
+}
+
+function closeNav() {
+    document.getElementById("navbarToggleExternalContent").style.width = "0px";
+    document.getElementById("navbarToggleExternalContent").style.opacity = "0";
+    document.getElementById("navbarToggleExternalContent").style.display = "none";
+    document.getElementById("navbarToggleExternalContent").style.zIndex = -999999;
+    document.getElementById("pageCont").style.marginLeft = "0";
+    navStatus = false
+}
+
+function toogleNav() {
+    if (navStatus) {
+        closeNav()
+    } else {
+        openNav()
+    }
+
+}
\ No newline at end of file
diff --git a/static/js/jquery.min.js b/static/js/jquery.min.js
new file mode 120000
index 0000000..08ac9f2
--- /dev/null
+++ b/static/js/jquery.min.js
@@ -0,0 +1 @@
+../../node_modules/jquery/dist/jquery.min.js
\ No newline at end of file
diff --git a/static/js/jsonview.js b/static/js/jsonview.js
new file mode 100644
index 0000000..1ac71b4
--- /dev/null
+++ b/static/js/jsonview.js
@@ -0,0 +1 @@
+!function(e,n){"object"==typeof exports&&"object"==typeof module?module.exports=n():"function"==typeof define&&define.amd?define([],n):"object"==typeof exports?exports.jsonview=n():e.jsonview=n()}(self,(function(){return(()=>{"use strict";var e={767:(e,n,t)=>{t.d(n,{Z:()=>s});var r=t(81),o=t.n(r),i=t(645),a=t.n(i)()(o());a.push([e.id,'.json-container{font-family:"Open Sans";font-size:16px;background-color:#fff;color:gray;box-sizing:border-box}.json-container .line{margin:4px 0;display:flex;justify-content:flex-start}.json-container .caret-icon{width:18px;text-align:center;cursor:pointer}.json-container .empty-icon{width:18px}.json-container .json-type{margin-right:4px;margin-left:4px}.json-container .json-key{color:#444;margin-right:4px;margin-left:4px}.json-container .json-index{margin-right:4px;margin-left:4px}.json-container .json-value{margin-left:8px}.json-container .json-number{color:#f9ae58}.json-container .json-boolean{color:#ec5f66}.json-container .json-string{color:#86b25c}.json-container .json-size{margin-right:4px;margin-left:4px}.json-container .hidden{display:none}.json-container .fas{display:inline-block;border-style:solid;width:0;height:0}.json-container .fa-caret-down{border-width:6px 5px 0 5px;border-color:gray transparent}.json-container .fa-caret-right{border-width:5px 0 5px 6px;border-color:transparent transparent transparent gray}',""]);const s=a},645:e=>{e.exports=function(e){var n=[];return n.toString=function(){return this.map((function(n){var t="",r=void 0!==n[5];return n[4]&&(t+="@supports (".concat(n[4],") {")),n[2]&&(t+="@media ".concat(n[2]," {")),r&&(t+="@layer".concat(n[5].length>0?" ".concat(n[5]):""," {")),t+=e(n),r&&(t+="}"),n[2]&&(t+="}"),n[4]&&(t+="}"),t})).join("")},n.i=function(e,t,r,o,i){"string"==typeof e&&(e=[[null,e,void 0]]);var a={};if(r)for(var s=0;s0?" ".concat(d[5]):""," {").concat(d[1],"}")),d[5]=i),t&&(d[2]?(d[1]="@media ".concat(d[2]," {").concat(d[1],"}"),d[2]=t):d[2]=t),o&&(d[4]?(d[1]="@supports (".concat(d[4],") {").concat(d[1],"}"),d[4]=o):d[4]="".concat(o)),n.push(d))}},n}},81:e=>{e.exports=function(e){return e[1]}},379:e=>{var n=[];function t(e){for(var t=-1,r=0;r{var n={};e.exports=function(e,t){var r=function(e){if(void 0===n[e]){var t=document.querySelector(e);if(window.HTMLIFrameElement&&t instanceof window.HTMLIFrameElement)try{t=t.contentDocument.head}catch(e){t=null}n[e]=t}return n[e]}(e);if(!r)throw new Error("Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.");r.appendChild(t)}},216:e=>{e.exports=function(e){var n=document.createElement("style");return e.setAttributes(n,e.attributes),e.insert(n,e.options),n}},565:(e,n,t)=>{e.exports=function(e){var n=t.nc;n&&e.setAttribute("nonce",n)}},795:e=>{e.exports=function(e){var n=e.insertStyleElement(e);return{update:function(t){!function(e,n,t){var r="";t.supports&&(r+="@supports (".concat(t.supports,") {")),t.media&&(r+="@media ".concat(t.media," {"));var o=void 0!==t.layer;o&&(r+="@layer".concat(t.layer.length>0?" ".concat(t.layer):""," {")),r+=t.css,o&&(r+="}"),t.media&&(r+="}"),t.supports&&(r+="}");var i=t.sourceMap;i&&"undefined"!=typeof btoa&&(r+="\n/*# sourceMappingURL=data:application/json;base64,".concat(btoa(unescape(encodeURIComponent(JSON.stringify(i))))," */")),n.styleTagTransform(r,e,n.options)}(n,e,t)},remove:function(){!function(e){if(null===e.parentNode)return!1;e.parentNode.removeChild(e)}(n)}}}},589:e=>{e.exports=function(e,n){if(n.styleSheet)n.styleSheet.cssText=e;else{for(;n.firstChild;)n.removeChild(n.firstChild);n.appendChild(document.createTextNode(e))}}}},n={};function t(r){var o=n[r];if(void 0!==o)return o.exports;var i=n[r]={id:r,exports:{}};return e[r](i,i.exports,t),i.exports}t.n=e=>{var n=e&&e.__esModule?()=>e.default:()=>e;return t.d(n,{a:n}),n},t.d=(e,n)=>{for(var r in n)t.o(n,r)&&!t.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:n[r]})},t.o=(e,n)=>Object.prototype.hasOwnProperty.call(e,n),t.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var r={};return(()=>{t.r(r),t.d(r,{collapse:()=>$,create:()=>O,default:()=>I,destroy:()=>z,expand:()=>P,render:()=>A,renderJSON:()=>N});var e=t(379),n=t.n(e),o=t(795),i=t.n(o),a=t(569),s=t.n(a),c=t(565),l=t.n(c),d=t(216),p=t.n(d),u=t(589),f=t.n(u),v=t(767),y={};function h(e){return Array.isArray(e)?"array":null===e?"null":typeof e}function m(e){return document.createElement(e)}y.styleTagTransform=f(),y.setAttributes=l(),y.insert=s().bind(null,"head"),y.domAPI=i(),y.insertStyleElement=p(),n()(v.Z,y),v.Z&&v.Z.locals&&v.Z.locals;const x="hidden",g="fa-caret-right",j="fa-caret-down";function b(e){e.children.forEach((e=>{e.el.classList.add(x),e.isExpanded&&b(e)}))}function E(e){e.children.forEach((e=>{e.el.classList.remove(x),e.isExpanded&&E(e)}))}function S(e){if(e.children.length>0){const n=e.el.querySelector(".fas");n&&n.classList.replace(g,j)}}function k(e){if(e.children.length>0){const n=e.el.querySelector(".fas");n&&n.classList.replace(j,g)}}function w(e){e.isExpanded?(e.isExpanded=!1,k(e),b(e)):(e.isExpanded=!0,S(e),E(e))}function L(e,n){n(e),e.children.length>0&&e.children.forEach((e=>{L(e,n)}))}function T(e={}){return{key:e.key||null,parent:e.parent||null,value:e.hasOwnProperty("value")?e.value:null,isExpanded:e.isExpanded||!1,type:e.type||null,children:e.children||[],el:e.el||null,depth:e.depth||0,dispose:null}}function M(e,n){if("object"==typeof e)for(let t in e){const r=T({value:e[t],key:t,depth:n.depth+1,type:h(e[t]),parent:n});n.children.push(r),M(e[t],r)}}function C(e){return"string"==typeof e?JSON.parse(e):e}function O(e){const n=C(e),t=T({value:n,key:h(n),type:h(n)});return M(n,t),t}function N(e,n){const t=C(e),r=createTree(t);return A(r,n),r}function A(e,n){const t=function(){const e=m("div");return e.className="json-container",e}();L(e,(function(e){e.el=function(e){let n=m("div");const t=e=>{const n=e.children.length;return"array"===e.type?`[${n}]`:"object"===e.type?`{${n}}`:null};if(e.children.length>0){n.innerHTML=function(e={}){const{key:n,size:t}=e;return`\n    \n  `}({key:e.key,size:t(e)});const r=n.querySelector(".caret-icon");e.dispose=function(e,n,t){return e.addEventListener(n,t),()=>e.removeEventListener(n,t)}(r,"click",(()=>w(e)))}else n.innerHTML=function(e={}){const{key:n,value:t,type:r}=e;return`\n    \n  `}({key:e.key,value:e.value,type:typeof e.value});const r=n.children[0];return null!==e.parent&&r.classList.add(x),r.style="margin-left: "+18*e.depth+"px;",r}(e),t.appendChild(e.el)})),n.appendChild(t)}function P(e){L(e,(function(e){e.el.classList.remove(x),e.isExpanded=!0,S(e)}))}function $(e){L(e,(function(n){n.isExpanded=!1,n.depth>e.depth&&n.el.classList.add(x),k(n)}))}function z(e){var n;L(e,(e=>{e.dispose&&e.dispose()})),(n=e.el.parentNode).parentNode.removeChild(n)}const I={render:A,create:O,renderJSON:N,expand:P,collapse:$,traverse:L,destroy:z}})(),r})()}));
\ No newline at end of file
diff --git a/static/js/less.min.js b/static/js/less.min.js
new file mode 120000
index 0000000..9ac4459
--- /dev/null
+++ b/static/js/less.min.js
@@ -0,0 +1 @@
+../../node_modules/less/dist/less.min.js
\ No newline at end of file
diff --git a/static/js/reconnecting-websocket.min.js b/static/js/reconnecting-websocket.min.js
new file mode 100644
index 0000000..3015099
--- /dev/null
+++ b/static/js/reconnecting-websocket.min.js
@@ -0,0 +1 @@
+!function(a,b){"function"==typeof define&&define.amd?define([],b):"undefined"!=typeof module&&module.exports?module.exports=b():a.ReconnectingWebSocket=b()}(this,function(){function a(b,c,d){function l(a,b){var c=document.createEvent("CustomEvent");return c.initCustomEvent(a,!1,!1,b),c}var e={debug:!1,automaticOpen:!0,reconnectInterval:1e3,maxReconnectInterval:3e4,reconnectDecay:1.5,timeoutInterval:2e3};d||(d={});for(var f in e)this[f]="undefined"!=typeof d[f]?d[f]:e[f];this.url=b,this.reconnectAttempts=0,this.readyState=WebSocket.CONNECTING,this.protocol=null;var h,g=this,i=!1,j=!1,k=document.createElement("div");k.addEventListener("open",function(a){g.onopen(a)}),k.addEventListener("close",function(a){g.onclose(a)}),k.addEventListener("connecting",function(a){g.onconnecting(a)}),k.addEventListener("message",function(a){g.onmessage(a)}),k.addEventListener("error",function(a){g.onerror(a)}),this.addEventListener=k.addEventListener.bind(k),this.removeEventListener=k.removeEventListener.bind(k),this.dispatchEvent=k.dispatchEvent.bind(k),this.open=function(b){h=new WebSocket(g.url,c||[]),b||k.dispatchEvent(l("connecting")),(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","attempt-connect",g.url);var d=h,e=setTimeout(function(){(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","connection-timeout",g.url),j=!0,d.close(),j=!1},g.timeoutInterval);h.onopen=function(){clearTimeout(e),(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","onopen",g.url),g.protocol=h.protocol,g.readyState=WebSocket.OPEN,g.reconnectAttempts=0;var d=l("open");d.isReconnect=b,b=!1,k.dispatchEvent(d)},h.onclose=function(c){if(clearTimeout(e),h=null,i)g.readyState=WebSocket.CLOSED,k.dispatchEvent(l("close"));else{g.readyState=WebSocket.CONNECTING;var d=l("connecting");d.code=c.code,d.reason=c.reason,d.wasClean=c.wasClean,k.dispatchEvent(d),b||j||((g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","onclose",g.url),k.dispatchEvent(l("close")));var e=g.reconnectInterval*Math.pow(g.reconnectDecay,g.reconnectAttempts);setTimeout(function(){g.reconnectAttempts++,g.open(!0)},e>g.maxReconnectInterval?g.maxReconnectInterval:e)}},h.onmessage=function(b){(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","onmessage",g.url,b.data);var c=l("message");c.data=b.data,k.dispatchEvent(c)},h.onerror=function(b){(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","onerror",g.url,b),k.dispatchEvent(l("error"))}},1==this.automaticOpen&&this.open(!1),this.send=function(b){if(h)return(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","send",g.url,b),h.send(b);throw"INVALID_STATE_ERR : Pausing to reconnect websocket"},this.close=function(a,b){"undefined"==typeof a&&(a=1e3),i=!0,h&&h.close(a,b)},this.refresh=function(){h&&h.close()}}return a.prototype.onopen=function(){},a.prototype.onclose=function(){},a.prototype.onconnecting=function(){},a.prototype.onmessage=function(){},a.prototype.onerror=function(){},a.debugAll=!1,a.CONNECTING=WebSocket.CONNECTING,a.OPEN=WebSocket.OPEN,a.CLOSING=WebSocket.CLOSING,a.CLOSED=WebSocket.CLOSED,a});
diff --git a/static/js/script.js b/static/js/script.js
index 88211bb..fd06ada 100644
--- a/static/js/script.js
+++ b/static/js/script.js
@@ -1,14 +1,68 @@
 var newDateObj = new Date();
 newDateObj = new Date(newDateObj.getTime() + 1000 * 20)
 
+backReqInt = 0;
+websocketFailed = false
+recoveryAttempts = 0;
+
+let socket = new ReconnectingWebSocket("ws://localhost:" + location.port);
+let ackdSessionToken = false
+let isFirstPacket = true
+
+let lastTime = "00:00:00";
+let timerCountdownFirst = true;
+
+socket.onopen = function (e) {
+  // alert("[open] Connection established");
+  //alert("Sending to server");
+  socket.send("new client");
+};
+
+socket.onmessage = function (event) {
+  // alert(`[message] Data received from server: ${event.data}`);
+  let inData = JSON.parse(event.data)
+  if (isFirstPacket) {
+    isFirstPacket = false
+    dataFame = JSON.parse(event.data);
+    timeDiff = new Date().getTime() - dataFame.srvTime
+  } else {
+    if (inData.sessionToken == dataFame.sessionToken) {
+      dataFame = JSON.parse(event.data);
+      timeDiff = new Date().getTime() - dataFame.srvTime
+    } else {
+      if (ackdSessionToken == false) {
+
+        ackdSessionToken = true
+
+        if (confirm("Session token mismatch, reload the page?")) {
+          location.reload();
+        }
+      }
+    }
+  }
+
+};
+
+socket.onclose = function (event) {
+  if (event.wasClean) {
+    console.log(`[close] Connection closed cleanly, code=${event.code} reason=${event.reason}`);
+  } else {
+    // e.g. server process killed or network down
+    // event.code is usually 1006 in this case
+    console.error('[close] Connection died');
+  }
+};
+
 allowFullscreen = true
+let dataFame = {}
+let timeDiff = 0
 
 function enterFullscreen(element) {
   if (element.requestFullscreen) {
     element.requestFullscreen();
-  } else if (element.msRequestFullscreen) {      // for IE11 (remove June 15, 2022)
+  } else if (element.msRequestFullscreen) {
     element.msRequestFullscreen();
-  } else if (element.webkitRequestFullscreen) {  // iOS Safari
+  } else if (element.webkitRequestFullscreen) {
     element.webkitRequestFullscreen();
   }
 }
@@ -28,7 +82,6 @@ function updateFullscreen() {
 }
 
 
-
 function msToTime(s, data) {
   isSmallerThenZero = false
   if (s < 0) {
@@ -69,93 +122,136 @@ function findProgessColor(colors, value) {
   return (resColor)
 }
 
-function handleUpdate() {
+function requestBackend() {
   resp = httpGet("/api/v1/data");
-
   resp.onloadend = function (e) {
-    if(resp.status == 200){
+    if (resp.status == 200) {
       resp = resp.responseText;
       var data = JSON.parse(resp);
-      document.getElementById("incomeData").innerHTML = JSON.stringify(data)
-      document.getElementById("timediff").innerHTML = new Date().getTime() - data.srvTime;
-      if(data.debug){
-        document.getElementById("timediff").style.display = "block";
-      }else{
-        document.getElementById("timediff").style.display = "none";
-      }
-  
-      if (data.defaultFullScreen && document.getElementById("initalDate").innerHTML.includes("true") && allowFullscreen) {
-        enterFullscreen(document.documentElement);
-        document.getElementById("initalDate").innerHTML = "false"
-      }
-  
-      if (data.showMessage) {
-        document.getElementById("overlay").style.display = "block";
-        document.getElementById("text").innerHTML = data.message
-      } else {
-        off()
-      }
-  
-      if (new Date().getTime() - data.messageAppearTime < 5000) {
-        if (!document.getElementById("text").classList.contains('blink')) {
-          document.getElementById("text").classList.add("blink")
-        }
-      } else {
-        if (document.getElementById("text").classList.contains('blink')) {
-          document.getElementById("text").classList.remove("blink")
-        }
-      }
-  
-  
-      if (data.mode == "clock") {
-        document.getElementById("timer").innerHTML = getTime();
-        document.getElementById("testImg").style.display = "none";
-        document.getElementById("wholeProgBar").style.display = "none";
-  
-      } else if (data.mode == "timer") {
-  
-        document.getElementById("wholeProgBar").style.display = "block";
-  
-        if (data.timerRunState) {
-          const now = new Date()
-          const diff = data.countdownGoal - now.getTime()
-          document.getElementById("timer").innerHTML = msToTime(diff, data);
-          if (diff > 0) {
-            document.getElementById("progBar").style.width = percentage(diff, data.timeAmountInital) + "%";
-          } else {
-            document.getElementById("progBar").style.width = "0%";
-          }
-  
-          document.getElementById("progBar").style.backgroundColor = findProgessColor(data.colorSegments, diff)
-          document.getElementById("testImg").style.display = "none";
-          if (data.enableColoredText) {
-            document.getElementById("timer").style.color = findProgessColor(data.textColors, diff)
-          } else {
-            document.getElementById("timer").style.color = "white"
-          }
-  
-        }
-        if (data.showTimeOnCountdown) {
-          document.getElementById("clockSec").innerHTML = getTime();
-        } else {
-          document.getElementById("clockSec").innerHTML = "";
-        }
-  
-      } else if (data.mode == "black") {
-        document.getElementById("timer").innerHTML = "";
-        document.getElementById("testImg").style.display = "none";
-        document.getElementById("wholeProgBar").style.display = "none";
-  
-      } else if (data.mode == "test") {
-        document.getElementById("timer").innerHTML = "";
-        document.getElementById("testImg").style.display = "block";
-        document.getElementById("progBar").style.display = "none";
-      }
+      timeDiff = new Date().getTime() - data.srvTime
+      dataFame = data;
+    }
+  }
+}
+
+let isSlowed = false
+
+function handleRecovery() {
+  var img = document.body.appendChild(document.createElement("img"));
+  img.onload = function () {
+    location.reload();
+  };
+  img.src = "SMPTE_Color_Bars.svg";
+}
+
+let recoInter = -1
+
+function handleUpdate() {
+  var data = dataFame;
+  document.getElementById("incomeData").innerHTML = JSON.stringify(data)
+  document.getElementById("timediff").innerHTML = timeDiff + " " + String(new Date().getTime() - data.srvTime);
+ 
+  if (data.debug) {
+    document.getElementById("timediff").style.display = "block";
+  } else {
+    document.getElementById("timediff").style.display = "none";
+  }
+
+  if (data.defaultFullScreen && document.getElementById("initalDate").innerHTML.includes("true") && allowFullscreen) {
+    enterFullscreen(document.documentElement);
+    document.getElementById("initalDate").innerHTML = "false"
+  }
+
+  if (data.showMessage) {
+    document.getElementById("overlay").style.display = "block";
+    document.getElementById("text").innerHTML = data.message
+  } else {
+    document.getElementById("overlay").style.display = "none";
+  }
+
+  if (data.showTimeOnCountdown && data.mode == "timer") {
+    document.getElementById("clockSec").innerHTML = getTime();
+  } else {
+    document.getElementById("clockSec").innerHTML = "";
+  }
+
+  if (new Date().getTime() - data.messageAppearTime < 5000) {
+    if (!document.getElementById("text").classList.contains('blink')) {
+      document.getElementById("text").classList.add("blink")
+    }
+  } else {
+    if (document.getElementById("text").classList.contains('blink')) {
+      document.getElementById("text").classList.remove("blink")
     }
-    
   }
 
 
+  if (data.mode == "clock") {
+    document.getElementById("timer").innerHTML = getTime();
+    document.getElementById("testImg").style.display = "none";
+    document.getElementById("wholeProgBar").style.display = "none";
+    document.getElementById("clockSec").innerHTML = "";
+    document.getElementById("timer").style.color = "white"
+    timerCountdownFirst = true;
+
+  } else if (data.mode == "timer") {
+    document.getElementById("wholeProgBar").style.display = "block";
+    const now = new Date()
+
+    if(timerCountdownFirst){
+      const diff = data.countdownGoal - now.getTime()
+      timerCountdownFirst = false
+      document.getElementById("timer").innerHTML = msToTime(diff, data);
+      if (diff > 0) {
+        document.getElementById("progBar").style.width = percentage(diff, data.timeAmountInital) + "%";
+      } else {
+        document.getElementById("progBar").style.width = "0%";
+      }
+
+      document.getElementById("progBar").style.backgroundColor = findProgessColor(data.colorSegments, diff)
+      document.getElementById("testImg").style.display = "none";
+      if (data.enableColoredText) {
+        document.getElementById("timer").style.color = findProgessColor(data.textColors, diff)
+      } else {
+        document.getElementById("timer").style.color = "white"
+      }
+    }
+
+    if (data.timerRunState) {
+      const diff = data.countdownGoal - now.getTime()
+      document.getElementById("timer").innerHTML = msToTime(diff, data);
+      lastTime = msToTime(diff, data);
+      if (diff > 0) {
+        document.getElementById("progBar").style.width = percentage(diff, data.timeAmountInital) + "%";
+      } else {
+        document.getElementById("progBar").style.width = "0%";
+      }
+
+      document.getElementById("progBar").style.backgroundColor = findProgessColor(data.colorSegments, diff)
+      document.getElementById("testImg").style.display = "none";
+      if (data.enableColoredText) {
+        document.getElementById("timer").style.color = findProgessColor(data.textColors, diff)
+      } else {
+        document.getElementById("timer").style.color = "white"
+      }
+
+    } else {
+      document.getElementById("timer").innerHTML = lastTime
+    }
+  } else if (data.mode == "black") {
+    timerCountdownFirst = true;
+    document.getElementById("timer").innerHTML = "";
+    document.getElementById("testImg").style.display = "none";
+    document.getElementById("wholeProgBar").style.display = "none";
+    document.getElementById("clockSec").innerHTML = "";
+
+  } else if (data.mode == "test") {
+    timerCountdownFirst = true;
+    document.getElementById("timer").innerHTML = "";
+    document.getElementById("testImg").style.display = "block";
+    document.getElementById("progBar").style.display = "none";
+    document.getElementById("clockSec").innerHTML = "";
+  }
 }
 
 function httpGet(theUrl) {
@@ -182,15 +278,9 @@ function getTime() {
   return time;
 }
 
-function on() {
-  document.getElementById("overlay").style.display = "block";
-}
 
-function off() {
-  document.getElementById("overlay").style.display = "none";
-}
-
-setInterval(handleUpdate, 200);
+// setInterval(requestBackend, 500);
+updateInter = setInterval(handleUpdate, 2);
 
 let temp = new URLSearchParams(window.location.search).get("smaller");
 
diff --git a/static/mdbootstrap/css b/static/mdbootstrap/css
new file mode 120000
index 0000000..5ada75b
--- /dev/null
+++ b/static/mdbootstrap/css
@@ -0,0 +1 @@
+../../node_modules/mdbootstrap/css/
\ No newline at end of file
diff --git a/static/mdbootstrap/js b/static/mdbootstrap/js
new file mode 120000
index 0000000..6a0f520
--- /dev/null
+++ b/static/mdbootstrap/js
@@ -0,0 +1 @@
+../../node_modules/mdbootstrap/js
\ No newline at end of file
diff --git a/templates/adminPanel.html b/templates/adminPanel.html
deleted file mode 100644
index 3e474ac..0000000
--- a/templates/adminPanel.html
+++ /dev/null
@@ -1,94 +0,0 @@
-
-
-
-
-
-   
-   
-
-  openCountdown - Admin 
-   
-   
-
-   
-
-
-
-
-
-  
-
-   
-  
-
-  
-
-  
-
-  
-
-  Play controls:
-  
-  
-  
-
-  Message: 
-  
-  
-  
-
-
-  
-
-
-
-
\ No newline at end of file
diff --git a/templates/errorPages/404.html b/templates/errorPages/404.html
new file mode 100644
index 0000000..edbc836
--- /dev/null
+++ b/templates/errorPages/404.html
@@ -0,0 +1,47 @@
+
+
+
+
+     
+     
+
+    openCountdown - Not found 
+     
+     
+
+    
+
+     
+
+
+    
+    
+
+     
+
+     
+
+
+
+    
+        
+             
+                404
+            
+        
+        
+            We're sorry, the page you were looking for isn't found here. 
+            The link you followed may either be broken or no longer exists. Please
+            check your spelling. 
+
+              Back home 
+         
+
+    
 
+
+
+
\ No newline at end of file
diff --git a/templates/newAdminPanel.html b/templates/newAdminPanel.html
new file mode 100644
index 0000000..6282241
--- /dev/null
+++ b/templates/newAdminPanel.html
@@ -0,0 +1,434 @@
+
+
+
+
+     
+     
+
+    openCountdown - Admin 
+     
+     
+
+
+     
+     
+
+    
+    
+    
+
+    
+    
+     
+     
+     
+     
+     
+    
+    
+
+    
+
+     
+
+
+
+
+    
+        
+            
+                
+                     
+                 
+                Sidebar 
+             
+            
+            
+            
+            openCountdown 
+        
 
+        
+            
+                
+                     
+                 
+            
+
+
+            
+                Home page 
+
+                
+                    
+                        
+                        
+                            
Mode 
+                                     
+                                  
+                            
+                                 
+                                Timer 
+
+                                 
+                                Clock 
+
+                                 
+                                Black 
+
+                                 
+                                Testimage 
+                            
+                            
+                            
+                            
+                            
+                                
+                                         
+                                      
+
+                                
+                                         
+                                      
+                                
+                                         
+                                         
+                                      
+                             
+                            
+                            
+                            
+                            
Messaging 
+                                     
+                                  
+
+                            
+
+                            
+                            
+                            
+                                 
+                             
+                        
+                    
+                
 
+                 
+                Timer 
+                
+
+
+                    
+                        
+                            00:05:00 
+                            00:10:00 
+                            00:15:00 
+                            00:20:00 
+                            00:25:00 
+                            00:30:00 
+                            00:35:00 
+                            00:40:00 
+                            00:45:00 
+                         
+                        
+                        
+                        
+                            Countdown to time
+                         
+                        
+                             
+                             
+
+                            
+
+                        
+                        
+                        
+                    
+                
 
+             
+
+
+
+
+
+            
+                Settings 
+                Show clock on Timer: 
+                 
+
+                Show Milliseconds on Timer: 
+                 
+
+                Show progressbar: 
+                 
+                
+                    Progressbar Colors 
+                    
+                    
+                        
+                        
+                            
+                                
+                                    Time 
+                                    Color 
+                                    Remove 
+                                 
+                             
+                            
+                        
+                    
 
+
+                    Add row 
+                          
+                
+                 
+                Enable Text Colors: 
+                 
+                
+                    Text Colors 
+                    
+
+                    
+                        
+                            
+                                
+                                    Time 
+                                    Color 
+                                    Remove 
+                                 
+                             
+                            
+                        
+                    
 
+                    Copy from progressbar
+                        colors 
+
+                    Add row 
+                    
+                 
+                 
+
+
+                  Apply
+                    settings 
+
+                  Save as
+                    startup settings (Layout
+                    only) 
+            
+
+            
+                Debug page 
+                
+                    
Attention 
+                    
This is a debug page which should only be used by professionals. Changing any options below might
+                        impact operation.
+                    
+                    
Proceed with caution. 
+                
 
+                Enable time variance display: 
+                 
+
+                Apply settings 
+                 
+                 
+                 
+
+                Host information 
+                
+
+                
+                Raw server reponse 
+                
+
+                
+                 
+                 
+                 
+                
+                    
+
+
+
+                    
+                        #VALUE# 
+                        
+                            
+                                 
+                                 
+                            
+                            
+                         
+                        
+                            
+                                    Remove
+                                  
+                         
+                     
+                 
+                
+
+                 
+             
+            
+                About 
+                Version:  
+                NodeJS Version:  
+
+             
+        
+    
+    
+    
+    
+
+
+
+
\ No newline at end of file
diff --git a/templates/timerPage.html b/templates/timerPage.html
index a08a0a1..faa3e30 100644
--- a/templates/timerPage.html
+++ b/templates/timerPage.html
@@ -11,14 +11,21 @@
      
 
      
+    
 
 
 
 
+
+
     
 
+    
+        Connection lost. Trying to reconnect...
+    
+
      
     
     
@@ -39,7 +46,7 @@
             
         
     
-    
+