diff --git a/.gitignore b/.gitignore
index a77fa96..8e8ab40 100644
--- a/.gitignore
+++ b/.gitignore
@@ -24,3 +24,5 @@ cover.png
 .vs/tranga-website/FileContentIndex/91a465d3-1190-42e0-95eb-fa3694744e58.vsidx
 .vs/tranga-website/v17/.wsuo
 .vs/VSWorkspaceState.json
+/node_modules/
+/.vite/
diff --git a/README.md b/README.md
index fed1b32..3df7b89 100644
--- a/README.md
+++ b/README.md
@@ -57,7 +57,9 @@ This repo makes HTTP-requests to the [Tranga-API](https://github.com/C9Glax/tran
 ### Built With
 
 - nginx
-- HTML, CSS, and barebones Javascript
+- vite
+- react
+- typescript
 - 💙 Blåhaj 🦈
 
 <p align="right">(<a href="#readme-top">back to top</a>)</p>
@@ -73,16 +75,6 @@ There is a single release:
 Download [docker-compose.yaml](https://github.com/C9Glax/tranga-website/blob/cuttingedge/docker-compose.yaml) and configure to your needs. 
 The `docker-compose` also includes [Tranga](https://github.com/C9Glax/tranga) as backend. For its configuration refer to the repo README.
 
-<!-- ROADMAP -->
-## Roadmap
-
-- [ ] ❓
-
-See the [open issues](https://github.com/C9Glax/tranga-website/issues) for a full list of proposed features (and known issues).
-
-<p align="right">(<a href="#readme-top">back to top</a>)</p>
-
-
 
 <!-- CONTRIBUTING -->
 ## Contributing
diff --git a/Screenshots/Screenshot 2023-09-08 at 20-03-13 Tranga.png b/Screenshots/Screenshot 2023-09-08 at 20-03-13 Tranga.png
deleted file mode 100644
index c30b3a8..0000000
Binary files a/Screenshots/Screenshot 2023-09-08 at 20-03-13 Tranga.png and /dev/null differ
diff --git a/Screenshots/Screenshot 2023-09-08 at 20-03-37 Tranga.png b/Screenshots/Screenshot 2023-09-08 at 20-03-37 Tranga.png
deleted file mode 100644
index 670743c..0000000
Binary files a/Screenshots/Screenshot 2023-09-08 at 20-03-37 Tranga.png and /dev/null differ
diff --git a/Screenshots/Screenshot 2023-09-08 at 20-03-45 Tranga.png b/Screenshots/Screenshot 2023-09-08 at 20-03-45 Tranga.png
deleted file mode 100644
index 2f38769..0000000
Binary files a/Screenshots/Screenshot 2023-09-08 at 20-03-45 Tranga.png and /dev/null differ
diff --git a/Screenshots/Screenshot 2023-09-08 at 20-03-58 Tranga.png b/Screenshots/Screenshot 2023-09-08 at 20-03-58 Tranga.png
deleted file mode 100644
index a287f41..0000000
Binary files a/Screenshots/Screenshot 2023-09-08 at 20-03-58 Tranga.png and /dev/null differ
diff --git a/Screenshots/Screenshot 2023-09-08 at 20-04-41 Tranga.png b/Screenshots/Screenshot 2023-09-08 at 20-04-41 Tranga.png
deleted file mode 100644
index 68c191f..0000000
Binary files a/Screenshots/Screenshot 2023-09-08 at 20-04-41 Tranga.png and /dev/null differ
diff --git a/Website/App.tsx b/Website/App.tsx
new file mode 100644
index 0000000..1788095
--- /dev/null
+++ b/Website/App.tsx
@@ -0,0 +1,132 @@
+import React, {useEffect, useState} from 'react';
+import Footer from "./modules/Footer";
+import Search from "./modules/Search";
+import Header from "./modules/Header";
+import MonitorJobsList from "./modules/MonitorJobsList";
+import './styles/index.css'
+import IFrontendSettings, {LoadFrontendSettings} from "./modules/interfaces/IFrontendSettings";
+import {useCookies} from "react-cookie";
+
+export default function App(){
+    const [, setCookie] = useCookies(['apiUri', 'jobInterval']);
+    const [connected, setConnected] = React.useState(false);
+    const [showSearch, setShowSearch] = React.useState(false);
+    const [frontendSettings, setFrontendSettings] = useState<IFrontendSettings>(LoadFrontendSettings());
+    const [updateInterval, setUpdateInterval] = React.useState<number>();
+    const [updateMonitorList, setUpdateMonitorList] = React.useState<Date>(new Date());
+
+    const apiUri =  frontendSettings.apiUri;
+
+    useEffect(() => {
+        checkConnection(apiUri).then(res => setConnected(res)).catch(() => setConnected(false));
+        if(updateInterval === undefined){
+            setUpdateInterval(setInterval(() => {
+                checkConnection(apiUri).then(res => setConnected(res)).catch(() => setConnected(false));
+            }, 500));
+        }else{
+            clearInterval(updateInterval);
+            setUpdateInterval(undefined);
+        }
+    }, [frontendSettings]);
+
+    function ChangeSettings(settings: IFrontendSettings) {
+        setFrontendSettings(settings);
+        setCookie('apiUri', settings.apiUri);
+        setCookie('jobInterval', settings.jobInterval);
+    }
+
+    const UpdateList = () => {setUpdateMonitorList(new Date())}
+
+    return(<div>
+        <Header apiUri={apiUri} backendConnected={connected} settings={frontendSettings} changeSettings={ChangeSettings}/>
+        {connected
+            ? <>
+                {showSearch
+                    ? <>
+                        <Search apiUri={apiUri} jobInterval={frontendSettings.jobInterval} onJobsChanged={UpdateList} closeSearch={() => setShowSearch(false)} />
+                        <hr/>
+                    </>
+                    : <></>}
+                <MonitorJobsList updateList={updateMonitorList} apiUri={apiUri} onStartSearch={() => setShowSearch(true)} onJobsChanged={UpdateList} connectedToBackend={connected} />
+            </>
+            : <>
+                <h1>No connection to the Backend.</h1>
+                <h3>Check the Settings ApiUri.</h3>
+            </>}
+        <Footer apiUri={apiUri} connectedToBackend={connected} />
+    </div>)
+}
+
+export function getData(uri: string) : Promise<object> {
+    return fetch(uri,
+        {
+            method: 'GET',
+            headers : {
+                'Content-Type': 'application/json',
+                'Accept': 'application/json'
+            }
+        })
+        .then(function(response){
+            if(!response.ok) throw new Error("Could not fetch data");
+            return response.json();
+        })
+        .catch(function(err){
+            console.error(`Error GETting Data ${uri}\n${err}`);
+            return Promise.reject();
+        });
+}
+
+export function postData(uri: string, content: object) : Promise<object> {
+    return fetch(uri,
+        {
+            method: 'POST',
+            headers : {
+                'Content-Type': 'application/json',
+                'Accept': 'application/json'
+            },
+            body: JSON.stringify(content)
+        })
+        .then(function(response){
+            if(!response.ok)
+                throw new Error("Could not fetch data");
+            let json = response.json();
+            return json.then((json) => json).catch(() => null);
+        })
+        .catch(function(err){
+            console.error(`Error POSTing Data ${uri}\n${err}`);
+            return Promise.reject();
+        });
+}
+
+export function deleteData(uri: string) : Promise<void> {
+    return fetch(uri,
+        {
+            method: 'DELETE',
+            headers : {
+                'Content-Type': 'application/json',
+                'Accept': 'application/json'
+            }
+        })
+        .then(() =>{
+            return Promise.resolve();
+        })
+        .catch(function(err){
+            console.error(`Error DELETEing Data ${uri}\n${err}`);
+            return Promise.reject();
+        });
+}
+
+export function isValidUri(uri: string) : boolean{
+    try {
+        new URL(uri);
+        return true;
+    } catch (err) {
+        return false;
+    }
+}
+
+export const checkConnection  = async (apiUri: string): Promise<boolean> =>{
+    return getData(`${apiUri}/v2/Ping`).then((result) => {
+        return result != null;
+    }).catch(() => Promise.reject());
+}
\ No newline at end of file
diff --git a/Website/apiConnector.js b/Website/apiConnector.js
deleted file mode 100644
index 64dca93..0000000
--- a/Website/apiConnector.js
+++ /dev/null
@@ -1,346 +0,0 @@
-let apiUri = `${window.location.protocol}//${window.location.host.split(':')[0]}:6531`
-
-if(getCookie("apiUri") != ""){
-    apiUri = getCookie("apiUri");
-}
-setCookie("apiUri", apiUri);
-
-function setCookie(cname, cvalue) {
-  const d = new Date();
-  d.setTime(d.getTime() + (365*24*60*60*1000));
-  let expires = "expires="+ d.toUTCString();
-  document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/;samesite=strict";
-}
-
-function getCookie(cname) {
-    let name = cname + "=";
-    let decodedCookie = decodeURIComponent(document.cookie);
-    let ca = decodedCookie.split(';');
-    for(let i = 0; i < ca.length; i++) {
-        let c = ca[i];
-        while (c.charAt(0) == ' ') {
-            c = c.substring(1);
-        }
-        if (c.indexOf(name) == 0) {
-            return c.substring(name.length, c.length);
-        }
-    }
-    return "";
-}
-
-async function GetData(uri){
-    let request = await fetch(uri, {
-        method: 'GET',
-        headers: {
-            'Accept': 'application/json'
-        }
-    });
-    let json = await request.json();
-    return json;
-}
-
-async function PostData(uri){
-    let request = await fetch(uri, {
-        method: 'POST'
-    });
-    //console.log(request);
-}
-
-function DeleteData(uri){
-    fetch(uri, {
-        method: 'DELETE'
-    });
-}
-
-async function Ping(){
-  let ret = await GetData(`${apiUri}/Ping`);
-  return ret;
-}
-
-async function GetAvailableControllers(){
-    var uri = apiUri + "/Connectors";
-    let json = await GetData(uri);
-    return json;
-}
-
-async function GetPublicationFromConnector(connector, title){
-  var uri;
-  if(title.includes("http")){
-    uri = `${apiUri}/Manga/FromConnector?connector=${connector}&url=${title}`;
-  }else{
-    uri = `${apiUri}/Manga/FromConnector?connector=${connector}&title=${title}`;
-  }
-  let json = await GetData(uri);
-  return json;
-}
-
-async function GetChapters(connector, internalId, language){
-    var uri = `${apiUri}/Manga/Chapters?connector=${connector}&internalId=${internalId}&translatedLanguage=${language}`;
-    let json = await GetData(uri);
-    return json;
-}
-
-function GetCoverUrl(internalId){
-  return `${apiUri}/Manga/Cover?internalId=${internalId}`;
-}
-
-async function GetAllJobs(){
-	var uri = `${apiUri}/Jobs`;
-	let json = await GetData(uri);
-	return json;
-}
-
-async function GetRunningJobs(){
-    var uri = `${apiUri}/Jobs/Running`;
-    let json = await GetData(uri);
-    return json;
-}
-
-async function GetWaitingJobs(){
-    var uri = `${apiUri}/Jobs/Waiting`;
-    let json = await GetData(uri);
-    return json;
-}
-
-async function GetMonitorJobs(){
-  var uri = `${apiUri}/Jobs/MonitorJobs`;
-  let json = await GetData(uri);
-  return json;
-}
-
-async function GetProgress(jobId){
-    var uri = `${apiUri}/Jobs/Progress?jobId=${jobId}`;
-    let json = await GetData(uri);
-    return json;
-}
-
-async function GetSettings(){
-    var uri = `${apiUri}/Settings`;
-    let json = await GetData(uri);
-    return json;
-}
-
-async function GetAvailableNotificationConnectors(){
-	var uri = `${apiUri}/NotificationConnectors/Types`;
-	let json = await GetData(uri);
-	return json;
-}
-
-async function GetNotificationConnectors(){
-	var uri = `${apiUri}/NotificationConnectors`;
-	let json = await GetData(uri);
-	return json;
-}
-
-async function GetAvailableLibraryConnectors(){
-	var uri = `${apiUri}/LibraryConnectors/Types`;
-	let json = await GetData(uri);
-	return json;
-}
-
-async function GetLibraryConnectors(){
-	var uri = `${apiUri}/LibraryConnectors`;
-	let json = await GetData(uri);
-	return json;
-}
-
-async function GetRateLimits() {
-    var uri = `${apiUri}/Settings/customRequestLimit`
-    let json = await GetData(uri);
-    return json;
-}
-
-function CreateMonitorJob(connector, internalId, language){
-    var uri = `${apiUri}/Jobs/MonitorManga?connector=${connector}&internalId=${internalId}&interval=03:00:00&translatedLanguage=${language}`;
-    PostData(uri);
-}
-
-function CreateDownloadNewChaptersJob(connector, internalId, language){
-    var uri = `${apiUri}/Jobs/DownloadNewChapters?connector=${connector}&internalId=${internalId}&translatedLanguage=${language}`;
-    PostData(uri);
-}
-
-function StartJob(jobId){
-    var uri = `${apiUri}/Jobs/StartNow?jobId=${jobId}`;
-    PostData(uri);
-}
-
-function UpdateDownloadLocation(downloadLocation){
-    var uri = `${apiUri}/Settings/UpdateDownloadLocation?downloadLocation=${downloadLocation}`;
-    PostData(uri);	
-}
-
-function RefreshLibraryMetadata() {
-    var uri = `${apiUri}/Jobs/UpdateMetadata`;
-    PostData(uri);
-}
-
-async function DownloadLogs() {
-    var uri = `${apiUri}/LogFile`;
-
-    //Below taken from https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream
-    fetch(uri)
-    .then((response) => response.body)
-    .then((rb) => {
-      const reader = rb.getReader();
-  
-      return new ReadableStream({
-        start(controller) {
-          // The following function handles each data chunk
-          function push() {
-            // "done" is a Boolean and value a "Uint8Array"
-            reader.read().then(({ done, value }) => {
-              // If there is no more data to read
-              if (done) {
-                console.log("done", done);
-                controller.close();
-                return;
-              }
-              // Get the data and send it to the browser via the controller
-              controller.enqueue(value);
-              // Check chunks by logging to the console
-              console.log(done, value);
-              push();
-            });
-          }
-  
-          push();
-        },
-      });
-    })
-    .then((stream) =>
-      // Respond with our stream
-      new Response(stream, { headers: { "Content-Type": "text/html" } }).text(),
-    )
-    .then((result) => {
-      // Do things with result
-      //console.log(result);
-
-      //Below download taken from https://stackoverflow.com/questions/3665115/how-to-create-a-file-in-memory-for-user-to-download-but-not-through-server
-      var element = document.createElement('a');
-      element.setAttribute('href', 'data:text/plain;charset-utf-8,' + encodeURIComponent(result));
-      var newDate = new Date();
-      var filename = "Tranga_Logs_" + newDate.today() + "_" + newDate.timeNow() + ".log";
-      element.setAttribute('download', filename);
-      element.click();
-    });
-}
-
-//Following date-time code taken from: https://stackoverflow.com/questions/10211145/getting-current-date-and-time-in-javascript
-// For todays date;
-Date.prototype.today = function () { 
-    return ((this.getDate() < 10)?"0":"") + this.getDate() +"/"+(((this.getMonth()+1) < 10)?"0":"") + (this.getMonth()+1) +"/"+ this.getFullYear();
-}
-
-// For the time now
-Date.prototype.timeNow = function () {
-     return ((this.getHours() < 10)?"0":"") + this.getHours() +"_"+ ((this.getMinutes() < 10)?"0":"") + this.getMinutes() +"_"+ ((this.getSeconds() < 10)?"0":"") + this.getSeconds();
-}
-
-//Komga
-function UpdateKomga(komgaUrl, komgaAuth){
-    var uri = `${apiUri}/LibraryConnectors/Update?libraryConnector=Komga&komgaUrl=${komgaUrl}&komgaAuth=${komgaAuth}`;
-    PostData(uri);
-}
-
-function ResetKomga(){
-    var uri = `${apiUri}/LibraryConnectors/Reset?libraryConnector=Komga`;
-    PostData(uri);
-}
-
-function TestKomga(komgaUrl, komgaAuth){
-    var uri = `${apiUri}/LibraryConnectors/Test?libraryConnector=Komga&komgaUrl=${komgaUrl}&komgaAuth=${komgaAuth}`;
-    PostData(uri);
-}
-
-
-//Kavita
-function UpdateKavita(kavitaUrl, kavitaUsername, kavitaPassword){
-    var uri = `${apiUri}/LibraryConnectors/Update?libraryConnector=Kavita&kavitaUrl=${kavitaUrl}&kavitaUsername=${kavitaUsername}&kavitaPassword=${kavitaPassword}`;
-    PostData(uri);
-}
-
-function ResetKavita(){
-    var uri = `${apiUri}/LibraryConnectors/Reset?libraryConnector=Kavita`;
-    PostData(uri);
-}
-
-function TestKavita(kavitaUrl, kavitaUsername, kavitaPassword){
-    var uri = `${apiUri}/LibraryConnectors/Test?libraryConnector=Kavita&kavitaUrl=${kavitaUrl}&kavitaUsername=${kavitaUsername}&kavitaPassword=${kavitaPassword}`;
-    PostData(uri);
-}
-
-//Gotify
-function UpdateGotify(gotifyUrl, gotifyAppToken){
-    var uri = `${apiUri}/NotificationConnectors/Update?notificationConnector=Gotify&gotifyUrl=${gotifyUrl}&gotifyAppToken=${gotifyAppToken}`;
-    PostData(uri);
-}
-
-function ResetGotify(){
-    var uri = `${apiUri}/NotificationConnectors/Reset?notificationConnector=Gotify`;
-    PostData(uri);
-}
-
-function TestGotify(gotifyUrl, gotifyAppToken){
-    var uri = `${apiUri}/NotificationConnectors/Test?notificationConnector=Gotify&gotifyUrl=${gotifyUrl}&gotifyAppToken=${gotifyAppToken}`;
-    PostData(uri);
-}
-
-//LunaSea
-function UpdateLunaSea(lunaseaWebhook){
-    var uri = `${apiUri}/NotificationConnectors/Update?notificationConnector=LunaSea&lunaseaWebhook=${lunaseaWebhook}`;
-    PostData(uri);
-}
-
-function ResetLunaSea(){
-    var uri = `${apiUri}/NotificationConnectors/Reset?notificationConnector=LunaSea`;
-    PostData(uri);
-}
-
-function TestLunaSea(lunaseaWebhook){
-    var uri = `${apiUri}/NotificationConnectors/Test?notificationConnector=LunaSea&lunaseaWebhook=${lunaseaWebhook}`;
-    PostData(uri);
-}
-
-//Ntfy
-function UpdateNtfy(ntfyEndpoint, ntfyAuth){
-    var uri = `${apiUri}/NotificationConnectors/Update?notificationConnector=Ntfy&ntfyUrl=${ntfyEndpoint}&ntfyAuth=${ntfyAuth}`;
-    PostData(uri);
-}
-
-function ResetNtfy(){
-    var uri = `${apiUri}/NotificationConnectors/Reset?notificationConnector=Ntfy`;
-    PostData(uri);
-}
-
-function TestNtfy(ntfyEndpoint, ntfyAuth){
-    var uri = `${apiUri}/NotificationConnectors/Test?notificationConnector=Ntfy&ntfyUrl=${ntfyEndpoint}&ntfyAuth=${ntfyAuth}`;
-    PostData(uri);
-}
-
-function UpdateUserAgent(userAgent){
-    var uri = `${apiUri}/Settings/userAgent?userAgent=${userAgent}`;
-    PostData(uri);
-}
-
-function UpdateRateLimit(byteValue, rateLimit) {
-    var uri = `${apiUri}/Settings/customRequestLimit?requestType=${byteValue}&requestsPerMinute=${rateLimit}`;
-    PostData(uri);
-}
-
-function RemoveJob(jobId){
-    var uri = `${apiUri}/Jobs?jobId=${jobId}`;
-    DeleteData(uri);
-}
-
-function CancelJob(jobId){
-    var uri = `${apiUri}/Jobs/Cancel?jobId=${jobId}`;
-    PostData(uri);
-}
-
-async function GetLogmessages(count){
-	var uri = `${apiUri}/LogMessages?count=${count}`;
-	let json = await GetData(uri);
-    console.log(json);
-	return json;
-}
\ No newline at end of file
diff --git a/Website/index.html b/Website/index.html
index 1bb8c4c..d0145cc 100644
--- a/Website/index.html
+++ b/Website/index.html
@@ -1,301 +1,13 @@
-<!DOCTYPE html>
+<!DOCTYPE html>
 <html lang="en">
 <head>
-  <meta charset="UTF-8">
-  <title>Tranga</title>
-  <link id='basestyle' rel="stylesheet" href="styles/base.css">
-  <link id='librarystyle' rel="stylesheet" href="styles/style_default.css">
-  <link rel="icon" type="image/x-icon" href="favicon.ico">
+    <meta charset="UTF-8">
+    <title>Tranga</title>
+    <link rel="icon" type="image/x-icon" href="favicon.ico">
+    <link rel="stylesheet" href="styles/index.css">
 </head>
 <body>
-  <wrapper>
-
-    <topbar>
-      <titlebox>
-        <img alt="website image is Blahaj" src="media/blahaj.png">
-        <span>Tranga</span>
-      </titlebox>
-      <spacer></spacer>
-      <img id="filterFunnel" src="media/filter-funnel.svg" height="50%" alt="filterFunnel">
-      <img id="settingscog" src="media/settings-cogwheel.svg" height="100%" alt="settingscog">
-    </topbar>
-
-    <filter-box id="filterBox">
-      <border-bar>
-        <popup-title>Filter by: </popup-title>
-        <popup-close onclick="filterBox.classList.toggle('animate')" >&times</popup-close>
-      </border-bar>
-      <popup-content id="filterContent">
-        <div class="popup-section">
-          NAME:
-          <div class="section-content">
-            <label for="searchbox"></label><input id="searchbox" placeholder="Title" type="text">
-          </div>
-        </div>
-        <div class = "popup-section">
-          CONNECTOR:
-          <div class="section-content" id="connectorFilterBox">
-          </div>
-        </div>
-        <div class = "popup-section">
-          STATUS:
-          <div class="section-content" id="statusFilterBox">
-          </div>
-        </div>
-      </popup-content>
-      <border-bar-button onclick="ClearFilter()" class="clearFilter">Clear Filter</border-bar-button>
-    </filter-box>
-
-
-    <viewport>
-      <div id="loaderdiv">
-        <blur-background></blur-background>
-        <div id="loader"></div>
-        <p id="loaderText">Check your Settings > API-URI</p>
-      </div>
-      <content>
-        <div id="addPublication">
-          <p>+</p>
-        </div>
-        <publication onclick="ShowNewMangaSearch()">
-          <img alt="cover" src="media/cover.jpg">
-          <publication-information>
-            <connector-name class="pill">Sample</connector-name>
-            <publication-name>Best Manga there is</publication-name>
-          </publication-information>
-        </publication>
-      </content>
-      
-      <popup id="newMangaPopup">
-        <blur-background id="blurBackgroundNewMangaPopup" onclick="newMangaPopup.style.display = 'none';"></blur-background>
-        <div id="newMangaPopupSelector">
-          <select id="newMangaConnector" />
-          <input type="text" placeholder="Title" id="newMangaTitle" />
-          <select id="newMangaTranslatedLanguage">
-            <option selected="selected">en</option>
-            <option>it</option>
-            <option>de</option>
-          </select>
-        </div>
-        <div id="newMangaResult"></div>
-      </popup>
-      
-      <popup id="settingsPopup">
-        <blur-background id="blurBackgroundSettingsPopup" onclick="settingsPopup.style.display = 'none';"></blur-background>
-        <popup-window>
-          <border-bar>
-            <popup-title>Settings</popup-title>
-            <popup-close onclick="settingsPopup.style.display = 'none'">&times</popup-close>
-          </border-bar>
-          <popup-content>
-
-            <div class="popup-section">
-              TRANGA
-              <div class="section-content">
-                <div class="section-item dyn-height">
-                  <span class="title">API Settings</span>
-                  <row><label for="settingApiUri">API URI:</label><input placeholder="https://" type="text" id="settingApiUri"></row>
-                  <row><label for="userAgent">User Agent:</label><input placeholder="UserAgent" id="userAgent" type="text"></row>
-                  <row>
-                    <border-bar-button class="section" onclick="RefreshLibraryMetadata()">Refresh Library Metadata</border-bar-button>
-                    <border-bar-button class="section" onclick="DownloadLogs()">Download Logs</border-bar-button>
-                  </row>
-                </div>
-                <div class="section-item dyn-height">
-                  <span class="title">Rate Limits</span>
-                  <row><label for="DefaultRL">Default:</label><input id="defaultRL" type="text" ></row>
-                  <row><label for="CoverRL">Manga Covers:</label><input id="coverRL" type="text"></row>
-                  <row><label for="ImageRL">Manga Images:</label><input id="imageRL" type="text"></row>
-                  <row><label for="InfoRL">Manga Info:</label><input id="infoRL" type="text"></row>
-                </div>
-                <div class="section-item dyn-height">
-                  <span class="title">Appearance</span>
-                  <row><label for="cssStyle">Library Style:</label><select id="cssStyle">
-                    <option id="card_compact" value="card_compact">Cards (Compact)</option>
-                    <option id="card_hover" value="card_hover">Cards (Hover)</option>
-                  </select></row>
-                </div>
-              </div>
-            </div>
-
-            <div class="popup-section">
-              MANGA SOURCES
-              <div class="section-content">
-                <!-- <div class="section-item dyn-height">
-                  <span class="title"><img src="connector-icons/manganato.png"><a href="https://manganato.com">MangaNato</a></span>
-
-                </div> -->
-                <!-- <div class="section-item dyn-height">
-                  <span class="title"><img src="connector-icons/mangasee.png"><a href="https://mangasee123.com">MangaSee</a></span>
-                  
-                </div> -->
-                <div class="section-item dyn-height">
-                  <span class="title"><img src="connector-icons/mangadex-logo.svg"><a href="https://mangadex.org">MangaDex</a></span>
-                  <row><label for="mDexAuthorRL">Author Rate Limit:</label><input id="mDexAuthorRL" type="text"></row>
-                  <row><label for="mDexFeedRL">Feed Rate Limit:</label><input id="mDexFeedRL" type="text"></row>
-                  <row><label for="mDexImageRL">Image Rate Limit:</label><input id="mDexImageRL" type="text"></row>
-                </div>
-                <!-- <div class="section-item dyn-height">
-                  <span class="title"><img src="connector-icons/mangakatana.png"><a href="https://mangakatana.com">MangaKatana</a></span>
-                  
-                </div> -->
-                <!-- <div class="section-item dyn-height">
-                  <span class="title"><img src="connector-icons/mangaworld.png"><a href="https://www.mangaworld.ac">MangaWorld</a></span>
-                  
-                </div> -->
-                <!-- <div class="section-item dyn-height">
-                  <span class="title"><img src="connector-icons/bato.ico"><a href="https://bato.to">Bato</a></span>
-                  
-                </div> -->
-                <!-- <div class="section-item dyn-height">
-                  <span class="title"><img src="connector-icons/mangalife.png"><a href="https://www.manga4life.com">MangaLife</a></span>
-                  
-                </div> -->
-              </div>
-              
-            </div>
-
-            <div class="popup-section">
-              LIBRARY CONNECTORS
-              <div class="section-content">
-                <div class="section-item">
-                  <span class="title"><img src='connector-icons/komga.svg'>Komga<connector-configured id="komgaConfigured"></connector-configured></span>
-                  <label for="komgaUrl"></label><input placeholder="URL" id="komgaUrl" type="text">
-                  <label for="komgaUsername"></label><input placeholder="Username" id="komgaUsername" type="text">
-                  <label for="komgaPassword"></label><input placeholder="Password" id="komgaPassword" type="password">
-                  <div class="section-buttons-container">
-                      <span onclick="TestKomga(komgaUrl.value, utf8_to_b64(`${komgaUsername.value}:${komgaPassword.value}`))" class='section-button' id="test-connector">Test</span>
-                      <span onclick="ClearKomga()" class='section-button' id="reset">Reset</span>
-                      <span onclick="UpdateKomga(komgaUrl.value, utf8_to_b64(`${komgaUsername.value}:${komgaPassword.value}`))" class='section-button'>Apply</span>
-                  </div>
-                </div>
-                <div class="section-item">
-                  <span class="title"><img src='connector-icons/kavita.png'>Kavita<connector-configured id="kavitaConfigured"></connector-configured></span>
-                  <label for="kavitaUrl"></label><input placeholder="URL" id="kavitaUrl" type="text">
-                  <label for="kavitaUsername"></label><input placeholder="Username" id="kavitaUsername" type="text">
-                  <label for="kavitaPassword"></label><input placeholder="Password" id="kavitaPassword" type="password">
-                  <div class="section-buttons-container">
-                    <span onclick="TestKavita(kavitaUrl.value, kavitaUsername.value, kavitaPassword.value)" class='section-button' id="test-connector">Test</span>
-                    <span onclick="ClearKavita()" class='section-button' id="reset">Reset</span>
-                    <span onclick="UpdateKavita(kavitaUrl.value, kavitaUsername.value, kavitaPassword.value)" class='section-button'>Apply</span>
-                </div>
-                </div>
-              </div>
-            </div>
-
-            <div class="popup-section">
-              NOTIFICATION CONNECTORS
-                <div class="section-content">
-                  <div class="section-item">
-                    <span class="title"><img src='connector-icons/gotify-logo.png'>Gotify<connector-configured id="gotifyConfigured"></connector-configured></span>
-                    <label for="gotifyUrl"></label><input placeholder="URL" id="gotifyUrl" type="text">
-                    <label for="gotifyAppToken"></label><input placeholder="App-Token" id="gotifyAppToken" type="text">
-                    <div class="section-buttons-container">
-                      <span onclick="TestGotify(gotifyUrl.value, gotifyAppToken.value)" class='section-button' id="test-connector">Test</span>
-                      <span onclick="ClearGotify()" class='section-button' id="reset">Reset</span>
-                      <span onclick="UpdateGotify(gotifyUrl.value, gotifyAppToken.value)" class='section-button'>Apply</span>
-                  </div>
-                  </div>
-                  <div class="section-item">
-                    <span class="title"><img src='connector-icons/lunasea.png'>LunaSea<connector-configured id="lunaseaConfigured"></connector-configured></span>
-                    <label for="lunaseaWebhook"></label><input placeholder="device/:id or user/:id" id="lunaseaWebhook" type="text">
-                    <div class="section-buttons-container">
-                      <span onclick="TestLunaSea(lunaseaWebhook.value);" class='section-button' id="test-connector">Test</span>
-                      <span onclick="ClearLunasea()" class='section-button' id="reset">Reset</span>
-                      <span onclick="UpdateLunaSea(lunaseaWebhook.value);" class='section-button'>Apply</span>
-                  </div>
-                  </div>
-                  <div class="section-item">
-                    <span class="title"><img src='connector-icons/ntfy.svg'>Ntfy<connector-configured id="ntfyConfigured"></connector-configured></span>
-                    <label for="ntfyEndpoint"></label><input placeholder="URL" id="ntfyEndpoint" type="text">
-                    <label for="ntfyAuth"></label><input placeholder="Auth" id="ntfyAuth" type="text">
-                    <div class="section-buttons-container">
-                      <span onclick="TestNtfy(ntfyEndpoint.value, ntfyAuth.value);" class='section-button' id="test-connector">Test</span>
-                      <span onclick="ClearNtfy()" class='section-button' id="reset">Reset</span>
-                      <span onclick="UpdateNtfy(ntfyEndpoint.value, ntfyAuth.value);" class='section-button'>Apply</span>
-                  </div>
-                  </div>
-                </div>
-            </div> 
-          </popup-content>
-
-          <border-bar>
-            <div class="button-container">
-              <border-bar-button class="primary" onclick="UpdateSettings()">Apply Settings</border-bar-button>
-            </div>
-          </border-bar>
-
-        </popup-window>
-      </popup>
-		
-	  <popup id="publicationViewerPopup">
-        <blur-background id="blurBackgroundPublicationPopup" onclick="publicationViewerPopup.style.display= 'none';"></blur-background>
-        <publication-viewer>
-          <img id="pubviewcover" src="media/cover.jpg" alt="cover">
-          <publication-details>
-            <publication-name id="publicationViewerName">Best Manga there is</publication-name>
-            <publication-tags id="publicationViewerTags">A Manga</publication-tags>
-            <publication-author id="publicationViewerAuthor">Glax</publication-author>
-            <publication-description id="publicationViewerDescription">
-              An interesting description. The description is very intriguing, yet wholesome.
-            </publication-description>
-            <publication-interactions>
-              <publication-starttask id="startJobButton">Start Job ▶️</publication-starttask>
-              <publication-canceltask id="cancelJobButton">Cancel Job ❌</publication-canceltask>
-              <publication-delete id="deleteJobButton">Delete Job 🗑️</publication-delete>
-              <publication-add id="createMonitorJobButton">Monitor ➕</publication-add>
-              <publication-add id="createDownloadChapterJobButton">Download Chapter 📥</publication-add>
-            </publication-interactions>
-          </publication-details>
-        </publication-viewer>
-      </popup>
-
-      <popup id="jobStatusView">
-        <blur-background id="blurBackgroundSettingsPopup" onclick="jobStatusView.style.display = 'none';"></blur-background>
-        <popup-window>
-          <border-bar>
-            <popup-title>Jobs</popup-title>
-            <popup-close onclick="jobStatusView.style.display = 'none'">&times</popup-close>
-          </border-bar>
-          <popup-content>
-
-            <div class="popup-section">
-              RUNNING JOBS
-              <div class="section-content" id="jobStatusRunning">
-                
-              </div>
-            </div>
-
-            <div class="popup-section">
-              QUEUED JOBS
-              <div class="section-content" id="jobStatusWaiting">
-                
-              </div>
-            </div>
-
-          </popup-content>
-
-          <border-bar>
-            <!-- <div class="button-container">
-              <border-bar-button class="primary" onclick="UpdateSettings()">Apply Settings</border-bar-button>
-            </div> -->
-          </border-bar>
-
-        </popup-window>
-    </viewport>
-	  
-    <footer>
-      <div onclick="ShowJobQueue();">
-        <img src="media/running.svg" alt="running"><div id="jobsRunningTag">0</div>
-      </div>
-      <div onclick="ShowJobQueue();">
-        <img src="media/queue.svg" alt="queue"><div id="jobsQueuedTag">0</div>
-      </div>
-      <p id="madeWith">Made with Blåhaj 🦈</p>
-    </footer> 
-  </wrapper>
-  
-  <script src="apiConnector.js"></script>
-  <script src="interaction.js"></script>
+    <div id="app"></div>
+    <script type="module" src="index.jsx"></script>
 </body>
 </html>
\ No newline at end of file
diff --git a/Website/index.jsx b/Website/index.jsx
new file mode 100644
index 0000000..de60bc4
--- /dev/null
+++ b/Website/index.jsx
@@ -0,0 +1,7 @@
+import React from 'react';
+import { createRoot } from 'react-dom/client';
+import App from './App.tsx';
+
+const domNode = document.getElementById('app');
+const root = createRoot(domNode);
+root.render(<App />);
\ No newline at end of file
diff --git a/Website/interaction.js b/Website/interaction.js
deleted file mode 100644
index 889ade9..0000000
--- a/Website/interaction.js
+++ /dev/null
@@ -1,885 +0,0 @@
-let monitoringJobsCount = 0;
-let runningJobs = [];
-let waitingJobs = [];
-let notificationConnectorTypes = [];
-let libraryConnectorTypes = [];
-let selectedManga;
-let selectedJob;
-let searchMatch;
-
-let connectorMatch = [];
-let connectorNameMatch;
-let statusMatch = [];
-let statusNameMatch = [];
-
-const searchBox = document.querySelector("#searchbox");
-const settingsPopup = document.querySelector("#settingsPopup");
-const filterBox = document.querySelector("#filterBox");
-const settingsCog = document.querySelector("#settingscog");
-const filterFunnel = document.querySelector("#filterFunnel");
-const tasksContent = document.querySelector("content");
-const createMonitorTaskButton = document.querySelector("#createMonitoJobButton");
-const createDownloadChapterTaskButton = document.querySelector("#createDownloadChapterJobButton");
-const startJobButton = document.querySelector("#startJobButton");
-const cancelJobButton = document.querySelector("#cancelJobButton");
-const deleteJobButton = document.querySelector("#deleteJobButton");
-
-//Manga viewer popup
-const mangaViewerPopup = document.querySelector("#publicationViewerPopup");
-const mangaViewerWindow = document.querySelector("publication-viewer");
-const mangaViewerDescription = document.querySelector("#publicationViewerDescription");
-const mangaViewerName = document.querySelector("#publicationViewerName");
-const mangaViewerTags = document.querySelector("#publicationViewerTags");
-const mangaViewerAuthor = document.querySelector("#publicationViewerAuthor");
-const mangaViewCover = document.querySelector("#pubviewcover");
-
-//General Rate Limits
-const defaultRL = document.querySelector("#defaultRL");
-const coverRL = document.querySelector("#coverRL");
-const imageRL = document.querySelector("#imageRL");
-const infoRL = document.querySelector("#infoRL");
-
-//MangaDex Rate Limits
-const mDexAuthorRL = document.querySelector("#mDexAuthorRL");
-const mDexFeedRL = document.querySelector("#mDexFeedRL");
-const mDexImageRL = document.querySelector("#mDexImageRL");
-
-//Komga
-const settingKomgaUrl = document.querySelector("#komgaUrl");
-const settingKomgaUser = document.querySelector("#komgaUsername");
-const settingKomgaPass = document.querySelector("#komgaPassword");
-
-//Kavita
-const settingKavitaUrl = document.querySelector("#kavitaUrl");
-const settingKavitaUser = document.querySelector("#kavitaUsername");
-const settingKavitaPass = document.querySelector("#kavitaPassword");
-
-//Gotify
-const settingGotifyUrl = document.querySelector("#gotifyUrl");
-const settingGotifyAppToken = document.querySelector("#gotifyAppToken");
-
-//Lunasea
-const settingLunaseaWebhook = document.querySelector("#lunaseaWebhook");
-
-//Ntfy
-const settingNtfyEndpoint = document.querySelector("#ntfyEndpoint");
-const settingNtfyAuth = document.querySelector("#ntfyAuth");
-
-//Connector Configured
-const settingKomgaConfigured = document.querySelector("#komgaConfigured");
-const settingKavitaConfigured = document.querySelector("#kavitaConfigured");
-const settingGotifyConfigured = document.querySelector("#gotifyConfigured");
-const settingLunaseaConfigured = document.querySelector("#lunaseaConfigured");
-const settingNtfyConfigured = document.querySelector("#ntfyConfigured");
-
-const settingUserAgent = document.querySelector("#userAgent");
-const settingApiUri = document.querySelector("#settingApiUri");
-const settingCSSStyle = document.querySelector('#cssStyle');
-const newMangaPopup = document.querySelector("#newMangaPopup");
-const newMangaConnector = document.querySelector("#newMangaConnector");
-const newMangaTitle = document.querySelector("#newMangaTitle");
-const newMangaResult = document.querySelector("#newMangaResult");
-const newMangaTranslatedLanguage = document.querySelector("#newMangaTranslatedLanguage");
-
-//Jobs
-const jobsRunningTag = document.querySelector("#jobsRunningTag");
-const jobsQueuedTag = document.querySelector("#jobsQueuedTag");
-const loaderdiv = document.querySelector('#loaderdiv');
-const jobStatusView = document.querySelector("#jobStatusView");
-const jobStatusRunning = document.querySelector("#jobStatusRunning");
-const jobStatusWaiting = document.querySelector("#jobStatusWaiting");
-
-function Setup(){
-  Ping().then((ret) => {
-    loaderdiv.style.display = 'none';
-    
-    GetAvailableNotificationConnectors().then((json) => {
-      //console.log(json);
-      json.forEach(connector => {
-        notificationConnectorTypes[connector.Key] = connector.Value;
-      });
-    });
-
-    GetAvailableLibraryConnectors().then((json) => {
-      //console.log(json);
-      json.forEach(connector => {
-        libraryConnectorTypes[connector.Key] = connector.Value;
-      });
-    });
-
-    GetAvailableControllers().then((json) => {
-      //console.log(json);
-      newMangaConnector.replaceChildren();
-      connectorFilterBox = document.querySelector("#connectorFilterBox");
-      connectorFilterBox.replaceChildren();
-      json.forEach(connector => {
-        //Add the connector to the New Manga dropdown        
-        var option = document.createElement('option');
-        option.value = connector;
-        option.innerText = connector;
-        newMangaConnector.appendChild(option);
-
-        //Add the connector to the filter box
-        connectorFilter = document.createElement('connector-name');
-        connectorFilter.innerText = connector;
-        connectorFilter.className = "pill";
-        connectorFilter.style.backgroundColor = stringToColour(connector);
-
-        connectorFilter.addEventListener("click", (event) => {
-          ToggleFilterConnector(connector, event);
-        });
-        connectorFilterBox.appendChild(connectorFilter);
-      });
-    });
-
-    //Add the publication status options to the filter bar
-    publicationStatusOptions = ["Ongoing", "Completed", "On Hiatus", "Cancelled", "Upcoming", "Status Unavailable"]; 
-    statusFilterBox = document.querySelector("#statusFilterBox");
-    statusFilterBox.replaceChildren();
-    publicationStatusOptions.forEach(publicationStatus => {
-      var releaseStatus = document.createElement('status-filter');
-      releaseStatus.innerText = publicationStatus;
-      releaseStatus.setAttribute("release-status", publicationStatus);
-      releaseStatus.addEventListener("click", (event) => {
-        ToggleFilterStatus(publicationStatus, event);
-      });
-
-      statusFilterBox.appendChild(releaseStatus);
-    });
-    
-    ResetContent();
-    UpdateJobs();
-    GetSettings().then((json) => {
-      //console.log(json);
-      settingApiUri.placeholder = apiUri;
-    });
-    GetRateLimits().then((json) => {
-      defaultRL.placeholder = json.Default + ' Requests/Minute';
-      coverRL.placeholder = json.MangaCover + ' Requests/Minute';
-      imageRL.placeholder = json.MangaImage + ' Requests/Minute';
-      infoRL.placeholder = json.MangaInfo + ' Requests/Minute';
-      mDexAuthorRL.placeholder = json.MangaDexAuthor + ' Requests/Minute';
-      mDexFeedRL.placeholder = json.MangaDexFeed + ' Requests/Minute';
-      mDexImageRL.placeholder = json.MangaDexImage + ' Requests/Minute';
-    });
-
-    //If the cssStyle key isn't in the local storage of the browser, then set the css style to the default and load the page
-    //Otherwise get the style key from storage and set it.
-    if (!localStorage.getItem('cssStyle')) {
-      localStorage.setItem('cssStyle', 'card_compact');
-      document.getElementById('librarystyle').setAttribute('href', 'styles/' + localStorage.getItem('cssStyle') + '.css');
-      document.getElementById('card_compact').selected = true;
-    } else {
-      css_style = localStorage.getItem('cssStyle');
-      document.getElementById('librarystyle').setAttribute('href', 'styles/' + css_style + '.css');
-      document.getElementById(css_style).selected = true;
-    }
-    setInterval(() => {
-      UpdateJobs();
-    }, 1000);
-  });
-  //Clear the previous values if any exist.
-  searchBox.value = "";
-  connectorMatch.length = 0;
-  statusMatch.length = 0;
-}
-Setup();
-
-function ToggleFilterConnector(connector, event) {
-  //console.log("Initial Array:");
-  //console.log(connectorMatch);
-  if (connectorMatch.includes(connector)) {
-    idx = connectorMatch.indexOf(connector);
-    connectorMatch.splice(idx, 1);
-    event.target.style.outline = 'none';
-    event.target.style.outlineOffset = "0px";
-  } else {
-    connectorMatch.push(connector);
-    event.target.style.outline = '4px solid var(--secondary-color)';
-    event.target.style.outlineOffset = '3px';
-  }
-  //console.log("Final Array");
-  //console.log(connectorMatch);
-  FilterResults();
-}
-
-function ToggleFilterStatus(status, event) {
-  //console.log("Initial Array:");
-  //console.log(statusMatch);
-  if (statusMatch.includes(status)) {
-    idx = statusMatch.indexOf(status);
-    statusMatch.splice(idx, 1);
-    event.target.style.outline = 'none';
-    event.target.style.outlineOffset = "0px";
-  } else {
-    statusMatch.push(status);
-    event.target.style.outline = '4px solid var(--secondary-color)';
-    event.target.style.outlineOffset = '3px';
-  }
-  //console.log("Final Array");
-  //console.log(statusMatch);
-  FilterResults();
-}
-
-function ClearFilter() {
-  searchBox.value = "";
-  statusMatch.length = 0;
-  connectorMatch.length = 0;
-  FilterResults();
-
-  //Get rid of the outlines
-  connectorFilterBox = document.querySelector("#connectorFilterBox");
-  connectorFilterBox.childNodes.forEach(connector => {
-    if (connector.nodeName.toLowerCase() == 'connector-name') {
-      connector.style.outline = 'none';
-      connector.style.outlineOffset = "0px";
-    }
-  });
-
-  statusFilterBox = document.querySelector("#statusFilterBox");
-  statusFilterBox.childNodes.forEach(publicationStatus => {
-    if (publicationStatus.nodeName.toLowerCase() == 'status-filter') {
-      publicationStatus.style.outline = 'none';
-      publicationStatus.style.outlineOffset = "0px";
-    }
-  });
-}
-
-settingCSSStyle.addEventListener("change", (event) => {
-  localStorage.setItem('cssStyle', settingCSSStyle.value);
-  document.getElementById('librarystyle').setAttribute('href', 'styles/' + localStorage.getItem('cssStyle') + '.css');
-}); 
-
-function ResetContent(){
-    //Delete everything
-    tasksContent.replaceChildren();
-    
-    //Add "Add new Task" Button
-    var add = document.createElement("div");
-    add.setAttribute("id", "addPublication")
-    var plus = document.createElement("p");
-    plus.innerText = "+";
-    add.appendChild(plus);
-    add.addEventListener("click", () => ShowNewMangaSearch());
-    tasksContent.appendChild(add);
-
-    //Populate with the monitored mangas
-    GetMonitorJobs().then((json) => {
-        //console.log(json);
-        json.forEach(job => {
-          var mangaView = CreateManga(job.manga, job.mangaConnector.name);
-          mangaView.addEventListener("click", (event) => {
-            ShowMangaWindow(job, job.manga, event, false);
-          });
-          tasksContent.appendChild(mangaView);
-        });
-        monitoringJobsCount = json.length;
-    });
-}
-
-function ShowNewMangaSearch(){
-  newMangaTitle.value = "";
-  newMangaPopup.style.display = "block";
-  newMangaResult.replaceChildren();
-}
-
-newMangaTitle.addEventListener("keypress", (event) => { if(event.key === "Enter") GetNewMangaItems();});
-
-
-
-
-function GetNewMangaItems(){
-  if(newMangaTitle.value.length < 4)
-    return;
-  
-  newMangaResult.replaceChildren();
-  newMangaConnector.disabled = true;
-  newMangaTitle.disabled = true;
-  newMangaTranslatedLanguage.disabled = true;
-  GetPublicationFromConnector(newMangaConnector.value, newMangaTitle.value).then((json) => {
-    //console.log(json);
-    if(json.length > 0)
-      newMangaResult.style.display = "flex";
-    json.forEach(result => {
-      var mangaElement = CreateManga(result, newMangaConnector.value)
-      newMangaResult.appendChild(mangaElement);
-      mangaElement.addEventListener("click", (event) => {
-        ShowMangaWindow(null, result, event, true);
-      });
-    });
-    
-    newMangaConnector.disabled = false;
-    newMangaTitle.disabled = false;
-  newMangaTranslatedLanguage.disabled = false;
-  });
-}
-
-//Returns a new "Publication" Item to display in the jobs section
-function CreateManga(manga, connector){
-  //Create a new publication and set an internal ID
-    var mangaElement = document.createElement('publication');
-    mangaElement.id = GetValidSelector(manga.internalId);
-    
-  //Append the cover image to the publication
-    var mangaImage = document.createElement('img');
-    mangaImage.src = GetCoverUrl(manga.internalId);
-    mangaElement.appendChild(mangaImage);
-  
-//Append the publication information to the publication
-    //console.log(manga);
-    var info = document.createElement('publication-information');
-    var connectorName = document.createElement('connector-name');
-    connectorName.innerText = connector;
-    connectorName.className = "pill";
-    connectorName.style.backgroundColor = stringToColour(connector);
-    info.appendChild(connectorName);
-
-    var mangaName = document.createElement('publication-name');
-    mangaName.innerText = manga.sortName;
-
-    //Create the publication status indicator
-    var releaseStatus = document.createElement('publication-status');
-    releaseStatus.setAttribute("release-status", manga.releaseStatus);
-    switch(manga.releaseStatus){
-      case 0:
-        releaseStatus.setAttribute("release-status", "Ongoing");
-        break;
-      case 1:
-        releaseStatus.setAttribute("release-status", "Completed");
-        break;
-      case 2:
-        releaseStatus.setAttribute("release-status", "On Hiatus");
-        break;
-      case 3:
-        releaseStatus.setAttribute("release-status", "Cancelled");
-        break;
-      case 4:
-        releaseStatus.setAttribute("release-status", "Upcoming");
-        break;
-      default:
-        releaseStatus.setAttribute("release-status", "Status Unavailable");
-        break;
-    }
-
-    info.appendChild(mangaName);
-    mangaElement.appendChild(info);
-    mangaElement.appendChild(releaseStatus);                  //Append the release status indicator to the publication element
-    return mangaElement;
-}
-
-createMonitorJobButton.addEventListener("click", () => {
-  CreateMonitorJob(newMangaConnector.value, selectedManga.internalId, newMangaTranslatedLanguage.value);
-  UpdateJobs();
-  mangaViewerPopup.style.display = "none";
-});
-startJobButton.addEventListener("click", () => {
-  StartJob(selectedJob.id);
-  mangaViewerPopup.style.display = "none";
-});
-cancelJobButton.addEventListener("click", () => {
-  CancelJob(selectedJob.id);
-  mangaViewerPopup.style.display = "none";
-});
-deleteJobButton.addEventListener("click", () => {
-  RemoveJob(selectedJob.id);
-  UpdateJobs();
-  mangaViewerPopup.style.display = "none";
-});
-
-function ShowMangaWindow(job, manga, event, add){
-    selectedManga = manga;
-    selectedJob = job;
-    //Show popup
-    mangaViewerPopup.style.display = "block";
-    
-    //Set position to mouse-position
-    if(event.clientY < window.innerHeight - mangaViewerWindow.offsetHeight)
-        mangaViewerWindow.style.top = `${event.clientY}px`;
-    else
-        mangaViewerWindow.style.top = `${event.clientY - mangaViewerWindow.offsetHeight}px`;
-    
-    if(event.clientX < window.innerWidth - mangaViewerWindow.offsetWidth)
-        mangaViewerWindow.style.left = `${event.clientX}px`;
-    else
-        mangaViewerWindow.style.left = `${event.clientX - mangaViewerWindow.offsetWidth}px`;
-    
-    //Edit information inside the window
-    mangaViewerName.innerText = manga.sortName;
-    mangaViewerTags.innerText = manga.tags.join(", ");
-    mangaViewerDescription.innerText = manga.description;
-    mangaViewerAuthor.innerText = manga.authors.join(',');
-    mangaViewCover.src = GetCoverUrl(manga.internalId);
-    toEditId = manga.internalId;
-    
-    //Check what action should be listed
-    if(add){
-        createMonitorJobButton.style.display = "initial";
-        createDownloadChapterJobButton.style.display = "none";
-        cancelJobButton.style.display = "none";
-        startJobButton.style.display = "none";
-        deleteJobButton.style.display = "none";
-    }
-    else{
-        createMonitorJobButton.style.display = "none";
-        createDownloadChapterJobButton.style.display = "none";
-        cancelJobButton.style.display = "initial";
-        startJobButton.style.display = "initial";
-        deleteJobButton.style.display = "initial";
-    }
-}
-
-function HidePublicationPopup(){
-    publicationViewerPopup.style.display = "none";
-}
-
-searchBox.addEventListener("keyup", () => FilterResults());
-//Filter shown jobs
-function FilterResults(){
-  //For each publication   
-  tasksContent.childNodes.forEach(publication => {
-    //If the search box isn't empty check that the title contains the searchbox content. If it does then
-    //'searchMatch' is true and the manga is shown. If the search box is empty, then consider this field
-    //to be true anyways.
-    if (searchBox.value.length > 0) {
-      publication.childNodes.forEach(item => {
-        if (item.nodeName.toLowerCase() == "publication-information"){
-          item.childNodes.forEach(information => {
-            if (information.nodeName.toLowerCase() == "publication-name") {
-              if (information.textContent.toLowerCase().includes(searchBox.value.toLowerCase())){
-                searchMatch = 1;
-              } else {
-                searchMatch = 0;
-              }
-            }
-          });
-        }
-      });
-    } else {  
-      searchMatch = 1;
-    }
-
-    //If the array connectorMatch isn't empty then check that the connector matches one of the ones
-    //in the array
-    if (connectorMatch.length > 0) {
-      publication.childNodes.forEach(item => {
-        if (item.nodeName.toLowerCase() == "publication-information"){
-          item.childNodes.forEach(information => {
-            if (information.nodeName.toLowerCase() == "connector-name") {
-              if (connectorMatch.includes(information.textContent)){
-                connectorNameMatch = 1;
-              } else {
-                connectorNameMatch = 0;
-              }
-            }
-          });
-        }
-      });
-    } else {
-      connectorNameMatch = 1;
-    }
-
-    //If the array statusMatch isn't empty then check that the status matches one of the ones
-    //in the array
-    if (statusMatch.length > 0) {
-      publication.childNodes.forEach(item => {
-        if (item.nodeName.toLowerCase() == "publication-status"){
-          if (statusMatch.includes(item.getAttribute('release-status'))) {
-            statusNameMatch = 1;
-          } else {
-            statusNameMatch = 0;
-          }
-        }
-      });
-    } else {
-      statusNameMatch = 1;
-    }
-
-    //If all of the filtering conditions are met then show the manga, otherwise hide it.
-    if (searchMatch && connectorNameMatch && statusNameMatch) {
-      publication.style.display = 'initial';
-    } else {
-      publication.style.display = 'none';
-    }
-  });
-}
-
-settingsCog.addEventListener("click", () => {
-  OpenSettings();
-  settingsPopup.style.display = "flex";
-});
-
-filterFunnel.addEventListener("click", () => {
-  filterBox.classList.toggle("animate");
-});
-
-settingKomgaUrl.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings(); });
-settingKomgaUser.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings(); });
-settingKomgaPass.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings(); });
-settingKavitaUrl.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings(); });
-settingKavitaUser.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings(); });
-settingKavitaPass.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings(); });
-settingGotifyUrl.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings(); });
-settingGotifyAppToken.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings(); });
-settingLunaseaWebhook.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings(); });
-settingNtfyEndpoint.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings(); });
-settingNtfyAuth.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings(); });
-settingUserAgent.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings(); });
-settingApiUri.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings(); });
-
-defaultRL.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings();}); 
-coverRL.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings();}); 
-imageRL.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings();}); 
-infoRL.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings();}); 
-mDexAuthorRL.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings();}); 
-mDexFeedRL.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings();}); 
-mDexImageRL.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings();}); 
-
-
-function OpenSettings(){
-  settingGotifyConfigured.setAttribute("configuration", "Not Configured");
-  settingLunaseaConfigured.setAttribute("configuration", "Not Configured");
-  settingNtfyConfigured.setAttribute("configuration", "Not Configured");
-  settingKavitaConfigured.setAttribute("configuration", "Not Configured");
-  settingKomgaConfigured.setAttribute("configuration", "Not Configured");
-  settingKomgaUrl.value = "";
-  settingKomgaUser.value = "";
-  settingKomgaPass.value = "";
-  settingKavitaUrl.value = "";
-  settingKavitaUser.value = "";
-  settingKavitaPass.value = "";
-  settingGotifyUrl.value = "";
-  settingGotifyAppToken.value = "";
-  settingLunaseaWebhook.value = "";
-  settingNtfyAuth.value = "";
-  settingNtfyEndpoint.value = "";
-  settingUserAgent.value = "";
-  settingApiUri.value = "";
-  defaultRL.value = "";
-  coverRL.value = "";
-  imageRL.value = "";
-  infoRL.value = "";
-  mDexAuthorRL.value = "";
-  mDexFeedRL.value = "";
-  mDexImageRL.value = "";
-  
-  GetSettings().then((json) => {
-    //console.log(json);
-    settingApiUri.value = apiUri;
-    settingUserAgent.value = json.userAgent;
-    //console.log(json.styleSheet);
-  });
-  GetRateLimits().then((json) => {
-    defaultRL.placeholder = json.Default + ' Requests/Minute';
-    coverRL.placeholder = json.MangaCover + ' Requests/Minute';
-    imageRL.placeholder = json.MangaImage + ' Requests/Minute';
-    infoRL.placeholder = json.MangaInfo + ' Requests/Minute';
-    mDexAuthorRL.placeholder = json.MangaDexAuthor + ' Requests/Minute';
-    mDexFeedRL.placeholder = json.MangaDexFeed + ' Requests/Minute';
-    mDexImageRL.placeholder = json.MangaDexImage + ' Requests/Minute';
-  });
-  GetLibraryConnectors().then((json) => {
-    //console.log(json);
-    json.forEach(connector => {
-      switch(libraryConnectorTypes[connector.libraryType]){
-        case "Kavita":
-          settingKavitaConfigured.setAttribute("configuration", "Active");
-          settingKavitaUrl.value = connector.baseUrl;
-          settingKavitaUser.value = "***";
-          settingKavitaPass.value = "***";
-          break;
-        case "Komga":
-          settingKomgaConfigured.setAttribute("configuration", "Active");
-          settingKomgaUrl.value = connector.baseUrl;
-          settingKomgaUser.value = "***";
-          settingKomgaPass.value = "***";
-          break;
-        default:
-          console.log("Unknown type");
-          console.log(connector);
-          break;
-      }
-    });
-  });
-  GetNotificationConnectors().then((json) => {
-    json.forEach(connector => {
-      switch(notificationConnectorTypes[connector.notificationConnectorType]){
-        case "Gotify":
-          settingGotifyUrl.value = connector.endpoint;
-          settingGotifyAppToken.value = "***";
-          settingGotifyConfigured.setAttribute("configuration", "Active");
-          break;
-        case "LunaSea":
-          settingLunaseaConfigured.setAttribute("configuration", "Active");
-          settingLunaseaWebhook.value = connector.id;
-          break;
-        case "Ntfy":
-          settingNtfyConfigured.setAttribute("configuration", "Active");
-          settingNtfyEndpoint.value = connector.endpoint;
-          settingNtfyAuth.value = "***";
-          break;
-        default:
-          console.log("Unknown type");
-          console.log(connector);
-          break;
-      }
-    });
-  });
-}
-
-//Functions for clearing/resetting connectors in the settings pop-up
-function ClearKomga(){
-  settingKomgaUrl.value = "";
-  settingKomgaUser.value = "";
-  settingKomgaPass.value = "";
-  settingKomgaConfigured.setAttribute("configuration", "Not Configured");
-  ResetKomga();
-}
-
-function ClearKavita(){
-  settingKavitaUrl.value = "";
-  settingKavitaUser.value = "";
-  settingKavitaPass.value = "";
-  settingKavitaConfigured.setAttribute("configuration", "Not Configured");
-  ResetKavita();
-}
-
-function ClearGotify(){
-  settingGotifyUrl.value = "";
-  settingGotifyAppToken.value = ""
-  settingGotifyConfigured.setAttribute("configuration", "Not Configured");
-  ResetGotify();
-}
-
-function ClearLunasea(){
-  settingLunaseaWebhook.value = "";
-  settingLunaseaConfigured.setAttribute("configuration", "Not Configured");
-  ResetLunaSea();
-}
-
-function ClearNtfy(){
-  settingNtfyEndpoint.value = "";
-  settingNtfyAuth.value = "";
-  settingNtfyConfigured.setAttribute("configuration", "Not Configured");
-  ResetNtfy();
-}
-
-function UpdateSettings(){
-  if(settingApiUri.value != ""){
-    apiUri = settingApiUri.value;
-    setCookie("apiUri", apiUri);
-    Setup();
-  }
-  
-  if(settingKomgaUrl.value != "" &&
-     settingKomgaUser.value != "" &&
-     settingKomgaPass.value != ""){
-    UpdateKomga(settingKomgaUrl.value, utf8_to_b64(`${settingKomgaUser.value}:${settingKomgaPass.value}`));
-  }
-  
-  if(settingKavitaUrl.value != "" &&
-    settingKavitaUser.value != "" &&
-    settingKavitaPass.value != ""){
-    UpdateKavita(settingKavitaUrl.value, settingKavitaUser.value, settingKavitaPass.value);
-  }
-  
-  if(settingGotifyUrl.value != "" &&
-    settingGotifyAppToken.value != ""){
-    UpdateGotify(settingGotifyUrl.value, settingGotifyAppToken.value);
-  }
-  
-  if(settingLunaseaWebhook.value != ""){
-    UpdateLunaSea(settingLunaseaWebhook.value);
-  }
-  
-  if(settingNtfyEndpoint.value != "" &&
-    settingNtfyAuth.value != ""){
-    UpdateNtfy(settingNtfyEndpoint.value, settingNtfyAuth.value);
-  }
-  
-  if(settingUserAgent.value != ""){
-      UpdateUserAgent(settingUserAgent.value);
-  }
-
-  if (defaultRL.value != "") {
-    UpdateRateLimit(0, defaultRL.value);
-  }
-
-  if (coverRL.value != "") {
-    UpdateRateLimit(3, coverRL.value);
-  }
-
-  if (imageRL.value != "") {
-    UpdateRateLimit(2, imageRL.value);
-  }
-
-  if (infoRL.value != "") {
-    UpdateRateLimit(6, infoRL.value);
-  }
-
-  if (mDexAuthorRL.value != "") {
-    UpdateRateLimit(5, mDexAuthorRL.value);
-  }
-
-  if (mDexFeedRL.value != "") {
-    UpdateRateLimit(1, mDexFeedRL.value);
-  }
-
-  if (mDexImageRL.value != "") {
-    UpdateRateLimit(5, mDexImageRL.value);
-  }
-  
-  setTimeout(() => {
-      OpenSettings();
-      Setup();
-  }, 100)
-}
-
-function utf8_to_b64(str) {
-    return window.btoa(unescape(encodeURIComponent( str )));
-}
-
-function UpdateJobs(){
-
-  GetMonitorJobs().then((json) => {
-    if(monitoringJobsCount != json.length){
-      ResetContent();
-      monitoringJobsCount = json.length;
-    }
-  });
-
-  //Get the jobs that are waiting in the queue
-  GetWaitingJobs().then((json) => {
-    jobsQueuedTag.innerText = json.length;
-    
-    var nowWaitingJobs = [];
-    
-    json.forEach(job => {
-      if(!waitingJobs.includes(GetValidSelector(job.id))){
-        var jobDom = createJob(job);
-        jobStatusWaiting.appendChild(jobDom);
-      }
-      nowWaitingJobs.push(GetValidSelector(job.id));
-    });
-    waitingJobs = nowWaitingJobs;
-  });
-  
-  jobStatusWaiting.childNodes.forEach(child => {
-    if(!waitingJobs.includes(child.id))
-      jobStatusWaiting.removeChild(child);
-  });
-  
-  //Get currently running jobs
-  GetRunningJobs().then((json) => {
-    jobsRunningTag.innerText = json.length;
-    
-    var nowRunningJobs = [];
-    
-    json.forEach(job => {
-      if(!runningJobs.includes(GetValidSelector(job.id))){
-        var jobDom = createJob(job);
-        jobStatusRunning.appendChild(jobDom);
-      }
-      nowRunningJobs.push(GetValidSelector(job.id));
-      UpdateJobProgress(job.id);
-    });
-    
-    runningJobs = nowRunningJobs;
-  });
-  
-  jobStatusRunning.childNodes.forEach(child => {
-    if(!runningJobs.includes(child.id))
-      jobStatusRunning.removeChild(child);
-  });
-}
-
-function createJob(jobjson){
-  var manga;
-  if(jobjson.chapter != null)
-    manga = jobjson.chapter.parentManga;
-  else if(jobjson.manga != null)
-    manga = jobjson.manga;
-  else return null;
-  
-  
-  var wrapper = document.createElement("div");
-  wrapper.className = "section-item";
-  wrapper.id = GetValidSelector(jobjson.id);
-  
-  var image = document.createElement("img");
-  image.className = "jobImage";
-  image.src = GetCoverUrl(manga.internalId);
-  wrapper.appendChild(image);
-
-  var details = document.createElement("div");
-  details.className = 'jobDetails';
-
-  var title = document.createElement("span");
-  title.className = "jobTitle";
-  if(jobjson.chapter != null)
-    title.innerText = `${manga.sortName} - ${jobjson.chapter.fileName}`;
-  else if(jobjson.manga != null)
-    title.innerText = manga.sortName;
-  details.appendChild(title);
-  
-  var progressBar = document.createElement("progress");
-  progressBar.className = "jobProgressBar";
-  progressBar.id = `jobProgressBar${GetValidSelector(jobjson.id)}`;
-  details.appendChild(progressBar);
-  
-  var progressSpan = document.createElement("span");
-  progressSpan.className = "jobProgressSpan";
-  progressSpan.id = `jobProgressSpan${GetValidSelector(jobjson.id)}`;
-  progressSpan.innerText = "Pending...";
-  details.appendChild(progressSpan);
-  
-  var cancelSpan = document.createElement("span");
-  cancelSpan.className = "jobCancel";
-  cancelSpan.innerText = "Cancel";
-  cancelSpan.addEventListener("click", () => CancelJob(jobjson.id));
-  details.appendChild(cancelSpan);
-  
-  wrapper.appendChild(details);
-
-  return wrapper;
-}
-
-function ShowJobQueue(){  
-  jobStatusView.style.display = "initial";
-}
-
-function UpdateJobProgress(jobId){
-  GetProgress(jobId).then((json) => {
-    var progressBar = document.querySelector(`#jobProgressBar${GetValidSelector(jobId)}`);
-    var progressSpan = document.querySelector(`#jobProgressSpan${GetValidSelector(jobId)}`);
-    if(progressBar != null && json.progress != 0){
-      progressBar.value = json.progress;
-    }
-    if(progressSpan != null){
-      var percentageStr = "0%";
-      var timeleftStr = "00:00:00";
-      if(json.progress != 0){
-        percentageStr = Intl.NumberFormat("en-US", { style: "percent"}).format(json.progress);
-        timeleftStr = json.timeRemaining.split('.')[0];
-      }
-      progressSpan.innerText = `${percentageStr} ${timeleftStr}`;
-    }
-  });
-}
-
-function GetValidSelector(str){
-    var clean = [...str.matchAll(/[a-zA-Z0-9]*-*_*/g)];
-    return clean.join('');
-}
-
-const stringToColour = (str) => {
-  let hash = 0;
-  str.split('').forEach(char => {
-    hash = char.charCodeAt(0) + ((hash << 5) - hash)
-  })
-  let colour = '#'
-  for (let i = 0; i < 3; i++) {
-    const value = (hash >> (i * 8)) & 0xff
-    colour += value.toString(16).padStart(2, '0')
-  }
-  return colour
-}
\ No newline at end of file
diff --git a/Website/media/connector-icons/bato.ico b/Website/media/connector-icons/bato.ico
new file mode 100644
index 0000000..42c201a
Binary files /dev/null and b/Website/media/connector-icons/bato.ico differ
diff --git a/Website/media/connector-icons/gotify-logo.png b/Website/media/connector-icons/gotify-logo.png
new file mode 100644
index 0000000..5d29004
Binary files /dev/null and b/Website/media/connector-icons/gotify-logo.png differ
diff --git a/Website/media/connector-icons/kavita.png b/Website/media/connector-icons/kavita.png
new file mode 100644
index 0000000..00a4ba7
Binary files /dev/null and b/Website/media/connector-icons/kavita.png differ
diff --git a/Website/media/connector-icons/komga.svg b/Website/media/connector-icons/komga.svg
new file mode 100644
index 0000000..e8b3f56
--- /dev/null
+++ b/Website/media/connector-icons/komga.svg
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+        xmlns="http://www.w3.org/2000/svg"
+        height="512pt"
+        viewBox="0 0 512 512"
+        width="512pt"
+        version="1.1"
+        id="svg4586">
+    <path
+            d="m512 256c0 141.386719-114.613281 256-256 256s-256-114.613281-256-256 114.613281-256 256-256 256 114.613281 256 256zm0 0"
+            fill="#005ed3"
+            id="path4556"/>
+    <path
+            d="m 512,256 c 0,-11.71094 -0.80469,-23.23047 -2.32422,-34.52344 L 382.48047,94.28125 320.52344,121.85938 256,56.933594 212.69531,131.30469 129.51953,94.28125 141.86719,178.42187 49.949219,193.81641 114.32031,256 l -64.371091,62.18359 82.121091,82.16016 -2.55078,17.375 91.95703,91.95703 C 232.76953,511.19531 244.28906,512 256,512 397.38672,512 512,397.38672 512,256 Z"
+            id="path4558"
+            style="fill:#00459f"/>
+    <path
+            d="m256 86.742188 37.109375 63.738281 70.574219-31.414063-10.527344 71.71875 77.078125 12.910156-54.144531 52.304688 54.144531 52.304688-77.078125 12.910156 10.527344 71.71875-70.574219-31.414063-37.109375 63.738281-37.109375-63.738281-70.574219 31.414063 10.527344-71.71875-77.078125-12.910156 54.144531-52.304688-54.144531-52.304688 77.078125-12.910156-10.527344-71.71875 70.574219 31.414063zm0 0"
+            fill="#ff0335"
+            id="path4560"/>
+    <path
+            d="m430.230469 308.300781-77.070313 12.910157 10.519532 71.71875-70.570313-31.410157-37.109375 63.742188v-338.523438l37.109375 63.742188 70.570313-31.410157-6.757813 46.101563-3.761719 25.617187 58.800782 9.851563 18.269531 3.058594-13.390625 12.929687-40.75 39.371094 11.378906 10.988281zm0 0"
+            fill="#c2001b"
+            id="path4562"/>
+    <path
+            d="m256 455.066406-43.304688-74.371094-83.175781 37.023438 12.347657-84.140625-91.917969-15.394531 64.371093-62.183594-64.371093-62.183594 91.917969-15.394531-12.347657-84.140625 83.179688 37.023438 43.300781-74.371094 43.304688 74.371094 83.175781-37.023438-12.347657 84.140625 91.917969 15.394531-64.371093 62.183594 64.371093 62.183594-91.917969 15.398437 12.347657 84.136719-83.175781-37.023438zm-30.917969-112.722656 30.917969 53.101562 30.917969-53.101562 57.964843 25.800781-8.703124-59.292969 62.238281-10.425781-43.917969-42.425781 43.917969-42.425781-62.238281-10.425781 8.703124-59.292969-57.964843 25.800781-30.917969-53.101562-30.917969 53.101562-57.964843-25.800781 8.703124 59.292969-62.238281 10.425781 43.917969 42.425781-43.917969 42.425781 62.238281 10.425781-8.703124 59.292969zm0 0"
+            fill="#ffdf47"
+            id="path4564"/>
+    <path
+            d="m403.308594 261.441406-5.628906-5.441406 25.160156-24.300781 39.210937-37.878907-55.75-9.339843-36.171875-6.058594 2.800782-19.09375 9.550781-65.046875-83.179688 37.019531-43.300781-74.371093v59.621093l30.921875 53.109375 57.957031-25.808594-3.910156 26.667969-2.546875 17.378907-2.242187 15.25 2.480468.421874 59.761719 10.007813-43.921875 42.421875 16.96875 16.390625 26.953125 26.03125-62.242187 10.429687 8.699218 59.296876-57.957031-25.808594-30.921875 53.109375v59.621093l43.300781-74.371093 83.179688 37.019531-12.351563-84.140625 91.921875-15.398437zm0 0"
+            fill="#fec000"
+            id="path4566"/>
+    <g
+            aria-label="K"
+            transform="matrix(1.1590846,-0.34467221,0.22789693,0.794981,0,0)"
+            style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:296.55969238px;line-height:125%;font-family:Impact;-inkscape-font-specification:Impact;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.54528999;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+            id="text4596">
+        <path
+                d="m 220.91497,266.9035 -34.89789,105.85211 38.2284,128.58643 H 161.2555 L 136.63873,400.84769 V 501.34204 H 75.676021 V 266.9035 h 60.962709 v 91.08205 l 27.07845,-91.08205 z"
+                style="font-size:296.55969238px;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.54528999;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+                id="path824"/>
+    </g>
+</svg>
diff --git a/Website/media/connector-icons/lunasea.png b/Website/media/connector-icons/lunasea.png
new file mode 100644
index 0000000..868e849
Binary files /dev/null and b/Website/media/connector-icons/lunasea.png differ
diff --git a/Website/media/connector-icons/mangadex-logo.svg b/Website/media/connector-icons/mangadex-logo.svg
new file mode 100644
index 0000000..696dd8b
--- /dev/null
+++ b/Website/media/connector-icons/mangadex-logo.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="40" height="34" fill="none" viewBox="0 0 40 34"><path fill="#ff6740" d="M34.542 21.669H8.216a.585.585 0 0 1 0-1.17h26.326a.585.585 0 0 1 0 1.17Zm0 2.218H8.216a.585.585 0 1 1 0-1.17h26.326a.585.585 0 0 1 0 1.17Z"/><path fill="#ff6740" d="M30.233 23.229v3.912l1.675-.97 1.676.97V23.23h-3.35Z"/><path fill="#272B30" d="M35.715 10.866h-4.403v2.441h4.403v-2.44Z"/><path fill="#F1F1F1" d="M38.08 13.928c-.021-.02-.043-.037-.065-.056a5.998 5.998 0 0 1-1.266-1.536l-.009-.018h-.001a5.91 5.91 0 0 1-.72-2.15c-.004-.047-.01-.093-.015-.14v-.009a1.514 1.514 0 0 0-.327-.725l-.048-.054a10.43 10.43 0 0 0-5.301-3.198 6.574 6.574 0 0 1 .81-3.484.376.376 0 0 0-.332-.557h-.026a13.705 13.705 0 0 0-6.965 3.33 13.792 13.792 0 0 0-2.85 3.56 12.616 12.616 0 0 0-1.722-.764 12.859 12.859 0 0 0-5.536-.697 12.765 12.765 0 0 0-4.165 1.105C5.379 10.437 2.405 14.49 2.039 19.27c-.026.33-.039.665-.039 1v3.233c0 .56.083 1.098.237 1.605a5.55 5.55 0 0 0 2.596 3.226c.76.427 1.63.68 2.56.704h8.212a1.623 1.623 0 0 1 .243 0c.013 0 .026 0 .04.003.021.001.042.002.064.005a1.941 1.941 0 0 1 1.706 1.702 1.291 1.291 0 0 1 .014.23l-.001.036v.004a1.95 1.95 0 0 0 .918 1.61 1.94 1.94 0 0 0 2.955-1.514.943.943 0 0 0 .004-.077v-.108c0-.662-.11-1.3-.313-1.891a5.816 5.816 0 0 0-1.421-2.254 5.815 5.815 0 0 0-4.087-1.676H8.88c-.016.002-.033.002-.05.002-.016 0-.033 0-.05-.002a2.917 2.917 0 0 1-2.87-2.918 2.92 2.92 0 0 1 2.92-2.921h26.106a1.52 1.52 0 0 0 1.443-1.05l.02-.07c.091-.294.27-.55.506-.737l.004-.002a.638.638 0 0 1 .058-.044.934.934 0 0 1 .082-.055 2.985 2.985 0 0 0 1.032-3.385Zm-3.487-1.165a.377.377 0 0 1-.175-.044 2.037 2.037 0 0 0-1.423-.234c-.212.04-.412.114-.595.215l-.008.006c-.012.004-.022.011-.032.018a.369.369 0 0 1-.37-.634l.036-.022a2.794 2.794 0 0 1 2.735 0l.036.022a.367.367 0 0 1-.204.673Z"/><path fill="#E6E6E6" d="M38.08 13.928c-.021-.02-.043-.037-.065-.056a5.998 5.998 0 0 1-1.266-1.536l-.009-.018h-.001a5.91 5.91 0 0 1-.72-2.15c-.004-.047-.01-.093-.015-.14v-.009a1.514 1.514 0 0 0-.327-.725l-.048-.054a10.43 10.43 0 0 0-5.301-3.198 6.574 6.574 0 0 1 .81-3.484.376.376 0 0 0-.332-.557h-.026a13.705 13.705 0 0 0-6.965 3.33 13.792 13.792 0 0 0-2.85 3.56 12.616 12.616 0 0 0-1.722-.764 12.859 12.859 0 0 0-5.536-.697 12.765 12.765 0 0 0-4.165 1.105C5.379 10.437 2.405 14.49 2.039 19.27c-.026.33-.039.665-.039 1v3.233c0 .56.083 1.098.237 1.605a5.55 5.55 0 0 0 2.596 3.226c.76.427 1.63.68 2.56.704h8.212a1.623 1.623 0 0 1 .243 0c.013 0 .026 0 .04.003.021.001.042.002.064.005a1.941 1.941 0 0 1 1.706 1.702 1.291 1.291 0 0 1 .014.23l-.001.036v.004a1.95 1.95 0 0 0 .918 1.61 1.94 1.94 0 0 0 2.955-1.514.943.943 0 0 0 .004-.077v-.108c0-.662-.11-1.3-.313-1.891a5.816 5.816 0 0 0-1.421-2.254 5.815 5.815 0 0 0-4.087-1.676H8.88c-.016.002-.033.002-.05.002-.016 0-.033 0-.05-.002a2.917 2.917 0 0 1-2.87-2.918 2.92 2.92 0 0 1 2.92-2.921h26.106a1.52 1.52 0 0 0 1.443-1.05l.02-.07c.091-.294.27-.55.506-.737l.004-.002a.638.638 0 0 1 .058-.044.934.934 0 0 1 .082-.055 2.985 2.985 0 0 0 1.032-3.385Zm-3.487-1.165a.377.377 0 0 1-.175-.044 2.037 2.037 0 0 0-1.423-.234c-.212.04-.412.114-.595.215l-.008.006c-.012.004-.022.011-.032.018a.369.369 0 0 1-.37-.634l.036-.022a2.794 2.794 0 0 1 2.735 0l.036.022a.367.367 0 0 1-.204.673Z"/><path fill="#ff6740" d="M21.005 8.824c.703 3.38 3.635 5.915 7.147 5.915 2.067 0 3.933-.88 5.262-2.292l-.021-.001c-.137 0-.27.013-.398.04a1.942 1.942 0 0 0-.595.214l-.008.006a.11.11 0 0 0-.033.019.369.369 0 0 1-.532-.33c0-.128.064-.239.162-.305l.037-.022a2.789 2.789 0 0 1 1.952-.294 7.519 7.519 0 0 0 1.309-2.898 10.415 10.415 0 0 0-4.96-2.834 6.573 6.573 0 0 1 .81-3.485.376.376 0 0 0-.332-.557c-.008 0-.017 0-.026.002a13.7 13.7 0 0 0-6.965 3.33 13.793 13.793 0 0 0-2.81 3.492Z"/><path fill="#272B30" d="M36.145 15.807a13.641 13.641 0 0 0-.955-.26 16.819 16.819 0 0 0-2.907-.459c-.652-.054-1.306-.027-1.956.023l-.488.054c-.162.023-.32.062-.482.09-.328.04-.636.16-.962.228a7.178 7.178 0 0 1 1.906-.572 8.967 8.967 0 0 1 1.998-.083 11.15 11.15 0 0 1 1.972.313 9.682 9.682 0 0 1 1.874.666Zm-.287.6a13.093 13.093 0 0 0-.983-.115 16.794 16.794 0 0 0-2.944-.022c-.653.043-1.295.167-1.93.313l-.475.126c-.156.047-.308.108-.463.16-.319.089-.605.252-.917.369a7.175 7.175 0 0 1 1.8-.849 8.965 8.965 0 0 1 1.963-.38c.665-.05 1.335-.05 1.997.018a9.73 9.73 0 0 1 1.952.38Zm-.045.699c-.33.026-.659.051-.985.1a16.838 16.838 0 0 0-2.878.614c-.628.183-1.228.442-1.818.723l-.435.225c-.143.08-.278.172-.418.257-.293.155-.537.376-.817.557a7.174 7.174 0 0 1 1.575-1.217 8.939 8.939 0 0 1 1.835-.794 11.13 11.13 0 0 1 1.953-.415 9.683 9.683 0 0 1 1.988-.05Z"/><path fill="#F27BAA" d="M38.015 13.872a6.04 6.04 0 0 1-.32-.3.968.968 0 0 0 .5 1.796h.015c.006-.041.016-.08.02-.122a2.95 2.95 0 0 0-.15-1.319l-.065-.055Z"/><path fill="#fff" d="M29.993 6.54c0 .046 0 .092.002.138a10.408 10.408 0 0 0-3.166 2.017 5.556 5.556 0 0 1 4.345-6.23 7.584 7.584 0 0 0-1.18 4.075Z"/><path fill="#ff6740" d="M19.814 26.785a5.844 5.844 0 0 0-.404-.36 4.512 4.512 0 0 0-3.245 2.66 1.942 1.942 0 0 1 1.507 1.893l-.001.037v.004a1.955 1.955 0 0 0 .918 1.611 1.94 1.94 0 0 0 2.959-1.592v-.108c0-.662-.11-1.3-.313-1.891a5.831 5.831 0 0 0-1.421-2.255Z"/></svg>
\ No newline at end of file
diff --git a/Website/media/connector-icons/mangakatana.png b/Website/media/connector-icons/mangakatana.png
new file mode 100644
index 0000000..2d3e044
Binary files /dev/null and b/Website/media/connector-icons/mangakatana.png differ
diff --git a/Website/media/connector-icons/mangalife.png b/Website/media/connector-icons/mangalife.png
new file mode 100644
index 0000000..af14c64
Binary files /dev/null and b/Website/media/connector-icons/mangalife.png differ
diff --git a/Website/media/connector-icons/manganato.png b/Website/media/connector-icons/manganato.png
new file mode 100644
index 0000000..0df9c28
Binary files /dev/null and b/Website/media/connector-icons/manganato.png differ
diff --git a/Website/media/connector-icons/mangasee.png b/Website/media/connector-icons/mangasee.png
new file mode 100644
index 0000000..7965217
Binary files /dev/null and b/Website/media/connector-icons/mangasee.png differ
diff --git a/Website/media/connector-icons/mangaworld.png b/Website/media/connector-icons/mangaworld.png
new file mode 100644
index 0000000..04b30e5
Binary files /dev/null and b/Website/media/connector-icons/mangaworld.png differ
diff --git a/Website/media/connector-icons/ntfy.svg b/Website/media/connector-icons/ntfy.svg
new file mode 100644
index 0000000..8530901
--- /dev/null
+++ b/Website/media/connector-icons/ntfy.svg
@@ -0,0 +1,40 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="50mm" height="50mm" viewBox="0 0 50 50">
+    <defs>
+        <linearGradient id="b">
+            <stop offset="0" style="stop-color:#348878;stop-opacity:1"/>
+            <stop offset="1" style="stop-color:#52bca6;stop-opacity:1"/>
+        </linearGradient>
+        <linearGradient id="a">
+            <stop offset="0" style="stop-color:#348878;stop-opacity:1"/>
+            <stop offset="1" style="stop-color:#56bda8;stop-opacity:1"/>
+        </linearGradient>
+        <linearGradient xlink:href="#a" id="e" x1="160.722" x2="168.412" y1="128.533" y2="134.326" gradientTransform="matrix(3.74959 0 0 3.74959 -541.79 -387.599)" gradientUnits="userSpaceOnUse"/>
+        <linearGradient xlink:href="#b" id="c" x1=".034" x2="50.319" y1="0" y2="50.285" gradientTransform="matrix(.99434 0 0 .99434 -.034 0)" gradientUnits="userSpaceOnUse"/>
+        <filter id="d" width="1.176" height="1.211" x="-.076" y="-.092" style="color-interpolation-filters:sRGB">
+            <feFlood flood-color="#fff" flood-opacity=".192" result="flood"/>
+            <feComposite in="flood" in2="SourceGraphic" operator="in" result="composite1"/>
+            <feGaussianBlur in="composite1" result="blur" stdDeviation="4"/>
+            <feOffset dx="3" dy="2.954" result="offset"/>
+            <feComposite in="SourceGraphic" in2="offset" result="composite2"/>
+        </filter>
+    </defs>
+    <g style="display:inline">
+        <path d="M0 0h50v50H0z" style="fill:url(#c);fill-opacity:1;stroke:none;stroke-width:.286502;stroke-linejoin:bevel"/>
+    </g>
+    <g style="display:inline">
+        <path d="M50.4 46.883c-9.168 0-17.023 7.214-17.023 16.387v.007l.09 71.37-2.303 16.992 31.313-8.319h77.841c9.17 0 17.024-7.224 17.024-16.396V63.27c0-9.17-7.85-16.383-17.016-16.387h-.008zm0 11.566h89.926c3.222.004 5.45 2.347 5.45 4.82v63.655c0 2.475-2.232 4.82-5.457 4.82h-79.54l-15.908 4.807.162-.938-.088-72.343c0-2.476 2.23-4.82 5.455-4.82z" style="color:#fff;display:inline;fill:#fff;stroke:none;stroke-width:1.93113;-inkscape-stroke:none;filter:url(#d)" transform="scale(.26458)"/>
+    </g>
+    <g style="display:inline">
+        <path d="M88.2 95.309H64.92c-1.601 0-2.91 1.236-2.91 2.746l.022 18.602-.435 2.506 6.231-1.881H88.2c1.6 0 2.91-1.236 2.91-2.747v-16.48c0-1.51-1.31-2.746-2.91-2.746z" style="color:#fff;fill:url(#e);stroke:none;stroke-width:2.49558;-inkscape-stroke:none" transform="translate(-51.147 -81.516)"/>
+        <path d="M50.4 46.883c-9.168 0-17.023 7.214-17.023 16.387v.007l.09 71.37-2.303 16.992 31.313-8.319h77.841c9.17 0 17.024-7.224 17.024-16.396V63.27c0-9.17-7.85-16.383-17.016-16.387h-.008zm0 11.566h89.926c3.222.004 5.45 2.347 5.45 4.82v63.655c0 2.475-2.232 4.82-5.457 4.82h-79.54l-15.908 4.807.162-.938-.088-72.343c0-2.476 2.23-4.82 5.455-4.82z" style="color:#fff;fill:#fff;stroke:none;stroke-width:1.93113;-inkscape-stroke:none" transform="scale(.26458)"/>
+        <g style="font-size:8.48274px;font-family:sans-serif;letter-spacing:0;word-spacing:0;fill:#fff;stroke:none;stroke-width:.525121">
+            <path d="M62.57 116.77v-1.312l3.28-1.459q.159-.068.306-.102.158-.045.283-.068l.271-.022v-.09q-.136-.012-.271-.046-.125-.023-.283-.057-.147-.045-.306-.113l-3.28-1.459v-1.323l5.068 2.319v1.413z" style="color:#fff;-inkscape-font-specification:&quot;JetBrains Mono, Bold&quot;;fill:#fff;stroke:none;-inkscape-stroke:none" transform="matrix(1.45366 0 0 1.72815 -75.122 -171.953)"/>
+            <path d="M62.309 110.31v1.903l3.437 1.53.022.007-.022.008-3.437 1.53v1.892l.37-.17 5.221-2.39v-1.75zm.525.817 4.541 2.08v1.076l-4.541 2.078v-.732l3.12-1.389.003-.002a1.56 1.56 0 0 1 .258-.086h.006l.008-.002c.094-.027.176-.047.246-.06l.498-.041v-.574l-.24-.02a1.411 1.411 0 0 1-.231-.04l-.008-.001-.008-.002a9.077 9.077 0 0 1-.263-.053 2.781 2.781 0 0 1-.266-.097l-.004-.002-3.119-1.39z"
+                  style="color:#fff;-inkscape-font-specification:&quot;JetBrains Mono, Bold&quot;;fill:#fff;stroke:none;-inkscape-stroke:none" transform="matrix(1.45366 0 0 1.72815 -75.122 -171.953)"/>
+        </g>
+        <g style="font-size:8.48274px;font-family:sans-serif;letter-spacing:0;word-spacing:0;fill:#fff;stroke:none;stroke-width:.525121">
+            <path d="M69.171 117.754h5.43v1.278h-5.43Z" style="color:#fff;-inkscape-font-specification:&quot;JetBrains Mono, Bold&quot;;fill:#fff;stroke:none;-inkscape-stroke:none" transform="matrix(1.44935 0 0 1.66414 -74.104 -166.906)"/>
+            <path d="M68.908 117.492v1.802h5.955v-1.802zm.526.524h4.904v.754h-4.904z" style="color:#fff;-inkscape-font-specification:&quot;JetBrains Mono, Bold&quot;;fill:#fff;stroke:none;-inkscape-stroke:none" transform="matrix(1.44935 0 0 1.66414 -74.104 -166.906)"/>
+        </g>
+    </g>
+</svg>
diff --git a/Website/media/link.svg b/Website/media/link.svg
new file mode 100644
index 0000000..e73cfdd
--- /dev/null
+++ b/Website/media/link.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12">
+	<title>
+		external link
+	</title>
+	<path fill="#fff" d="M6 1h5v5L8.86 3.85 4.7 8 4 7.3l4.15-4.16L6 1Z M2 3h2v1H2v6h6V8h1v2a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1Z"/>
+</svg>
diff --git a/Website/media/queue.svg b/Website/media/queue.svg
deleted file mode 100644
index 30b9620..0000000
--- a/Website/media/queue.svg
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
-<svg width="800px" height="800px" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="none">
-
<g fill="#000000">
-
<path d="M2.23 2.674a.75.75 0 00-.96 1.152L3.578 5.75 1.27 7.674a.75.75 0 00.96 1.152l3-2.5a.75.75 0 000-1.152l-3-2.5zM8.25 5a.75.75 0 000 1.5h6a.75.75 0 000-1.5h-6zM5.5 9.25a.75.75 0 01.75-.75h8a.75.75 0 010 1.5h-8a.75.75 0 01-.75-.75zM6.25 12a.75.75 0 000 1.5h8a.75.75 0 000-1.5h-8z"/>
-
</g>
-
</svg>
\ No newline at end of file
diff --git a/Website/media/running.svg b/Website/media/running.svg
deleted file mode 100644
index ddde0a6..0000000
--- a/Website/media/running.svg
+++ /dev/null
@@ -1,53 +0,0 @@
-<?xml version="1.0" encoding="iso-8859-1"?>
-<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
-<svg fill="#000000" version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" 
-	 width="800px" height="800px" viewBox="0 0 235.504 235.504"
-	 xml:space="preserve">
-<g>
-	<g>
-		<path d="M195.209,81.456l-49.227-0.15c0.737-0.886,1.351-1.868,2.284-2.583c3.282-2.497,3.911-7.166,1.427-10.438
-			c-2.501-3.266-7.161-3.919-10.443-1.423c-4.873,3.715-8.388,8.704-10.255,14.389l-22.191-0.064
-			c-9.508,0-19.588,7.398-22.938,16.851l-16.877,47.479c-1.775,5.013-1.338,9.966,1.207,13.568
-			c2.412,3.427,6.384,5.318,11.187,5.358l45.126,0.136c-1.509,5.186-4.701,9.622-9.352,12.424
-			c-4.891,2.957-10.636,3.814-16.172,2.444c-3.994-0.998-8.031,1.442-9.027,5.418c-0.99,4.012,1.445,8.035,5.432,9.032
-			c2.927,0.738,5.879,1.091,8.808,1.091c6.516,0,12.93-1.788,18.645-5.23c8.312-5.013,14.172-12.979,16.484-22.409
-			c0.232-0.905,0.232-1.823,0.124-2.713l28.296,0.092h0.049c2.925,0,5.854-0.89,8.684-2.147c0.2,0.493,0.32,1.014,0.661,1.471
-			c3.335,4.677,4.629,10.343,3.688,15.993c-0.95,5.627-4.028,10.536-8.688,13.862c-3.351,2.376-4.14,7.037-1.755,10.379
-			c1.466,2.04,3.751,3.122,6.062,3.122c1.491,0,3.006-0.429,4.312-1.367c7.919-5.61,13.16-13.966,14.771-23.52
-			c1.603-9.565-0.613-19.203-6.28-27.122c-0.48-0.693-1.134-1.19-1.779-1.659c1.318-1.831,2.501-3.763,3.238-5.854l16.863-47.464
-			c1.795-5.018,1.351-9.969-1.194-13.58C203.954,83.387,200.015,81.47,195.209,81.456z M201.979,98.405l-16.868,47.464
-			c-0.981,2.757-2.941,5.214-5.213,7.329c-0.337,0.16-0.706,0.229-1.026,0.465c-0.673,0.485-1.182,1.122-1.639,1.747
-			c-2.962,1.996-6.288,3.339-9.434,3.339v2.989l-0.044-2.989l-33.194-0.101c-0.232-0.076-0.424-0.261-0.661-0.324
-			c-1.435-0.353-2.805-0.145-4.095,0.309l-29.768-0.101l1.192-3.358c0.549-1.547-0.269-3.25-1.813-3.795
-			c-1.521-0.553-3.25,0.24-3.799,1.804l-1.899,5.334l-14.318-0.044c-2.805,0-5.063-0.998-6.336-2.813
-			c-1.437-2.032-1.603-4.921-0.463-8.144l16.877-47.478c2.48-6.979,10.417-12.868,17.356-12.868l12.217,0.038l-1.963,5.536
-			c-0.555,1.549,0.262,3.25,1.805,3.797c0.331,0.12,0.661,0.174,0.998,0.174c1.227,0,2.372-0.768,2.793-1.986l2.497-7.019
-			c0.064-0.164-0.048-0.322-0.016-0.487h2.512c-0.905,7.758,1.163,15.42,5.947,21.638c5.903,7.687,14.852,11.726,23.873,11.726
-			c6.371,0,12.771-2.001,18.186-6.129c3.266-2.488,3.911-7.167,1.426-10.441c-2.508-3.267-7.161-3.901-10.455-1.415
-			c-6.612,5.056-16.146,3.775-21.223-2.809c-2.445-3.194-3.487-7.133-2.958-11.117c0.061-0.503,0.353-0.916,0.481-1.402
-			l52.216,0.156c2.806,0,5.054,1.004,6.324,2.811C202.928,92.241,203.105,95.223,201.979,98.405z"/>
-		<path d="M107.997,127.194c-1.531-0.553-3.248,0.244-3.799,1.791l-4.302,12.099c-0.551,1.543,0.265,3.242,1.813,3.795
-			c0.331,0.116,0.659,0.16,0.998,0.16c1.214,0,2.372-0.765,2.801-1.976l4.294-12.099
-			C110.369,129.446,109.551,127.728,107.997,127.194z"/>
-		<path d="M116.6,103.014c-1.529-0.541-3.25,0.252-3.805,1.805l-4.298,12.088c-0.547,1.547,0.261,3.252,1.799,3.799
-			c0.329,0.12,0.659,0.172,1,0.172c1.222,0,2.368-0.769,2.809-1.983l4.294-12.09C118.955,105.268,118.139,103.555,116.6,103.014z"/>
-		<path d="M232.527,90.428l-14.896-0.038l0,0c-1.639,0-2.974,1.327-2.997,2.976c0,1.639,1.342,2.981,2.981,2.989l14.896,0.042l0,0
-			c1.643,0,2.978-1.331,2.993-2.979C235.504,91.763,234.17,90.436,232.527,90.428z"/>
-		<path d="M220.333,80.436c0.629,0,1.242-0.188,1.771-0.583l11.994-8.83c1.326-0.974,1.611-2.842,0.645-4.168
-			c-0.965-1.327-2.845-1.611-4.163-0.637l-11.998,8.833c-1.323,0.974-1.607,2.841-0.642,4.167
-			C218.513,80.003,219.418,80.436,220.333,80.436z"/>
-		<path d="M209.152,56.279c-1.547-0.549-3.25,0.269-3.787,1.805l-4.997,14.036c-0.537,1.547,0.26,3.252,1.803,3.807
-			c0.337,0.12,0.674,0.172,0.994,0.172c1.242,0,2.385-0.757,2.821-1.986l4.985-14.036C211.516,58.541,210.695,56.846,209.152,56.279
-			z"/>
-		<path d="M17.587,100.894h55.208c1.641,0,2.976-1.343,2.976-2.981c0-1.641-1.334-2.988-2.976-2.988H17.587
-			c-1.641,0-2.988,1.338-2.988,2.988C14.599,99.559,15.946,100.894,17.587,100.894z"/>
-		<path d="M68.471,119.328c0-1.641-1.345-2.987-2.986-2.987H10.283c-1.639,0-2.981,1.338-2.981,2.987
-			c0,1.639,1.342,2.974,2.981,2.974h55.202C67.119,122.301,68.471,120.967,68.471,119.328z"/>
-		<path d="M58.188,137.758H2.974c-1.641,0-2.974,1.335-2.974,2.989c0,1.64,1.333,2.974,2.974,2.974h55.214
-			c1.639,0,2.981-1.334,2.981-2.974C61.162,139.093,59.827,137.758,58.188,137.758z"/>
-		<path d="M169.611,28.097c11.821,0,21.403,9.584,21.403,21.41c0,11.82-9.582,21.408-21.403,21.408
-			c-11.822,0-21.412-9.588-21.412-21.408C148.199,37.681,157.789,28.097,169.611,28.097z"/>
-	</g>
-</g>
-</svg>
\ No newline at end of file
diff --git a/Website/media/tasks.svg b/Website/media/tasks.svg
deleted file mode 100644
index 6e64e66..0000000
--- a/Website/media/tasks.svg
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
-<svg fill="#000000" height="800px" width="800px" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" 
-	 viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
-<g id="task">
-	<path d="M4,23.4l-3.7-3.7l1.4-1.4L4,20.6l4.3-4.3l1.4,1.4L4,23.4z M24,21H12v-2h12V21z M4,15.4l-3.7-3.7l1.4-1.4L4,12.6l4.3-4.3
-		l1.4,1.4L4,15.4z M24,13H12v-2h12V13z M4,7.4L0.3,3.7l1.4-1.4L4,4.6l4.3-4.3l1.4,1.4L4,7.4z M24,5H12V3h12V5z"/>
-</g>
-</svg>
\ No newline at end of file
diff --git a/Website/modules/Footer.tsx b/Website/modules/Footer.tsx
new file mode 100644
index 0000000..d0d9a44
--- /dev/null
+++ b/Website/modules/Footer.tsx
@@ -0,0 +1,48 @@
+import React, {useEffect} from 'react';
+import '../styles/footer.css';
+import Job from './Job';
+import Icon from '@mdi/react';
+import { mdiRun, mdiCounter, mdiEyeCheck, mdiTrayFull } from '@mdi/js';
+import QueuePopUp from "./QueuePopUp";
+
+export default function Footer({connectedToBackend, apiUri} : {connectedToBackend: boolean, apiUri: string}) {
+    const [MonitoringJobsCount, setMonitoringJobsCount] = React.useState(0);
+    const [AllJobsCount, setAllJobsCount] = React.useState(0);
+    const [RunningJobsCount, setRunningJobsCount] = React.useState(0);
+    const [StandbyJobsCount, setStandbyJobsCount] = React.useState(0);
+    const [countUpdateInterval, setCountUpdateInterval] = React.useState<number>();
+
+    function UpdateBackendState(){
+        Job.GetMonitoringJobs(apiUri).then((jobs) => setMonitoringJobsCount(jobs.length));
+        Job.GetAllJobs(apiUri).then((jobs) => setAllJobsCount(jobs.length));
+        Job.GetRunningJobs(apiUri).then((jobs) => setRunningJobsCount(jobs.length));
+        Job.GetStandbyJobs(apiUri).then((jobs) => setStandbyJobsCount(jobs.length));
+    }
+
+    useEffect(() => {
+        if(connectedToBackend){
+            UpdateBackendState();
+            setCountUpdateInterval(setInterval(() => {
+                UpdateBackendState();
+            }, 2000));
+        }else{
+            clearInterval(countUpdateInterval);
+            setCountUpdateInterval(undefined);
+        }
+    }, [connectedToBackend]);
+
+    return (
+        <footer>
+            <div className="statusBadge" ><Icon path={mdiEyeCheck} size={1}/> <span>{MonitoringJobsCount}</span></div>
+            <span>+</span>
+            <QueuePopUp connectedToBackend={connectedToBackend} apiUri={apiUri}>
+                <div className="statusBadge hoverHand"><Icon path={mdiRun} size={1}/> <span>{RunningJobsCount}</span>
+                </div>
+                <span>+</span>
+                <div className="statusBadge hoverHand"><Icon path={mdiTrayFull} size={1}/><span>{StandbyJobsCount}</span></div>
+            </QueuePopUp>
+            <span>=</span>
+            <div className="statusBadge"><Icon path={mdiCounter} size={1}/> <span>{AllJobsCount}</span></div>
+            <p id="madeWith">Made with Blåhaj 🦈</p>
+        </footer>)
+}
\ No newline at end of file
diff --git a/Website/modules/Header.tsx b/Website/modules/Header.tsx
new file mode 100644
index 0000000..bdd76ea
--- /dev/null
+++ b/Website/modules/Header.tsx
@@ -0,0 +1,15 @@
+import React from 'react';
+import '../styles/header.css'
+import Settings from "./Settings";
+import IFrontendSettings from "./interfaces/IFrontendSettings";
+
+export default function Header({backendConnected, apiUri, settings, changeSettings} : {backendConnected: boolean, apiUri: string, settings: IFrontendSettings, changeSettings(settings: IFrontendSettings): void}){
+    return (
+        <header>
+            <div id="titlebox">
+                <img alt="website image is Blahaj" src="../media/blahaj.png"/>
+                <span>Tranga</span>
+            </div>
+            <Settings settings={settings} changeSettings={changeSettings} backendConnected={backendConnected} apiUri={apiUri}/>
+        </header>)
+}
\ No newline at end of file
diff --git a/Website/modules/Job.tsx b/Website/modules/Job.tsx
new file mode 100644
index 0000000..09709b7
--- /dev/null
+++ b/Website/modules/Job.tsx
@@ -0,0 +1,142 @@
+import {deleteData, getData, postData} from '../App';
+import IJob from "./interfaces/IJob";
+import IProgressToken from "./interfaces/IProgressToken";
+
+export default class Job
+{
+    static IntervalStringFromDate(date: Date) : string {
+        let x = new Date(date);
+        return `${x.getDay()}.${x.getHours()}:${x.getMinutes()}:${x.getSeconds()}`;
+    }
+
+    static async GetAllJobs(apiUri: string): Promise<string[]> {
+        //console.info("Getting all Jobs");
+        return getData(`${apiUri}/v2/Jobs`)
+            .then((json) => {
+                //console.info("Got all Jobs");
+                const ret = json as string[];
+                //console.debug(ret);
+                return (ret);
+            });
+    }
+
+    static async GetRunningJobs(apiUri: string): Promise<string[]> {
+        //console.info("Getting all running Jobs");
+        return getData(`${apiUri}/v2/Jobs/Running`)
+            .then((json) => {
+                //console.info("Got all running Jobs");
+                const ret = json as string[];
+                //console.debug(ret);
+                return (ret);
+            });
+    }
+
+    static async GetWaitingJobs(apiUri: string): Promise<string[]> {
+        //console.info("Getting all waiting Jobs");
+        return getData(`${apiUri}/v2/Jobs/Waiting`)
+            .then((json) => {
+                //console.info("Got all waiting Jobs");
+                const ret = json as string[];
+                //console.debug(ret);
+                return (ret);
+            });
+    }
+
+    static async GetStandbyJobs(apiUri: string): Promise<string[]> {
+        //console.info("Getting all standby Jobs");
+        return getData(`${apiUri}/v2/Jobs/Standby`)
+            .then((json) => {
+                //console.info("Got all standby Jobs");
+                const ret = json as string[];
+                //console.debug(ret);
+                return (ret);
+            });
+    }
+
+    static async GetMonitoringJobs(apiUri: string): Promise<string[]> {
+        //console.info("Getting all monitoring Jobs");
+        return getData(`${apiUri}/v2/Jobs/Monitoring`)
+            .then((json) => {
+                //console.info("Got all monitoring Jobs");
+                const ret = json as string[];
+                //console.debug(ret);
+                return (ret);
+            });
+    }
+
+    static async GetJob(apiUri: string, jobId: string): Promise<IJob>{
+        if(jobId === undefined || jobId === null || jobId.length < 1) {
+            console.error(`JobId was not provided`);
+            return Promise.reject();
+        }
+        //console.info(`Getting Job ${jobId}`);
+        return getData(`${apiUri}/v2/Job/${jobId}`)
+            .then((json) => {
+                //console.info(`Got Job ${jobId}`);
+                const ret = json as IJob;
+                //console.debug(ret);
+                return (ret);
+            });
+    }
+
+    static async GetJobs(apiUri: string, jobIds: string[]): Promise<IJob[]> {
+        if(jobIds === undefined || jobIds === null || jobIds.length < 1) {
+            console.error(`JobIds was not provided`);
+            return Promise.reject();
+        }
+        let reqStr = jobIds.join(",");
+        //console.info(`Getting Jobs ${reqStr}`);
+        return getData(`${apiUri}/v2/Job?jobIds=${reqStr}`)
+            .then((json) => {
+                //console.info(`Got Jobs ${reqStr}`);
+                const ret = json as IJob[];
+                //console.debug(ret);
+                return (ret);
+            });
+    }
+
+    static async GetJobProgress(apiUri: string, jobId: string): Promise<IProgressToken> {
+        //console.info(`Getting Job ${jobId} Progress`);
+        return getData(`${apiUri}/v2/Job/${jobId}/Progress`)
+            .then((json) => {
+                //console.info(`Got Job ${jobId} Progress`);
+                const ret = json as IProgressToken;
+                //console.debug(ret);
+                return (ret);
+            });
+    }
+
+    static async CreateJobDateInterval(apiUri: string, internalId: string, jobType: string, interval: Date) : Promise<null> {
+        return this.CreateJob(apiUri, internalId, jobType, this.IntervalStringFromDate(interval));
+    }
+
+    static async CreateJob(apiUri: string, internalId: string, jobType: string, interval: string): Promise<null> {
+        const validate = /(?:[0-9]{1,2}\.)?[0-9]{1,2}:[0-9]{1,2}(?::[0-9]{1,2})?/
+        //console.info(`Creating Job for Manga ${internalId} at ${interval} interval`);
+        if(!validate.test(interval)){
+            console.error("Interval was in incorrect format.");
+            return Promise.reject();
+        }
+        const data = {
+            internalId: internalId,
+            interval: interval
+        };
+        return postData(`${apiUri}/v2/Job/Create/${jobType}`, data)
+            .then((json) => {
+                //console.info(`Created Job for Manga ${internalId} at ${interval} interval`);
+                return null;
+            });
+    }
+
+    static DeleteJob(apiUri: string, jobId: string) : Promise<void> {
+        return deleteData(`${apiUri}/v2/Job/${jobId}`);
+    }
+
+    static StartJob(apiUri: string, jobId: string) : Promise<object> {
+        return postData(`${apiUri}/v2/Job/${jobId}/StartNow`, {});
+    }
+
+    static CancelJob(apiUri: string, jobId: string) : Promise<object> {
+        return postData(`${apiUri}/v2/Job/${jobId}/Cancel`, {});
+    }
+}
\ No newline at end of file
diff --git a/Website/modules/LibraryConnector.tsx b/Website/modules/LibraryConnector.tsx
new file mode 100644
index 0000000..219e48a
--- /dev/null
+++ b/Website/modules/LibraryConnector.tsx
@@ -0,0 +1,135 @@
+import {deleteData, getData, postData} from "../App";
+import ILibraryConnector from "./interfaces/ILibraryConnector";
+
+export default abstract class LibraryConnector
+{
+    static async GetLibraryConnectors(apiUri: string) : Promise<ILibraryConnector[]> {
+    //console.info("Getting Library Connectors");
+    return getData(`${apiUri}/v2/LibraryConnector`)
+        .then((json) => {
+            //console.info("Got Library Connectors");
+            const ret = json as ILibraryConnector[];
+            //console.debug(ret);
+            return (ret);
+        })
+        .catch(Promise.reject);
+    }
+    public url = "";
+
+    protected constructor(url: string) {
+        this.url = url;
+    }
+
+    public SetUrl(url: string){
+        this.url = url;
+    }
+
+    public abstract Test(apiUri: string) : Promise<boolean>;
+    public abstract Reset(apiUri: string) : Promise<boolean>;
+    public abstract Create(apiUri: string) : Promise<boolean>;
+    protected abstract CheckConnector() : boolean;
+
+    protected async TestConnector(apiUri: string, connectorType: string, data: object): Promise<boolean> {
+        if(!this.CheckConnector())
+            return Promise.reject("Connector not fully configured.");
+        //console.info(`Testing ${connectorType}`);
+        return postData(`${apiUri}/v2/LibraryConnector/${connectorType}/Test`, data)
+            .then((json) => {
+                //console.info(`Successfully tested ${connectorType}`);
+                return true;
+            })
+            .catch(Promise.reject);
+    }
+
+    protected async ResetConnector(apiUri: string, connectorType: string): Promise<boolean> {
+        //console.info(`Deleting ${connectorType}`);
+        return deleteData(`${apiUri}/v2/LibraryConnector/${connectorType}`)
+            .then((json) => {
+                //console.info(`Successfully deleted ${connectorType}`);
+                return true;
+            })
+            .catch(Promise.reject);
+    }
+
+    protected async CreateConnector(apiUri: string, connectorType: string, data: object): Promise<boolean> {
+        if(!this.CheckConnector())
+            return Promise.reject("Connector not fully configured.");
+        //console.info(`Creating ${connectorType}`);
+        return postData(`${apiUri}/v2/LibraryConnector/${connectorType}`, data)
+            .then((json) => {
+                //console.info(`Successfully created ${connectorType}`);
+                return true;
+            })
+            .catch(Promise.reject);
+    }
+}
+
+export class Komga extends LibraryConnector
+{
+    private username = "";
+    private password = "";
+
+    constructor({url, username, password} : {url: string, username: string, password: string}){
+        super(url);
+        this.username = username;
+        this.password = password;
+    }
+
+    public async Test(apiUri: string) : Promise<boolean> {
+        return this.TestConnector(apiUri, "Komga", {url: this.url, username: this.username, password: this.password}).then(() => true).catch(() => false);
+    }
+
+    public async Reset(apiUri: string) : Promise<boolean> {
+        return this.ResetConnector(apiUri, "Komga").then(() => true).catch(() => false);
+    }
+
+    public async Create(apiUri: string) : Promise<boolean> {
+        return this.CreateConnector(apiUri, "Komga", {url: this.url, username: this.username, password: this.password}).then(() => true).catch(() => false);
+    }
+
+    protected CheckConnector(): boolean {
+        try{
+            new URL(this.url)
+        }catch{
+            return false;
+        }
+        if(this.username.length < 1 || this.password.length < 1)
+            return false;
+        return true;
+    }
+}
+
+export class Kavita extends LibraryConnector
+{
+    private username = "";
+    private password = "";
+
+    constructor({url, username, password} : {url: string, username: string, password: string}) {
+        super(url);
+        this.username = username;
+        this.password = password;
+    }
+
+    public async Test(apiUri: string) : Promise<boolean> {
+        return this.TestConnector(apiUri, "Kavita", {url: this.url, username: this.username, password: this.password}).then(() => true).catch(() => false);
+    }
+
+    public async Reset(apiUri: string) : Promise<boolean> {
+        return this.ResetConnector(apiUri, "Kavita").then(() => true).catch(() => false);
+    }
+
+    public async Create(apiUri: string) : Promise<boolean> {
+        return this.CreateConnector(apiUri, "Kavita", {url: this.url, username: this.username, password: this.password}).then(() => true).catch(() => false);
+    }
+
+    protected CheckConnector(): boolean {
+        try{
+            new URL(this.url)
+        }catch{
+            return false;
+        }
+        if(this.username.length < 1 || this.password.length < 1)
+            return false;
+        return true;
+    }
+}
\ No newline at end of file
diff --git a/Website/modules/Manga.tsx b/Website/modules/Manga.tsx
new file mode 100644
index 0000000..2e0e419
--- /dev/null
+++ b/Website/modules/Manga.tsx
@@ -0,0 +1,54 @@
+import IManga from './interfaces/IManga';
+import { getData } from '../App';
+
+export default class Manga
+{
+    static async GetAllManga(apiUri: string): Promise<IManga[]> {
+        //console.info("Getting all Manga");
+        return getData(`${apiUri}/v2/Mangas`)
+            .then((json) => {
+                //console.info("Got all Manga");
+                const ret = json as IManga[];
+                //console.debug(ret);
+                return (ret);
+            });
+    }
+
+    static async SearchManga(apiUri: string, name: string): Promise<IManga[]> {
+        //console.info(`Getting Manga ${name} from all Connectors`);
+        return await getData(`${apiUri}/v2/Manga/Search?title=${name}`)
+            .then((json) => {
+                //console.info(`Got Manga ${name}`);
+                const ret = json as IManga[];
+                //console.debug(ret);
+                return (ret);
+            });
+    }
+
+    static async GetMangaById(apiUri: string, internalId: string): Promise<IManga> {
+        //console.info(`Getting Manga ${internalId}`);
+        return await getData(`${apiUri}/v2/Manga/${internalId}`)
+            .then((json) => {
+                //console.info(`Got Manga ${internalId}`);
+                const ret = json as IManga;
+                //console.debug(ret);
+                return (ret);
+            });
+    }
+
+    static async GetMangaByIds(apiUri: string, internalIds: string[]): Promise<IManga[]> {
+        //console.debug(`Getting Mangas ${internalIds.join(",")}`);
+        return await getData(`${apiUri}/v2/Manga?mangaIds=${internalIds.join(",")}`)
+            .then((json) => {
+                //console.debug(`Got Manga ${internalIds.join(",")}`);
+                const ret = json as IManga[];
+                //console.debug(ret);
+                return (ret);
+            });
+    }
+
+    static GetMangaCoverUrl(apiUri: string, internalId: string, ref: HTMLElement): string {
+        //console.debug(`Getting Manga Cover-Url ${internalId}`);
+        return `${apiUri}/v2/Manga/${internalId}/Cover?dimensions=${ref.clientWidth*1.5}x${ref.clientHeight*1.5}`;
+    }
+}
\ No newline at end of file
diff --git a/Website/modules/MangaConnector.tsx b/Website/modules/MangaConnector.tsx
new file mode 100644
index 0000000..25637f0
--- /dev/null
+++ b/Website/modules/MangaConnector.tsx
@@ -0,0 +1,33 @@
+import IMangaConnector from './interfaces/IMangaConnector';
+import IManga from './interfaces/IManga';
+import { getData } from '../App';
+
+export class MangaConnector
+{
+    static async GetAllConnectors(): Promise<IMangaConnector[]> {
+        //console.info("Getting all MangaConnectors");
+        return getData("http://127.0.0.1:6531/v2/Connector/Types")
+            .then((json) => {
+                //console.info("Got all MangaConnectors");
+                return (json as IMangaConnector[]);
+            });
+    }
+
+    static async GetMangaFromConnectorByTitle(connector: IMangaConnector, name: string): Promise<IManga[]> {
+        //console.info(`Getting Manga ${name}`);
+        return await getData(`http://127.0.0.1:6531/v2/Connector/${connector.name}/GetManga?title=${name}`)
+            .then((json) => {
+                //console.info(`Got Manga ${name}`);
+                return (json as IManga[]);
+            });
+    }
+
+    static async GetMangaFromConnectorByUrl(connector: IMangaConnector, url: string): Promise<IManga> {
+        //console.info(`Getting Manga ${url}`);
+        return await getData(`http://127.0.0.1:6531/v2/Connector/${connector.name}/GetManga?url=${url}`)
+            .then((json) => {
+                //console.info(`Got Manga ${url}`);
+                return (json as IManga);
+            });
+    }
+}
\ No newline at end of file
diff --git a/Website/modules/MonitorJobsList.tsx b/Website/modules/MonitorJobsList.tsx
new file mode 100644
index 0000000..2d1c11b
--- /dev/null
+++ b/Website/modules/MonitorJobsList.tsx
@@ -0,0 +1,100 @@
+import React, {EventHandler, MouseEventHandler, ReactElement, useEffect, useState} from 'react';
+import Job from './Job';
+import '../styles/monitorMangaList.css';
+import IJob from "./interfaces/IJob";
+import IManga, {CoverCard} from "./interfaces/IManga";
+import Manga from './Manga';
+import '../styles/MangaCoverCard.css'
+import Icon from '@mdi/react';
+import { mdiTrashCanOutline, mdiPlayBoxOutline } from '@mdi/js';
+
+export default function MonitorJobsList({onStartSearch, onJobsChanged, connectedToBackend, apiUri, updateList} : {onStartSearch() : void, onJobsChanged: EventHandler<any>, connectedToBackend: boolean, apiUri: string, updateList: Date}) {
+    const [MonitoringJobs, setMonitoringJobs] = useState<IJob[]>([]);
+    const [AllManga, setAllManga] = useState<IManga[]>([]);
+    const [joblistUpdateInterval, setJoblistUpdateInterval] = React.useState<number>();
+
+    useEffect(() => {
+        //console.debug("Updating display list.");
+        //Remove all Manga that are not associated with a Job
+        setAllManga(AllManga.filter(manga => MonitoringJobs.find(job => job.mangaInternalId == manga.internalId)));
+        //Fetch Manga that are missing (from Jobs)
+        if(MonitoringJobs === null)
+            return;
+        MonitoringJobs.forEach(job => {
+            if(AllManga.find(manga => manga.internalId == job.mangaInternalId))
+                return;
+            Manga.GetMangaById(apiUri, job.mangaInternalId != undefined ? job.mangaInternalId : job.chapter != undefined ? job.chapter.parentManga.internalId : "").
+            then((manga: IManga) => setAllManga([...AllManga, manga]));
+        });
+    }, [MonitoringJobs]);
+
+    useEffect(() => {
+        if(connectedToBackend){
+            UpdateMonitoringJobsList();
+            setJoblistUpdateInterval(setInterval(() => {
+                UpdateMonitoringJobsList();
+            }, 1000));
+        }else{
+            clearInterval(joblistUpdateInterval);
+            setJoblistUpdateInterval(undefined);
+        }
+    }, [connectedToBackend]);
+
+    useEffect(() => {
+        UpdateMonitoringJobsList();
+    }, [updateList]);
+
+    function UpdateMonitoringJobsList(){
+        if(!connectedToBackend)
+            return;
+        //console.debug("Updating MonitoringJobsList");
+        Job.GetMonitoringJobs(apiUri)
+            .then((jobs) => {
+                if(jobs.length > 0)
+                    return Job.GetJobs(apiUri, jobs)
+                return [];
+            })
+            .then((jobs) => setMonitoringJobs(jobs));
+    }
+
+    function StartSearchMangaEntry() : ReactElement {
+        return (<div key="monitorMangaEntry.StartSearch" className="monitorMangaEntry" onClick={onStartSearch}>
+            <div className="Manga" key="StartSearch.Manga">
+                <img src="../media/blahaj.png" alt="Blahaj"></img>
+                <div>
+                    <p style={{textAlign: "center", width: "100%"}} className="Manga-name">Add new Manga</p>
+                    <p style={{fontSize: "42pt", textAlign: "center"}}>+</p>
+                </div>
+            </div>
+        </div>);
+    }
+
+    const DeleteJob : MouseEventHandler = (e) => {
+        const jobId = e.currentTarget.id.slice(e.currentTarget.id.indexOf("-")+1);
+        //console.info(`Pressed ${e.currentTarget.id} => ${jobId}`);
+        Job.DeleteJob(apiUri, jobId).then(() => onJobsChanged(jobId));
+    }
+
+    const StartJob : MouseEventHandler = (e) => {
+        const jobId = e.currentTarget.id.slice(e.currentTarget.id.indexOf("-")+1);
+        //console.info(`Pressed ${e.currentTarget.id} => ${jobId}`);
+        Job.StartJob(apiUri, jobId);
+    }
+
+    return (
+        <div id="MonitorMangaList">
+            {StartSearchMangaEntry()}
+            {AllManga.map((manga: IManga) => {
+                const job = MonitoringJobs.find(job => job.mangaInternalId == manga.internalId);
+                if (job === undefined || job == null)
+                    return <div>Error. Could not find matching job for {manga.internalId}</div>
+                return <div key={"monitorMangaEntry-" + manga.internalId} className="monitorMangaEntry">
+                    {CoverCard(apiUri, manga)}
+                    <div className="MangaActionButtons">
+                        <div id={"Delete-"+job.id} className="DeleteJobButton" onClick={DeleteJob}><Icon path={mdiTrashCanOutline} size={1.5} /></div>
+                        <div id={"Start-"+job.id} className="StartJobNowButton" onClick={StartJob}><Icon path={mdiPlayBoxOutline} size={1.5} /></div>
+                    </div>
+                </div>;
+            })}
+        </div>)
+}
\ No newline at end of file
diff --git a/Website/modules/NotificationConnector.tsx b/Website/modules/NotificationConnector.tsx
new file mode 100644
index 0000000..6223653
--- /dev/null
+++ b/Website/modules/NotificationConnector.tsx
@@ -0,0 +1,164 @@
+import INotificationConnector from "./interfaces/INotificationConnector";
+import {deleteData, getData, postData} from "../App";
+
+export default abstract class NotificationConnector {
+
+    static async GetNotificationConnectors(apiUri: string) : Promise<INotificationConnector[]> {
+        //console.info("Getting Notification Connectors");
+        return getData(`${apiUri}/v2/NotificationConnector`)
+            .then((json) => {
+                //console.info("Got Notification Connectors");
+                const ret = json as INotificationConnector[];
+                //console.debug(ret);
+                return (ret);
+            })
+            .catch(Promise.reject);
+    }
+
+    protected constructor() {
+
+    }
+
+    public abstract Test(apiUri: string) : Promise<boolean>;
+    public abstract Reset(apiUri: string) : Promise<boolean>;
+    public abstract Create(apiUri: string) : Promise<boolean>;
+    protected abstract CheckConnector() : boolean;
+
+    protected async TestConnector(apiUri: string, connectorType: string, data: object): Promise<boolean> {
+        if(!this.CheckConnector())
+            return Promise.reject("Connector not fully configured.");
+        //console.info(`Testing ${connectorType}`);
+        return postData(`${apiUri}/v2/NotificationConnector/${connectorType}/Test`, data)
+            .then((json) => {
+                //console.info(`Successfully tested ${connectorType}`);
+                return true;
+            })
+            .catch(Promise.reject);
+    }
+
+    protected async ResetConnector(apiUri: string, connectorType: string): Promise<boolean> {
+        if(!this.CheckConnector())
+            return Promise.reject("Connector not fully configured.");
+        //console.info(`Deleting ${connectorType}`);
+        return deleteData(`${apiUri}/v2/NotificationConnector/${connectorType}`)
+            .then((json) => {
+                //console.info(`Successfully deleted ${connectorType}`);
+                return true;
+            })
+            .catch(Promise.reject);
+    }
+
+    protected async CreateConnector(apiUri: string, connectorType: string, data: object): Promise<boolean> {
+        if(!this.CheckConnector())
+            return Promise.reject("Connector not fully configured.");
+        //console.info(`Creating ${connectorType}`);
+        return postData(`${apiUri}/v2/NotificationConnector/${connectorType}`, data)
+            .then((json) => {
+                //console.info(`Successfully created ${connectorType}`);
+                return true;
+            })
+            .catch(Promise.reject);
+    }
+}
+
+export class Gotify extends NotificationConnector
+{
+    public url = "";
+    private appToken = "";
+
+    constructor({url, appToken} : {url: string, appToken:string}){
+        super();
+        this.url = url;
+        this.appToken = appToken;
+    }
+
+    public async Test(apiUri: string) : Promise<boolean> {
+        return this.TestConnector(apiUri, "Gotify", {url: this.url, appToken: this.appToken}).then(() => true).catch(() => false);
+    }
+
+    public async Reset(apiUri: string) : Promise<boolean> {
+        return this.ResetConnector(apiUri, "Gotify").then(() => true).catch(() => false);
+    }
+
+    public async Create(apiUri: string) : Promise<boolean> {
+        return this.CreateConnector(apiUri, "Gotify", {url: this.url, appToken: this.appToken}).then(() => true).catch(() => false);
+    }
+
+    protected CheckConnector(): boolean {
+        try{
+            new URL(this.url)
+        }catch{
+            return false;
+        }
+        if(this.appToken.length < 1 || this.appToken.length < 1)
+            return false;
+        return true;
+    }
+}
+
+export class Lunasea extends NotificationConnector
+{
+    private webhook = "";
+
+    constructor({webhook} : {webhook: string}){
+        super();
+        this.webhook = webhook;
+    }
+
+    public async Test(apiUri: string) : Promise<boolean> {
+        return this.TestConnector(apiUri, "LunaSea", {webhook: this.webhook}).then(() => true).catch(() => false);
+    }
+
+    public async Reset(apiUri: string) : Promise<boolean> {
+        return this.ResetConnector(apiUri, "LunaSea").then(() => true).catch(() => false);
+    }
+
+    public async Create(apiUri: string) : Promise<boolean> {
+        return this.CreateConnector(apiUri, "LunaSea", {webhook: this.webhook}).then(() => true).catch(() => false);
+    }
+
+    protected CheckConnector(): boolean {
+        if(this.webhook.length < 1 || this.webhook.length < 1)
+            return false;
+        return true;
+    }
+}
+
+export class Ntfy extends NotificationConnector
+{
+    public url = "";
+    private username = "";
+    private password = "";
+    public topic:string | undefined = undefined;
+
+    constructor({url, username, password, topic} : {url: string, username: string, password: string, topic : string | undefined}){
+        super();
+        this.url = url;
+        this.username = username;
+        this.password = password;
+        this.topic = topic;
+    }
+
+    public async Test(apiUri: string) : Promise<boolean> {
+        return this.TestConnector(apiUri, "Ntfy", {url: this.url, username: this.username, password: this.password, topic: this.topic}).then(() => true).catch(() => false);
+    }
+
+    public async Reset(apiUri: string) : Promise<boolean> {
+        return this.ResetConnector(apiUri, "Ntfy").then(() => true).catch(() => false);
+    }
+
+    public async Create(apiUri: string) : Promise<boolean> {
+        return this.CreateConnector(apiUri, "Ntfy", {url: this.url, username: this.username, password: this.password, topic: this.topic}).then(() => true).catch(() => false);
+    }
+
+    protected CheckConnector(): boolean {
+        try{
+            new URL(this.url)
+        }catch{
+            return false;
+        }
+        if(this.username.length < 1 || this.password.length < 1)
+            return false;
+        return true;
+    }
+}
\ No newline at end of file
diff --git a/Website/modules/QueuePopUp.tsx b/Website/modules/QueuePopUp.tsx
new file mode 100644
index 0000000..f9e4b57
--- /dev/null
+++ b/Website/modules/QueuePopUp.tsx
@@ -0,0 +1,120 @@
+import React, {useEffect, useState} from 'react';
+import IJob from "./interfaces/IJob";
+import '../styles/queuePopUp.css';
+import '../styles/popup.css';
+import Job from "./Job";
+import IManga, {QueueItem} from "./interfaces/IManga";
+import Manga from "./Manga";
+
+export default function QueuePopUp({connectedToBackend, children, apiUri} : {connectedToBackend: boolean, children: JSX.Element[], apiUri: string}) {
+
+    const [StandbyJobs, setStandbyJobs] = React.useState<IJob[]>([]);
+    const [StandbyJobsManga, setStandbyJobsManga] = React.useState<IManga[]>([]);
+    const [RunningJobs, setRunningJobs] = React.useState<IJob[]>([]);
+    const [RunningJobsManga, setRunningJobsManga] = React.useState<IManga[]>([]);
+    const [showQueuePopup, setShowQueuePopup] = useState<boolean>(false);
+    const [queueListInterval, setQueueListInterval] = React.useState<number>();
+
+    useEffect(() => {
+        if(!showQueuePopup)
+            return;
+        UpdateMonitoringJobsList();
+    }, [showQueuePopup]);
+
+    useEffect(() => {
+        if(connectedToBackend){
+            UpdateMonitoringJobsList();
+            setQueueListInterval(setInterval(() => {
+                UpdateMonitoringJobsList();
+            }, 2000));
+        }else{
+            clearInterval(queueListInterval);
+            setQueueListInterval(undefined);
+        }
+    }, [connectedToBackend]);
+
+    function UpdateMonitoringJobsList(){
+        Job.GetStandbyJobs(apiUri)
+            .then((jobs:string[]) => {
+                if(jobs.length > 0)
+                    return Job.GetJobs(apiUri, jobs);
+                return [];
+            })
+            .then((jobs:IJob[]) => {
+                //console.debug("Removing Metadata Jobs");
+                //console.log(StandbyJobs)
+                setStandbyJobs(jobs.filter(job => job.jobType <= 2));
+                //console.log(StandbyJobs)
+            });
+        Job.GetRunningJobs(apiUri)
+            .then((jobs:string[]) => {
+                if(jobs.length > 0)
+                    return Job.GetJobs(apiUri, jobs);
+                return [];
+            })
+            .then((jobs:IJob[]) =>{
+                //console.debug("Removing Metadata Jobs");
+                setRunningJobs(jobs.filter(job => job.jobType <= 2));
+            });
+    }
+
+    useEffect(() => {
+        if(StandbyJobs.length < 1)
+            return;
+        const mangaIds = StandbyJobs.filter(job => job.jobType<=2).map((job) => job.mangaInternalId != undefined ? job.mangaInternalId : job.chapter != undefined ? job.chapter.parentManga.internalId : "");
+        //console.debug(`Waiting mangaIds: ${mangaIds.join(",")}`);
+        Manga.GetMangaByIds(apiUri, mangaIds)
+            .then(setStandbyJobsManga);
+    }, [StandbyJobs]);
+
+    useEffect(() => {
+        if(RunningJobs.length < 1)
+            return;
+        //console.log(RunningJobs);
+        const mangaIds = RunningJobs.filter(job => job.jobType<=2).map((job) => job.mangaInternalId != undefined ? job.mangaInternalId : job.chapter != undefined ? job.chapter.parentManga.internalId : "");
+        //console.debug(`Running mangaIds: ${mangaIds.join(",")}`);
+        Manga.GetMangaByIds(apiUri, mangaIds)
+            .then(setRunningJobsManga);
+    }, [RunningJobs]);
+
+    return (<>
+            <div onClick={() => setShowQueuePopup(true)}>
+                {children}
+            </div>
+            {showQueuePopup
+                ? <div className="popup" id="QueuePopUp">
+                    <div className="popupHeader">
+                        <h1>Queue Status</h1>
+                        <img alt="Close Search" className="close" src="../media/close-x.svg"
+                             onClick={() => setShowQueuePopup(false)}/>
+                    </div>
+                    <div id="QueuePopUpBody" className="popupBody">
+                        <div id="RunningJobQueue">
+                            <h1>Running</h1>
+                            <div className="JobQueue">
+                                {RunningJobs.map((job: IJob) => {
+                                    const manga = RunningJobsManga.find(manga => manga.internalId == job.mangaInternalId || manga.internalId == job.chapter?.parentManga.internalId);
+                                    if (manga === undefined || manga === null)
+                                        return <div key={"QueueJob-" + job.id}>Error. Could not find matching manga for {job.id}</div>
+                                    return QueueItem(apiUri, manga, job, UpdateMonitoringJobsList);
+                                })}
+                            </div>
+                        </div>
+                        <div id="WaitingJobQueue">
+                            <h1>Standby</h1>
+                            <div className="JobQueue">
+                                {StandbyJobs.map((job: IJob) => {
+                                    const manga = StandbyJobsManga.find(manga => manga.internalId == job.mangaInternalId || manga.internalId == job.chapter?.parentManga.internalId);
+                                    if (manga === undefined || manga === null)
+                                        return <div key={"QueueJob-" + job.id}>Error. Could not find matching manga for {job.id}</div>
+                                    return QueueItem(apiUri, manga, job, UpdateMonitoringJobsList);
+                                })}
+                            </div>
+                        </div>
+                    </div>
+                </div>
+                : <></>
+            }
+        </>
+    );
+}
\ No newline at end of file
diff --git a/Website/modules/Search.tsx b/Website/modules/Search.tsx
new file mode 100644
index 0000000..f0d1c0a
--- /dev/null
+++ b/Website/modules/Search.tsx
@@ -0,0 +1,111 @@
+import React, {ChangeEventHandler, EventHandler, useEffect, useState} from 'react';
+import {MangaConnector} from "./MangaConnector";
+import IMangaConnector from "./interfaces/IMangaConnector";
+import {isValidUri} from "../App";
+import IManga, {SearchResult} from "./interfaces/IManga";
+import '../styles/search.css';
+import '../styles/MangaSearchResult.css'
+
+export default function Search({apiUri, jobInterval, onJobsChanged, closeSearch} : {apiUri: string, jobInterval: Date, onJobsChanged: (internalId: string) => void, closeSearch(): void}) {
+    const [mangaConnectors, setConnectors] = useState<IMangaConnector[]>();
+    const [selectedConnector, setSelectedConnector] = useState<IMangaConnector>();
+    const [selectedLanguage, setSelectedLanguage] = useState<string>();
+    const [searchBoxValue, setSearchBoxValue] = useState("");
+    const [searchResults, setSearchResults] = useState<IManga[]>();
+
+    const pattern = /https:\/\/([a-z0-9.]+\.[a-z0-9]{2,})(?:\/.*)?/i
+
+    useEffect(() => {
+        if(mangaConnectors === undefined) {
+            MangaConnector.GetAllConnectors().then(setConnectors);
+            return;
+        }
+    }, [mangaConnectors]);
+
+    const selectedConnectorChanged : ChangeEventHandler<HTMLSelectElement> = (event) => {
+        event.preventDefault();
+        if(mangaConnectors === undefined)
+            return;
+        const selectedConnector = mangaConnectors.find((con: IMangaConnector) => con.name == event.target.value);
+        if(selectedConnector === undefined)
+            return;
+        setSelectedConnector(selectedConnector);
+        setSelectedLanguage(selectedConnector.SupportedLanguages[0]);
+    }
+
+    const searchBoxValueChanged : ChangeEventHandler<HTMLInputElement> = (event) => {
+        event.currentTarget.style.width = event.target.value.length + "ch";
+        if(mangaConnectors === undefined)
+            return;
+        var str : string = event.target.value;
+        setSearchBoxValue(str);
+        if(isValidUri(str))
+            setSelectedConnector(undefined);
+        const match = str.match(pattern);
+        if(match === null)
+            return;
+        let baseUri = match[1];
+        const selectCon = mangaConnectors.find((con: IMangaConnector) => {
+            return con.BaseUris.find(uri => uri == baseUri);
+        });
+        if(selectCon != undefined){
+            setSelectedConnector(selectCon);
+            setSelectedLanguage(selectCon.SupportedLanguages[0]);
+        }
+    }
+
+    const ExecuteSearch : EventHandler<any> = () => {
+        if(searchBoxValue.length < 1 || selectedConnector === undefined || selectedLanguage === ""){
+            console.error("Tried initiating search while not all fields where submitted.")
+            return;
+        }
+        //console.info(`Searching Name: ${searchBoxValue} Connector: ${selectedConnector.name} Language: ${selectedLanguage}`);
+        if(isValidUri(searchBoxValue) && !selectedConnector.BaseUris.find((uri: string) => {
+                const match = searchBoxValue.match(pattern);
+                if(match === null)
+                    return false;
+                return match[1] == uri
+            }))
+        {
+            console.error("URL in Searchbox detected, but does not match selected connector.");
+            return;
+        }
+        if(!isValidUri(searchBoxValue)){
+            MangaConnector.GetMangaFromConnectorByTitle(selectedConnector, searchBoxValue)
+                .then((mangas: IManga[]) => {
+                    setSearchResults(mangas);
+                });
+        }else{
+            MangaConnector.GetMangaFromConnectorByUrl(selectedConnector, searchBoxValue)
+                .then((manga: IManga) => {
+                    setSearchResults([manga]);
+                });
+        }
+    }
+
+    const changeSelectedLanguage : ChangeEventHandler<HTMLSelectElement> = (event) => setSelectedLanguage(event.target.value);
+
+    return (<div id="Search">
+        <div id="SearchBox">
+            <input type="text" placeholder="Manganame" id="Searchbox-Manganame" onKeyDown={(e) => {if(e.key == "Enter") ExecuteSearch(null);}} onChange={searchBoxValueChanged}></input>
+            <select id="Searchbox-Connector" value={selectedConnector === undefined ? "" : selectedConnector.name} onChange={selectedConnectorChanged}>
+                <option value="" disabled hidden>Select</option>
+                {mangaConnectors === undefined
+                    ? <option value="Loading">Loading</option>
+                    : mangaConnectors.map(con => <option value={con.name} key={con.name}>{con.name}</option>)}
+            </select>
+            <select id="Searchbox-language" onChange={changeSelectedLanguage} value={selectedLanguage === null ? "" : selectedLanguage}>
+                {selectedConnector === undefined
+                    ? <option value="" disabled hidden>Select Connector</option>
+                    : selectedConnector.SupportedLanguages.map(language => <option value={language} key={language}>{language}</option>)}
+            </select>
+            <button id="Searchbox-button" type="submit" onClick={ExecuteSearch}>Search</button>
+        </div>
+        <img alt="Close Search" id="closeSearch" src="../media/close-x.svg" onClick={closeSearch} />
+        <div id="SearchResults">
+            {searchResults === undefined
+                ? <p></p>
+                : searchResults.map(result => SearchResult(apiUri, result, jobInterval, onJobsChanged))}
+        </div>
+    </div>)
+}
\ No newline at end of file
diff --git a/Website/modules/Settings.tsx b/Website/modules/Settings.tsx
new file mode 100644
index 0000000..6c16302
--- /dev/null
+++ b/Website/modules/Settings.tsx
@@ -0,0 +1,318 @@
+import React, {ChangeEventHandler, KeyboardEventHandler, MouseEventHandler, useEffect, useState} from 'react';
+import IFrontendSettings from "./interfaces/IFrontendSettings";
+import '../styles/settings.css';
+import IBackendSettings from "./interfaces/IBackendSettings";
+import {getData, postData} from "../App";
+import LibraryConnector, {Kavita, Komga} from "./LibraryConnector";
+import NotificationConnector, {Gotify, Lunasea, Ntfy} from "./NotificationConnector";
+import ILibraryConnector from "./interfaces/ILibraryConnector";
+import INotificationConnector from "./interfaces/INotificationConnector";
+import Toggle from 'react-toggle';
+import '../styles/react-toggle.css';
+
+export default function Settings({backendConnected, apiUri, settings, changeSettings} : {backendConnected: boolean, apiUri: string, settings: IFrontendSettings, changeSettings: (settings: IFrontendSettings) => void}) {
+    const [frontendSettings, setFrontendSettings] = useState<IFrontendSettings>(settings);
+    const [backendSettings, setBackendSettings] = useState<IBackendSettings>();
+    const [showSettings, setShowSettings] = useState<boolean>(false);
+    const [libraryConnectors, setLibraryConnectors] = useState<ILibraryConnector[]>();
+    const [notificationConnectors, setNotificationConnectors] = useState<INotificationConnector[]>();
+    const [komgaSettings, setKomgaSettings] = useState<{url: string, username: string, password: string}>({url: "", username: "", password: ""});
+    const [kavitaSettings, setKavitaSettings] = useState<{url: string, username: string, password: string}>({url: "", username: "", password: ""});
+    const [gotifySettings, setGotifySettings] = useState<{url: string, appToken: string}>({url: "", appToken: ""});
+    const [lunaseaSettings, setLunaseaSettings] = useState<{webhook: string}>({webhook: ""});
+    const [ntfySettings, setNtfySettings] = useState<{url: string, username: string, password: string, topic: string | undefined}>({url: "", username: "", password: "", topic: undefined});
+
+    useEffect(() => {
+        console.debug(`${showSettings ? "Showing" : "Not showing"} settings.`);
+        if(!showSettings || !backendConnected)
+            return;
+        UpdateBackendSettings();
+        LibraryConnector.GetLibraryConnectors(apiUri).then(setLibraryConnectors).catch(console.error);
+        NotificationConnector.GetNotificationConnectors(apiUri).then(setNotificationConnectors).catch(console.error);
+    }, [showSettings]);
+
+    useEffect(() => {
+        changeSettings(frontendSettings);
+    }, [frontendSettings]);
+
+    async function GetSettings(apiUri: string) : Promise<IBackendSettings> {
+        //console.info("Getting Settings");
+        return getData(`${apiUri}/v2/Settings`)
+            .then((json) => {
+                //console.info("Got Settings");
+                const ret = json as IBackendSettings;
+                //console.debug(ret);
+                return (ret);
+            })
+            .catch(Promise.reject);
+    }
+
+    function UpdateBackendSettings() {
+        GetSettings(apiUri).then(setBackendSettings).catch(console.error);
+    }
+
+    const GetKomga = () : ILibraryConnector | undefined =>
+        libraryConnectors?.find(con => con.libraryType == 0);
+
+    const KomgaConnected = () : boolean => GetKomga() != undefined;
+
+    const GetKavita = () : ILibraryConnector | undefined =>
+        libraryConnectors?.find(con => con.libraryType == 1);
+
+    const KavitaConnected = () : boolean => GetKavita() != undefined;
+
+    const GetGotify = () : INotificationConnector | undefined =>
+        notificationConnectors?.find(con => con.notificationConnectorType == 0);
+
+    const GotifyConnected = () : boolean => GetGotify() != undefined;
+
+    const GetLunasea = () : INotificationConnector | undefined =>
+        notificationConnectors?.find(con => con.notificationConnectorType == 1);
+
+    const LunaseaConnected = () : boolean => GetLunasea() != undefined;
+
+    const GetNtfy = () : INotificationConnector | undefined =>
+        notificationConnectors?.find(con => con.notificationConnectorType == 2);
+
+    const NtfyConnected = () : boolean => GetNtfy() != undefined;
+
+    const SubmitApiUri : KeyboardEventHandler<HTMLInputElement> = (e) => {
+        if(e.currentTarget.value.length < 1)
+            return;
+        if(e.key == "Enter"){
+            setFrontendSettings({...frontendSettings, apiUri: e.currentTarget.value});
+            RefreshInputs();
+        }
+    }
+
+    const SubmitUserAgent: KeyboardEventHandler<HTMLInputElement> = (e) => {
+        if(e.currentTarget.value.length < 1 || backendSettings === undefined)
+            return;
+        if(e.key == "Enter"){
+            //console.info(`Updating Useragent ${e.currentTarget.value}`);
+            postData(`${apiUri}/v2/Settings/UserAgent`, {value: e.currentTarget.value})
+                .then((json) => {
+                    //console.info(`Successfully updated Useragent ${e.currentTarget.value}`);
+                    UpdateBackendSettings();
+                    RefreshInputs();
+                })
+                .catch(() => alert("Failed to update Useragent."));
+        }
+    }
+
+    const ResetUserAgent: MouseEventHandler<HTMLSpanElement>  = () => {
+        //console.info(`Resetting Useragent`);
+        postData(`${apiUri}/v2/Settings/UserAgent`, {value: undefined})
+            .then((json) => {
+                //console.info(`Successfully reset Useragent`);
+                UpdateBackendSettings();
+                RefreshInputs();
+            })
+            .catch(() => alert("Failed to update Useragent."));
+    }
+
+    const SetAprilFoolsMode : ChangeEventHandler<HTMLInputElement> = (e) => {
+        //console.info(`Updating AprilFoolsMode ${e.target.checked}`);
+        postData(`${apiUri}/v2/Settings/AprilFoolsMode`, {value: e.target.checked})
+            .then((json) => {
+                //console.info(`Successfully updated AprilFoolsMode ${e.currentTarget.value}`);
+                UpdateBackendSettings();
+            })
+    }
+
+    const SetCompressImages : MouseEventHandler<HTMLInputElement> = (e) => {
+        //console.info(`Updating ImageCompression ${e.currentTarget.value}`);
+        postData(`${apiUri}/v2/Settings/CompressImages`, {value: e.currentTarget.value})
+            .then((json) => {
+                //console.info(`Successfully updated ImageCompression ${e.currentTarget.value}`);
+                UpdateBackendSettings();
+            })
+    }
+
+    const SetBWImages : ChangeEventHandler<HTMLInputElement> = (e) => {
+        //console.info(`Updating B/W Images ${e.target.checked}`);
+        postData(`${apiUri}/v2/Settings/BWImages`, {value: e.target.checked})
+            .then((json) => {
+                //console.info(`Successfully updated B/W Images ${e.target.checked}`);
+                UpdateBackendSettings();
+            })
+    }
+
+    function RefreshInputs(){
+        alert("Saved.");
+        setShowSettings(false);
+    }
+
+    return (
+        <div id="Settings">
+            <img id="SettingsIcon" src="../media/settings-cogwheel.svg" alt="cogwheel" onClick={() => setShowSettings(true)}/>
+            {showSettings
+                ? <div className="popup">
+                    <div className="popupHeader">
+                        <h1>Settings</h1>
+                        <img alt="Close Settings" className="close" src="../media/close-x.svg" onClick={() => setShowSettings(false)}/>
+                    </div>
+                    <div id="settingsPopupBody" className="popupBody">
+                        <div className="settings-section">
+                            TRANGA
+                            <div className="settings-section-content">
+                                <div className="section-item">
+                                    <span className="settings-section-title">API Settings</span>
+                                    <label className="tooltip" data-tooltip="Set the URI of the Backend. Include https:// and if necessary port." htmlFor="settingApiUri">API URI:</label>
+                                    <input placeholder={frontendSettings.apiUri} type="text" id="settingApiUri"
+                                           onKeyDown={SubmitApiUri}/>
+                                    <label htmlFor="userAgent" className="tooltip" data-tooltip="Set a custom User-Agent. This will allow more frequent requests to sites.">User Agent:</label>
+                                    <input id="userAgent" type="text"
+                                           placeholder={backendSettings != undefined ? backendSettings.userAgent : "UserAgent"}
+                                           onKeyDown={SubmitUserAgent}/>
+                                    <span id="resetUserAgent" onClick={ResetUserAgent}>Reset</span>
+                                    <label htmlFor="aprilFoolsMode" className="tooltip" data-tooltip="Disable all downloads on April 1st">April Fools Mode</label>
+                                    <Toggle id="aprilFoolsMode"
+                                            checked={backendSettings?.aprilFoolsMode ?? false}
+                                            onChange={SetAprilFoolsMode}/>
+                                    <label htmlFor="compression" className="tooltip" data-tooltip="JPEG Compression Quality">Image Compression</label>
+                                    <input type="range" min="1" max="100" defaultValue={backendSettings?.compression ?? 50} className="slider" id="compression" onMouseUp={SetCompressImages}/>
+                                    <label htmlFor="bwImages">B/W Images</label>
+                                    <Toggle id="bwImages"
+                                            checked={backendSettings?.bwImages ?? false}
+                                            onChange={SetBWImages}/>
+                                </div>
+                                <div className="section-item">
+                                <span className="settings-section-title">Rate Limits</span>
+                                    <label htmlFor="DefaultRL">Default:</label>
+                                    <input id="defaultRL" type="text" placeholder={backendSettings != undefined ? backendSettings.requestLimits.Default.toString() : "-1"} />
+                                    <label htmlFor="CoverRL">Manga Covers:</label>
+                                    <input id="coverRL" type="text" placeholder={backendSettings != undefined ? backendSettings.requestLimits.MangaCover.toString() : "-1"} />
+                                    <label htmlFor="ImageRL">Manga Images:</label>
+                                    <input id="imageRL" type="text" placeholder={backendSettings != undefined ? backendSettings.requestLimits.MangaImage.toString() : "-1"} />
+                                    <label htmlFor="InfoRL">Manga Info:</label>
+                                    <input id="infoRL" type="text" placeholder={backendSettings != undefined ? backendSettings.requestLimits.MangaInfo.toString() : "-1"} />
+                                </div>
+                                <div className="section-item">
+                                    <span className="settings-section-title">Appearance</span>
+                                    <label htmlFor="cssStyle">Library Style:</label>
+                                    <select id="cssStyle">
+                                        <option id="card_compact" value="card_compact">Cards (Compact)</option>
+                                        <option id="card_hover" value="card_hover">Cards (Hover)</option>
+                                    </select>
+                                </div>
+                            </div>
+                        </div>
+
+                        <div className="settings-section">
+                            <span className="settings-section-title">Sources</span>
+                            <div className="settings-section-content">
+                                <div className="section-item">
+                                    <span className="settings-section-title">
+                                        <img src="../media/connector-icons/mangadex-logo.svg" alt="Mangadex Logo" />
+                                        <a href="https://mangadex.org">MangaDex</a>
+                                    </span>
+                                    <label htmlFor="mDexFeedRL">Feed Rate Limit:</label>
+                                    <input id="mDexFeedRL" type="text" placeholder={backendSettings != undefined ? backendSettings.requestLimits.MangaDexFeed.toString() : "-1"} />
+                                    <label htmlFor="mDexImageRL">Image Rate Limit:</label>
+                                    <input id="mDexImageRL" type="number" placeholder={backendSettings != undefined ? backendSettings.requestLimits.MangaDexImage.toString() : "-1"} />
+                                </div>
+                            </div>
+                        </div>
+
+                        <div className="settings-section" >
+                        LIBRARY CONNECTORS
+                            <div className="settings-section-content">
+                                <div className="section-item" connector-status={KomgaConnected() ? "Configured" : "Not Configured"}>
+                                    <span className="settings-section-title">
+                                        <img src='../media/connector-icons/komga.svg' alt="Komga Logo"/>
+                                        Komga
+                                    </span>
+                                    <label htmlFor="komgaUrl">URL</label>
+                                    <input placeholder={GetKomga()?.baseUrl ?? "URL"} id="komgaUrl" type="text" onChange={(e) => setKomgaSettings(s => ({...s, url: e.target.value}))} />
+                                    <label htmlFor="komgaUsername">Username</label>
+                                    <input placeholder={KomgaConnected() ?  "***" : "Username"} id="komgaUsername" type="text" onChange={(e) => setKomgaSettings(s => ({...s, username: e.target.value}))} />
+                                    <label htmlFor="komgaPassword">Password</label>
+                                    <input placeholder={KomgaConnected() ? "***" : "Password"} id="komgaPassword" type="password" onChange={(e) => setKomgaSettings(s => ({...s, password: e.target.value}))} />
+                                    <div className="section-actions">
+                                        <span onClick={() => new Komga(komgaSettings).Test(apiUri).then(()=>alert("Test successful"))}>Test</span>
+                                        <span onClick={() => new Komga(komgaSettings).Reset(apiUri).then(RefreshInputs)}>Reset</span>
+                                        <span onClick={() => new Komga(komgaSettings).Create(apiUri).then(RefreshInputs)}>Apply</span>
+                                    </div>
+                                </div>
+                                <div className="section-item" connector-status={KavitaConnected() ? "Configured" : "Not Configured" }>
+                                    <span className="settings-section-title">
+                                        <img src='../media/connector-icons/kavita.png' alt="Kavita Logo"/>
+                                        Kavita
+                                    </span>
+                                    <label htmlFor="kavitaUrl">URL</label>
+                                    <input placeholder={GetKavita()?.baseUrl ?? "URL"} id="kavitaUrl" type="text" onChange={(e) => setKavitaSettings(s => ({...s, url: e.target.value}))} />
+                                    <label htmlFor="kavitaUsername">Username</label>
+                                    <input placeholder={KavitaConnected() ? "***" : "Username"} id="kavitaUsername" type="text" onChange={(e) => setKavitaSettings(s => ({...s, username: e.target.value}))} />
+                                    <label htmlFor="kavitaPassword">Password</label>
+                                    <input placeholder={KavitaConnected() ? "***" : "Password"} id="kavitaPassword" type="password" onChange={(e) => setKavitaSettings(s => ({...s, password: e.target.value}))} />
+                                    <div className="section-actions">
+                                        <span onClick={() => new Kavita(kavitaSettings).Test(apiUri).then(()=>alert("Test successful"))}>Test</span>
+                                        <span onClick={() => new Kavita(kavitaSettings).Reset(apiUri).then(RefreshInputs)}>Reset</span>
+                                        <span onClick={() => new Kavita(kavitaSettings).Create(apiUri).then(RefreshInputs)}>Apply</span>
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+
+                        <div className="settings-section">
+                            NOTIFICATION CONNECTORS
+                            <div className="settings-section-content">
+                            <div className="section-item" connector-status={GotifyConnected() ? "Configured" : "Not Configured"}>
+                                    <span className="settings-section-title">
+                                        <img src='../media/connector-icons/gotify-logo.png' alt="Gotify Logo"/>
+                                        Gotify
+                                    </span>
+                                    <label htmlFor="gotifyUrl">URL</label>
+                                    <input placeholder={GetGotify()?.endpoint ?? "URL"} id="gotifyUrl" type="text" onChange={(e) => setGotifySettings(s => ({...s, url: e.target.value}))} />
+                                    <label htmlFor="gotifyAppToken">AppToken</label>
+                                    <input placeholder={GotifyConnected() ? "***" : "AppToken"} id="gotifyAppToken" type="text" onChange={(e) => setGotifySettings(s => ({...s, appToken: e.target.value}))} />
+                                    <div className="section-actions">
+                                        <span onClick={() => new Gotify(gotifySettings).Test(apiUri).then(()=>alert("Test successful"))}>Test</span>
+                                        <span onClick={() => new Gotify(gotifySettings).Reset(apiUri).then(RefreshInputs)}>Reset</span>
+                                        <span onClick={() => new Gotify(gotifySettings).Create(apiUri).then(RefreshInputs)}>Apply</span>
+                                    </div>
+                            </div>
+                                <div className="section-item"
+                                     connector-status={LunaseaConnected() ? "Configured" : "Not Configured"}>
+                                    <span className="settings-section-title">
+                                        <img src='../media/connector-icons/lunasea.png' alt="Lunasea Logo"/>
+                                        LunaSea
+                                    </span>
+                                    <label htmlFor="lunaseaWebhook">Webhook id</label>
+                                    <input placeholder={GetLunasea() != undefined ? "***" : "device/:id or user/:id"} id="lunaseaWebhook" type="text" onChange={(e) => setLunaseaSettings(s => ({...s, webhook: e.target.value}))} />
+                                    <div className="section-actions">
+                                        <span onClick={() => new Lunasea(lunaseaSettings).Test(apiUri).then(()=>alert("Test successful"))}>Test</span>
+                                        <span onClick={() => new Lunasea(lunaseaSettings).Reset(apiUri).then(RefreshInputs)}>Reset</span>
+                                        <span onClick={() => new Lunasea(lunaseaSettings).Create(apiUri).then(RefreshInputs)}>Apply</span>
+                                    </div>
+                                </div>
+                                <div className="section-item"
+                                     connector-status={NtfyConnected() ? "Configured" : "Not Configured"}>
+                                    <span className="settings-section-title">
+                                        <img src='../media/connector-icons/ntfy.svg' alt="ntfy Logo"/>
+                                        Ntfy
+                                    </span>
+                                    <label htmlFor="ntfyEndpoint">URL</label>
+                                    <input placeholder={GetNtfy()?.endpoint ?? "URL"} id="ntfyEndpoint" type="text" onChange={(e) => setNtfySettings(s => ({...s, url: e.target.value}))} />
+                                    <label htmlFor="ntfyUsername">Username</label>
+                                    <input placeholder={NtfyConnected() ? "***" : "Username"} id="ntfyUsername" type="text" onChange={(e) => setNtfySettings(s => ({...s, username: e.target.value}))} />
+                                    <label htmlFor="ntfyPassword">Password</label>
+                                    <input placeholder={NtfyConnected() ? "***" : "Password"} id="ntfyPassword" type="password" onChange={(e) => setNtfySettings(s => ({...s, password: e.target.value}))} />
+                                    <label htmlFor="ntfyTopic">Topic</label>
+                                    <input placeholder={GetNtfy()?.topic ?? "Topic"} id="ntfyTopic" type="text" onChange={(e) => setNtfySettings(s => ({...s, topic: e.target.value}))} />
+                                    <div className="section-actions">
+                                        <span onClick={() => new Ntfy(ntfySettings).Test(apiUri).then(()=>alert("Test successful"))}>Test</span>
+                                        <span onClick={() => new Ntfy(ntfySettings).Reset(apiUri).then(RefreshInputs)}>Reset</span>
+                                        <span onClick={() => new Ntfy(ntfySettings).Create(apiUri).then(RefreshInputs)}>Apply</span>
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+                : <></>
+            }
+        </div>
+    );
+}
\ No newline at end of file
diff --git a/Website/modules/interfaces/IBackendSettings.tsx b/Website/modules/interfaces/IBackendSettings.tsx
new file mode 100644
index 0000000..a4edf2a
--- /dev/null
+++ b/Website/modules/interfaces/IBackendSettings.tsx
@@ -0,0 +1,20 @@
+export default interface IBackendSettings {
+    "downloadLocation": string;
+    "workingDirectory": string;
+    "apiPortNumber": number;
+    "userAgent": string;
+    "bufferLibraryUpdates": boolean;
+    "bufferNotifications": boolean;
+    "version": number;
+    "aprilFoolsMode": boolean;
+    "compression": number;
+    "bwImages": boolean;
+    "requestLimits": {
+        "MangaInfo": number;
+        "MangaDexFeed": number;
+        "MangaDexImage": number;
+        "MangaImage": number;
+        "MangaCover": number;
+        "Default": number
+    }
+}
\ No newline at end of file
diff --git a/Website/modules/interfaces/IChapter.tsx b/Website/modules/interfaces/IChapter.tsx
new file mode 100644
index 0000000..5d4eb41
--- /dev/null
+++ b/Website/modules/interfaces/IChapter.tsx
@@ -0,0 +1,10 @@
+import IManga from "./IManga";
+
+export default interface IChapter{
+    parentManga: IManga;
+    name: string | undefined;
+    volumeNumber: string;
+    chapterNumber: string;
+    url: string;
+    fileName: string;
+}
\ No newline at end of file
diff --git a/Website/modules/interfaces/IFrontendSettings.tsx b/Website/modules/interfaces/IFrontendSettings.tsx
new file mode 100644
index 0000000..1b56ab4
--- /dev/null
+++ b/Website/modules/interfaces/IFrontendSettings.tsx
@@ -0,0 +1,18 @@
+import {Cookies} from "react-cookie";
+
+export default interface IFrontendSettings {
+    jobInterval: Date;
+    apiUri: string;
+}
+
+export function LoadFrontendSettings(): IFrontendSettings {
+    const cookies = new Cookies();
+    return {
+        jobInterval: cookies.get('jobInterval') === undefined
+            ? new Date(0,0,0,3)
+            : cookies.get('jobInterval'),
+        apiUri: cookies.get('apiUri') === undefined
+            ? `${window.location.protocol}//${window.location.host}/api`
+            : cookies.get('apiUri')
+    }
+}
\ No newline at end of file
diff --git a/Website/modules/interfaces/IJob.tsx b/Website/modules/interfaces/IJob.tsx
new file mode 100644
index 0000000..85baf23
--- /dev/null
+++ b/Website/modules/interfaces/IJob.tsx
@@ -0,0 +1,28 @@
+import IMangaConnector from "./IMangaConnector";
+import IProgressToken from "./IProgressToken";
+import IChapter from "./IChapter";
+
+export default interface IJob{
+    progressToken: IProgressToken;
+    recurring: boolean;
+    recurrenceTime: string;
+    lastExecution: Date;
+    nextExecution: Date;
+    id: string;
+    jobType: number;
+    parentJobId: string | null;
+    mangaConnector: IMangaConnector;
+    mangaInternalId: string | undefined; //only on DownloadNewChapters
+    translatedLanguage: string | undefined; //only on DownloadNewChapters
+    chapter: IChapter | undefined; //only on DownloadChapter
+}
+
+export function JobTypeFromNumber(n: number): string {
+    switch(n) {
+        case 0: return "Download Chapter";
+        case 1: return "Download New Chapters";
+        case 2: return "Update Metadata";
+        case 3: return "Monitor";
+    }
+    return "";
+}
\ No newline at end of file
diff --git a/Website/modules/interfaces/ILibraryConnector.tsx b/Website/modules/interfaces/ILibraryConnector.tsx
new file mode 100644
index 0000000..c1520c6
--- /dev/null
+++ b/Website/modules/interfaces/ILibraryConnector.tsx
@@ -0,0 +1,13 @@
+export default interface ILibraryConnector {
+    libraryType: number;
+    baseUrl: string;
+    auth: string;
+}
+
+export function GetLibraryConnectorNameFromNumber(n: number): string {
+    switch(n){
+        case 0: return "Komga";
+        case 1: return "Kavita";
+    }
+    return "";
+}
\ No newline at end of file
diff --git a/Website/modules/interfaces/IManga.tsx b/Website/modules/interfaces/IManga.tsx
new file mode 100644
index 0000000..eba776c
--- /dev/null
+++ b/Website/modules/interfaces/IManga.tsx
@@ -0,0 +1,130 @@
+import IMangaConnector from "./IMangaConnector";
+import KeyValuePair from "./KeyValuePair";
+import Manga from "../Manga";
+import React, {EventHandler, ReactElement, ReactEventHandler} from "react";
+import Icon from '@mdi/react';
+import { mdiTagTextOutline, mdiAccountEdit } from '@mdi/js';
+import MarkdownPreview from '@uiw/react-markdown-preview';
+import IJob, {JobTypeFromNumber} from "./IJob";
+import Job from "../Job";
+import ProgressBar from "@ramonak/react-progress-bar";
+
+export default interface IManga{
+    "sortName": string,
+    "authors": string[],
+    "altTitles": KeyValuePair[],
+    "description": string,
+    "tags": string[],
+    "coverUrl": string,
+    "coverFileNameInCache": string,
+    "links": KeyValuePair[],
+    "year": number,
+    "originalLanguage": string,
+    "releaseStatus": number,
+    "folderName": string,
+    "publicationId": string,
+    "internalId": string,
+    "ignoreChaptersBelow": number,
+    "latestChapterDownloaded": number,
+    "latestChapterAvailable": number,
+    "websiteUrl": string,
+    "mangaConnector": IMangaConnector
+}
+
+export function ReleaseStatusFromNumber(n: number): string {
+    switch(n) {
+        case 0: return "Ongoing";
+        case 1: return "Completed";
+        case 2: return "OnHiatus";
+        case 3: return "Cancelled";
+        case 4: return "Unreleased";
+    }
+    return "";
+}
+
+
+
+export function CoverCard(apiUri: string, manga: IManga) : ReactElement {
+    const MangaCover : ReactEventHandler<HTMLImageElement> = (e) => {
+        if(e.currentTarget.src != Manga.GetMangaCoverUrl(apiUri, manga.internalId, e.currentTarget))
+            e.currentTarget.src = Manga.GetMangaCoverUrl(apiUri, manga.internalId, e.currentTarget);
+    }
+
+    return(
+        <div className="Manga" key={manga.internalId}>
+            <img src="../../media/blahaj.png" onLoad={MangaCover} alt="Manga Cover"></img>
+            <div>
+                <p className="pill connector-name">{manga.mangaConnector.name}</p>
+                <div className="Manga-status" release-status={ReleaseStatusFromNumber(manga.releaseStatus)}></div>
+                <p className="Manga-name">{manga.sortName}</p>
+            </div>
+        </div>);
+}
+
+export function SearchResult(apiUri: string, manga: IManga, interval: Date, onJobsChanged: (internalId: string) => void) : ReactElement {
+    const MangaCover : ReactEventHandler<HTMLImageElement> = (e) => {
+        if(e.currentTarget.src != Manga.GetMangaCoverUrl(apiUri, manga.internalId, e.currentTarget))
+            e.currentTarget.src = Manga.GetMangaCoverUrl(apiUri, manga.internalId, e.currentTarget);
+    }
+
+    return(
+        <div className="SearchResult" key={manga.internalId}>
+            <img src="../../media/blahaj.png" onLoad={MangaCover} alt="Manga Cover"></img>
+            <p className="connector-name">{manga.mangaConnector.name}</p>
+            <div className="Manga-status" release-status={ReleaseStatusFromNumber(manga.releaseStatus)}></div>
+            <p className="Manga-name"><a href={manga.websiteUrl}>{manga.sortName}<img src="../../media/link.svg"
+                                                                                      alt=""/></a></p>
+            <div className="Manga-tags">
+                {manga.authors.map(author => <p className="Manga-author" key={manga.internalId + "-author-" + author}>
+                    <Icon path={mdiAccountEdit} size={0.5}/> {author}</p>)}
+                {manga.tags.map(tag => <p className="Manga-tag" key={manga.internalId + "-tag-" + tag}><Icon
+                    path={mdiTagTextOutline} size={0.5}/> {tag}</p>)}
+            </div>
+            <MarkdownPreview className="Manga-description" source={manga.description}
+                             style={{backgroundColor: "transparent", color: "black"}}/>
+            <button className="Manga-AddButton" onClick={() => {
+                Job.CreateJobDateInterval(apiUri, manga.internalId, "MonitorManga", interval).then(() => onJobsChanged(manga.internalId));
+            }}>Monitor
+            </button>
+        </div>);
+}
+
+function ProgressbarStr(job: IJob): string {
+    return job.progressToken.timeRemaining.substring(0,job.progressToken.timeRemaining.indexOf(".")).concat(" ", ToPercentString(job.progressToken.progress));
+}
+
+function ToPercentString(n: number): string {
+    return n.toString().substring(2,4).concat("%");
+}
+
+export function QueueItem(apiUri: string, manga: IManga, job: IJob, triggerUpdate: () => void){
+    const MangaCover : ReactEventHandler<HTMLImageElement> = (e) => {
+        if(e.currentTarget.src != Manga.GetMangaCoverUrl(apiUri, manga.internalId, e.currentTarget))
+            e.currentTarget.src = Manga.GetMangaCoverUrl(apiUri, manga.internalId, e.currentTarget);
+    }
+
+    return (
+        <div className="QueueJob" key={"QueueJob-" + job.id}>
+            <img src="../../media/blahaj.png" onLoad={MangaCover} alt="Manga Cover"></img>
+            <p className="QueueJob-Name">{manga.sortName}</p>
+            <p className="QueueJob-JobType">{JobTypeFromNumber(job.jobType)}</p>
+            <p className="QueueJob-additional">{job.jobType == 0 ? `Vol.${job.chapter?.volumeNumber} Ch.${job.chapter?.chapterNumber}` : ""}</p>
+            {job.progressToken.state === 0
+                ? <ProgressBar labelColor={"#000"} height={"10px"} labelAlignment={"outside"}
+                               className="QueueJob-Progressbar" completed={job.progressToken.progress} maxCompleted={1}
+                               customLabel={ProgressbarStr(job)}/>
+                : <div className="QueueJob-Progressbar"></div>}
+            <div className="QueueJob-actions">
+                <button className="QueueJob-Cancel"
+                        onClick={() => Job.CancelJob(apiUri, job.id).then(triggerUpdate)}>Cancel
+                </button>
+                {job.parentJobId != null
+                    ? <button className="QueueJob-Cancel"
+                              onClick={() => Job.CancelJob(apiUri, job.parentJobId!).then(triggerUpdate)}>Cancel all
+                        related</button>
+                    : <></>
+                }
+            </div>
+        </div>
+    );
+}
\ No newline at end of file
diff --git a/Website/modules/interfaces/IMangaConnector.tsx b/Website/modules/interfaces/IMangaConnector.tsx
new file mode 100644
index 0000000..94fd237
--- /dev/null
+++ b/Website/modules/interfaces/IMangaConnector.tsx
@@ -0,0 +1,5 @@
+export default interface IMangaConnector {
+    SupportedLanguages: string[];
+    name: string;
+    BaseUris: string[];
+}
\ No newline at end of file
diff --git a/Website/modules/interfaces/INotificationConnector.tsx b/Website/modules/interfaces/INotificationConnector.tsx
new file mode 100644
index 0000000..a3ff39d
--- /dev/null
+++ b/Website/modules/interfaces/INotificationConnector.tsx
@@ -0,0 +1,8 @@
+export default interface INotificationConnector {
+    "notificationConnectorType": number; //see NotificationConnectorType
+    "endpoint": string | undefined;//only on Ntfy, Gotify
+    "appToken": string | undefined;//only on Gotify
+    "auth": string | undefined;//only on Ntfy
+    "topic": string | undefined;//only on Ntfy
+    "id": string | undefined;//only on LunaSea
+}
\ No newline at end of file
diff --git a/Website/modules/interfaces/IProgressToken.tsx b/Website/modules/interfaces/IProgressToken.tsx
new file mode 100644
index 0000000..f33a507
--- /dev/null
+++ b/Website/modules/interfaces/IProgressToken.tsx
@@ -0,0 +1,21 @@
+export default interface IProgressToken{
+    cancellationRequested: boolean;
+    increments: number;
+    incrementsCompleted: number;
+    progress: number;
+    lastUpdate: Date;
+    executionStarted: Date;
+    timeRemaining: string;
+    state: number;
+}
+
+export function GetProgressStateFromNumber(n: number): string {
+    switch (n){
+        case 0: return "Running";
+        case 1: return "Complete";
+        case 2: return "Standby";
+        case 3: return "Cancelled";
+        case 4: return "Waiting";
+    }
+    return "";
+}
\ No newline at end of file
diff --git a/Website/modules/interfaces/KeyValuePair.tsx b/Website/modules/interfaces/KeyValuePair.tsx
new file mode 100644
index 0000000..165ba57
--- /dev/null
+++ b/Website/modules/interfaces/KeyValuePair.tsx
@@ -0,0 +1,4 @@
+export default interface KeyValuePair {
+    key: string;
+    value: string;
+}
\ No newline at end of file
diff --git a/Website/styles/card_compact.css b/Website/styles/MangaCoverCard.css
similarity index 59%
rename from Website/styles/card_compact.css
rename to Website/styles/MangaCoverCard.css
index 74a6648..bdbe35e 100644
--- a/Website/styles/card_compact.css
+++ b/Website/styles/MangaCoverCard.css
@@ -1,24 +1,3 @@
-#addPublication {
-    cursor: pointer;
-    background-color: var(--secondary-color);
-    width: 180px;
-    height: 300px;
-    border-radius: 5px;
-    margin: 10px 10px;
-    padding: 15px 20px;
-    position: relative;
-}
-
-#addPublication p{
-    width: 100%;
-    text-align: center;
-    font-size: 150pt;
-    vertical-align: middle;
-    line-height: 300px;
-    margin: 0;
-    color: var(--accent-color);
-}
-
 .pill {
     flex-grow: 0;
     height: 14pt;
@@ -27,64 +6,40 @@
     background-color: var(--primary-color);
     padding: 2pt 17px;
     color: black;
+    width: fit-content;
+    margin: 10px 0;
 }
 
-publication{
+.Manga{
     cursor: pointer;
     background-color: var(--secondary-color);
     width: 180px;
     height: 300px;
     border-radius: 5px;
     margin: 10px 10px;
-    padding: 15px 19px;
+    padding: 14px 20px;
     position: relative;
     flex-shrink: 0;
 }
 
-publication::after{
+.Manga::after{
     content: '';
     position: absolute;
     left: 0; top: 0;
     border-radius: 5px;
     width: 100%; height: 100%;
     background: linear-gradient(rgba(0,0,0,0.8), rgba(0, 0, 0, 0.7),rgba(0, 0, 0, 0.2));
+    z-index: 0;
 }
 
-publication-information {
-    display: flex;
-    flex-direction: column;
-    justify-content: start;
-}
-
-publication-details {
-    display: flex;
-    flex-direction: column;
-    justify-content: start;
-}
-
-publication-information * {
-    z-index: 1;
-    color: var(--accent-color);
-}
-
-publication-details * {
-    z-index: 1;
-    color: var(--accent-color);
-}
-
-connector-name{
-    width: fit-content;
-    margin: 10px 0;
-}
-
-publication-name{
+.Manga-name{
     width: fit-content;
     font-size: 16pt;
     font-weight: bold;
     color: white;
 }
 
-publication-status {
+.Manga-status {
     display:block;
     height: 10px;
     width: 10px;
@@ -97,7 +52,7 @@ publication-status {
     box-shadow: rgba(0, 0, 0, 0.16) 0px 1px 10px, rgb(51, 51, 51) 0px 0px 10px 3px;
 }
 
-publication-status::after {
+.Manga-status::after {
     content: attr(release-status);
     position: absolute;
     top: 0;
@@ -118,42 +73,55 @@ publication-status::after {
     background-color: inherit;
 }
 
-publication-status:hover::after{
+.Manga-status:hover::after{
     visibility:visible;
 }
 
 
-publication-status[release-status="Ongoing"]{
+.Manga-status[release-status="Ongoing"]{
     background-color: limegreen;
 }
 
-publication-status[release-status="Completed"]{
+.Manga-status[release-status="Completed"]{
     background-color: blueviolet;
 }
 
-publication-status[release-status="On Hiatus"]{
+.Manga-status[release-status="On Hiatus"]{
     background-color: darkorange;
 }
 
-publication-status[release-status="Cancelled"]{
+.Manga-status[release-status="Cancelled"]{
     background-color: firebrick;
 }
 
-publication-status[release-status="Upcoming"]{
+.Manga-status[release-status="Upcoming"]{
     background-color: aqua;
 }
 
-publication-status[release-status="Status Unavailable"]{
+.Manga-status[release-status="Status Unavailable"]{
     background-color: gray;
 }
 
-publication img {
+.Manga img {
     position: absolute;
     top: 0;
     left: 0;
     width: 100%;
     height: 100%;
     object-fit: cover;
-    z-index: 0;
     border-radius: 5px;
+    z-index: 0;
+}
+
+.Manga p {
+    margin: 2px 0;
+}
+
+.Manga > div {
+    position: relative;
+    z-index: 1;
+    width: 100%;
+    height: 100%;
+    left: 0;
+    top: 0;
 }
\ No newline at end of file
diff --git a/Website/styles/MangaSearchResult.css b/Website/styles/MangaSearchResult.css
new file mode 100644
index 0000000..3b22ddd
--- /dev/null
+++ b/Website/styles/MangaSearchResult.css
@@ -0,0 +1,119 @@
+.SearchResult {
+    background-color: var(--second-background-color);
+    border-radius: 2px;
+    padding: 5px 5px 9px 5px;
+    position: relative;
+    max-width: 100%;
+    width: fit-content;
+    height: 328px;
+    display: grid;
+    grid-template-columns: 220px 600px 80px;
+    grid-template-rows: 55px 55px 190px auto;
+    column-gap: 10px;
+    grid-template-areas:
+    "cover header header"
+    "cover alltags alltags"
+    "cover description description"
+    "cover footer button";
+}
+
+.SearchResult p {
+    margin: 2px 0;
+}
+
+.SearchResult > img {
+    grid-area: cover;
+    position: relative;
+    height: 100%;
+    width: 100%;
+    z-index: 0;
+    border: 2px solid var(--primary-color);
+    border-radius: 4px;
+}
+
+.SearchResult > .connector-name {
+    grid-area: cover;
+    position: absolute;
+    z-index: 1;
+    left: 2px;
+    top: 2px;
+    border-top-left-radius: 4px;
+    border-top-right-radius: 4px;
+    width: 100%;
+    background-color: var(--accent-color);
+    margin: 0;
+    padding: 2px 0;
+    text-align: center;
+    color: var(--secondary-color);
+}
+
+.SearchResult > .Manga-status {
+    grid-area: header;
+}
+
+.SearchResult > .Manga-name {
+    grid-area: header;
+    color: black;
+    padding: 0 30px 0 0;
+}
+
+.SearchResult > .Manga-tags {
+    display: flex;
+    flex-direction: row;
+    flex-wrap: wrap;
+    grid-area: alltags;
+    color: white;
+    padding: 0;
+    margin: 0;
+    white-space: nowrap;
+}
+
+.SearchResult > .Manga-tags p {
+    margin: 0 2px;
+    padding: 5px;
+    font-size: 10pt;
+    height: fit-content;
+    width: min-content;
+}
+
+.SearchResult .Manga-author {
+    background-color: green;
+}
+
+.SearchResult .Manga-tag {
+    background-color: blue;
+}
+
+.SearchResult > .Manga-description {
+    grid-area: description;
+    color: black;
+    overflow-y: scroll;
+}
+
+.SearchResult > .Manga-AddButton {
+    grid-area: button;
+    background-color: white;
+    border: 1px solid var(--primary-color);
+    border-radius: 4px;
+    width: fit-content;
+    height: fit-content;
+    padding: 5px 10px;
+}
+
+.SearchResult > .Manga-AddButton:hover {
+    background-color: #eee;
+}
+
+.SearchResult a, .SearchResult a:visited {
+    color: initial;
+}
+
+.SearchResult a img {
+    filter: brightness(0) saturate(100%) invert(0%) sepia(0%) saturate(7480%) hue-rotate(141deg) brightness(111%) contrast(99%);
+    position: relative;
+    bottom: 7px;
+}
+
+.monitorMangaEntry {
+    position: relative;
+}
\ No newline at end of file
diff --git a/Website/styles/base.css b/Website/styles/base.css
deleted file mode 100644
index ee80fc5..0000000
--- a/Website/styles/base.css
+++ /dev/null
@@ -1,960 +0,0 @@
-:root{
-    --background-color: #030304;
-    --second-background-color: white;
-    --primary-color: #f5a9b8;
-    --secondary-color:  #5bcefa;
-    --blur-background: rgba(245, 169, 184, 0.58);
-    --accent-color: #fff;
-    /* --primary-color: green;
-    --secondary-color: gold;
-    --blur-background: rgba(86, 131, 36, 0.8);
-    --accent-color: olive; */
-    --topbar-height: 60px;
-    box-sizing: border-box;
-}
-
-body{
-    padding: 0;
-    margin: 0;
-    height: 100vh;
-    background-color: var(--background-color);
-    font-family: "Inter", sans-serif;
-    overflow-x: hidden;
-}
-
-wrapper {
-    display: flex;
-    flex-flow: column;
-    flex-wrap: nowrap;
-    height: 100vh;
-}
-
-background-placeholder{
-    background-color: var(--second-background-color);
-    opacity: 1;
-    position: absolute;
-    width: 100%;
-    height: 100%;
-    border-radius: 0 0 5px 0;
-    z-index: -1;
-}
-
-topbar {
-    display: flex;
-    align-items: center;
-    height: var(--topbar-height);
-    background-color: var(--secondary-color);
-    z-index: 100;
-    box-shadow: 0 0 20px black;
-}
-
-titlebox {
-    position: relative;
-    display: flex;
-    margin: 0 0 0 40px;
-    height: 100%;
-    align-items:center;
-    justify-content:center;
-}
-
-titlebox span{
-    cursor: default;
-    font-size: 24pt;
-    font-weight: bold;
-    background: linear-gradient(150deg, var(--primary-color), var(--accent-color));
-    -webkit-background-clip: text;
-    -webkit-text-fill-color: transparent;
-    margin-left: 20px;
-}
-
-titlebox img {
-    height: 100%;
-    cursor: grab;
-}
-
-spacer{
-    flex-grow: 1;
-}
-
-filter-box {
-    display: none;
-    align-self: center;
-    flex-direction: column;
-    position: relative;
-
-    margin: 10px;
-    background-color: var(--second-background-color);
-    border-style: solid;
-    border-color: var(--primary-color);
-    border-width: 2px;
-    border-radius: 15px;
-    min-width: 300px;
-    width: 50%;
-    overflow: hidden;
-    max-height: 50%;
-    height: 600px;
-}
-
-filter-box.animate {
-    display: flex;
-}
-
-filter-box border-bar popup-title{
-    font-size: 12pt;
-}
-
-filter-box border-bar popup-close {
-    height: 20px;
-    width: 20px;
-    font-size: 12pt;
-    -webkit-user-select: none; /* Safari */
-    -ms-user-select: none; /* IE 10 and IE 11 */
-    user-select: none; /* Standard syntax */
-}
-
-border-bar-button.clearFilter{
-    font-weight: bold;
-    margin: 0px 10px 10px 10px;
-    border-color: lightgray;
-    color: gray;
-    align-content: center;
-    justify-content: center;
-}
-
-border-bar-button.clearFilter:hover {
-    background-color: red;
-    border-color: var(--second-background-color);
-    color: var(--second-background-color);
-}
-
-status-filter {
-    display: block;
-    margin: 10px;
-
-    /*Text Properties*/
-    font-size:10pt;
-    font-weight:bold;
-    color:white;
-    text-align: center;
-
-    /*Size*/
-    padding: 3px 8px;
-    border-radius: 6px;
-    border: 0px;
-    background-color: inherit;
-
-    cursor: pointer;
-    -webkit-user-select: none; /* Safari */
-    -ms-user-select: none; /* IE 10 and IE 11 */
-    user-select: none; /* Standard syntax */
-}
-
-status-filter[release-status="Ongoing"]{
-    background-color: limegreen;
-}
-
-status-filter[release-status="Completed"]{
-    background-color: blueviolet;
-}
-
-status-filter[release-status="On Hiatus"]{
-    background-color: darkorange;
-}
-
-status-filter[release-status="Cancelled"]{
-    background-color: firebrick;
-}
-
-status-filter[release-status="Upcoming"]{
-    background-color: aqua;
-}
-
-status-filter[release-status="Status Unavailable"]{
-    background-color: gray;
-}
-
-
-searchdiv{
-    display: flex;
-    width: 100%;
-}
-
-#searchbox {
-    display: flex;
-    padding: 3px 5px;
-    margin: 5px;
-    border-style: solid;
-    border-width: 2px;
-    border-radius: 10px;
-    font-size: 12pt;   
-    outline: none;
-    border-color: lightgray;
-    flex-grow: 1;
-    flex-shrink: 1;
-}
-
-#searchbox:focus {
-    border-color: var(--secondary-color);
-}
-
-.pill {
-    flex-grow: 0;
-    height: 14pt;
-    font-size: 12pt;
-    border-radius: 9pt;
-    background-color: var(--primary-color);
-    padding: 2pt 17px;
-    color: black;
-}
-
-#connectorFilterBox .pill {
-    margin: 10px;
-    cursor: pointer;
-    -webkit-user-select: none; /* Safari */
-    -ms-user-select: none; /* IE 10 and IE 11 */
-    user-select: none; /* Standard syntax */
-}
-
-#settingscog {
-    cursor: pointer;
-    margin: 0px 30px;
-    margin-left: 15px;
-    height: 50%;
-    filter: invert(100%) sepia(0%) saturate(7465%) hue-rotate(115deg) brightness(116%) contrast(101%);
-}
-
-#filterFunnel {
-    cursor: pointer;
-    margin: 0px 15px;
-    height: 50%;
-    filter: invert(100%) sepia(0%) saturate(7465%) hue-rotate(115deg) brightness(116%) contrast(101%);
-}
-
-viewport {
-    position: relative;
-    display: flex;
-    flex-flow: row;
-    flex-wrap: nowrap;
-    flex-grow: 1;
-    height: 100%;
-    overflow-y: scroll;
-    scrollbar-color: var(--accent-color) var(--primary-color);
-    scrollbar-width: thin;
-}
-
-footer {
-    display: flex;
-    flex-direction: row;
-    flex-wrap: nowrap;
-    width: 100%;
-    height: 40px;
-    align-items: center;
-    justify-content: center;
-    background-color: var(--primary-color);
-    align-content: center;
-}
-
-footer > div {
-    height: 100%;
-    margin: 0 30px;
-    display: flex;
-    flex-direction: row;
-    flex-wrap: nowrap;
-    align-items: center;
-    cursor: pointer;
-}
-
-footer > div > *{
-    height: 40%;
-    margin: 0 5px;
-}
-
-#madeWith {
-    flex-grow: 1;
-    text-align: right;
-    margin-right: 20px;
-    cursor: url("media/blahaj.png"), grab;
-}
-
-content {
-    position: relative;
-    flex-grow: 1;
-    border-radius: 5px;
-    display: flex;
-    flex-direction: row;
-    flex-wrap: wrap;
-    justify-content: start;
-    align-content: start;
-}
-
-#settingsPopup{
-    z-index: 300;
-}
-
-popup{
-    display: none;
-    width: 100%;
-    min-height: 100%;
-    top: 0;
-    left: 0;
-    position: fixed;
-    z-index: 2;
-    flex-direction: column;
-}
-
-border-bar {
-    display: flex;
-    flex-direction: row;
-    background-color: var(--primary-color);
-    color: var(--accent-color);
-    font-weight: bolder;
-    padding: 7px 5px;
-    margin:0;
-    align-items: center;
-    position: relative;
-    width: 100%;
-}
-
-popup-title {
-    font-size: 14pt;
-    display: flex;
-    margin-top: 3px;
-    margin-left: 5px;
-    color: var(--second-background-color);
-}
-
-popup-close {
-    border: none;
-    background-color: inherit;
-    color: var(--second-background-color);;
-    font-weight: inherit;
-    font-size: 27px;
-    font-family: 'Trebuchet MS', 'Lucida Sans Unicode', 'Lucida Grande', 'Lucida Sans', Arial, sans-serif;
-    display: flex;
-    cursor: pointer;
-    margin-left: auto;
-    margin-right: 15px;
-    height: 32px;
-    width: 32px;
-    border-radius: 16px;
-    align-content: center;
-    justify-content: center;
-}
-
-popup-close:hover {
-    background-color: var(--secondary-color);
-}
-
-border-bar > .button-container {
-    display: flex;
-    flex-direction: row;
-    align-items: center;
-    flex-wrap: wrap;
-    margin-right: 0;
-    margin-left: auto;
-}
-
-border-bar-button {
-    border-style: solid;
-    border-width: 2px;
-    background-color: inherit;
-    color: var(--second-background-color);
-    font-weight: inherit;
-    font-size: inherit;
-    font-family: inherit;
-    display: flex;
-    cursor: pointer;
-    margin: 0px 5px;
-    padding: 5px 20px;
-    border-radius: 20px;
-    height: 20px;
-    align-items: center;
-    border-color: var(--accent-color);
-    -webkit-user-select: none; /* Safari */
-    -ms-user-select: none; /* IE 10 and IE 11 */
-    user-select: none; /* Standard syntax */
-}
-
-border-bar-button:hover {
-    border-color: var(--secondary-color);
-}
-
-border-bar-button.primary {
-    background-color: var(--secondary-color);
-    color: var(--accent-color);
-    border-color: var(--primary-color);
-    margin-right: 10px;
-}
-
-border-bar-button.primary:hover {
-    border-color: var(--accent-color);
-}
-
-border-bar-button.section {
-    font-weight: bold;
-    color: darkgray;
-    border-color: darkgray;
-    text-align: center;
-    padding: 5px;
-    flex-grow: 1;
-    justify-content: center;
-}
-
-border-bar-button.section:hover {
-    color: var(--secondary-color);
-    border-color: var(--secondary-color);
-}
-
-
-popup popup-window {
-    position: absolute;
-    z-index: 3;
-    left: 10%;
-    top: 10%;
-    height: 80%;
-    width: 80%;
-    display: flex;
-    flex-direction: column;
-    background-color: var(--second-background-color);
-    border-radius: 15px;
-    overflow: hidden;
-}
-
-popup#jobStatusView popup-window {
-    left: 20%;
-    top: 20%;
-    height: 60%;
-    width: 60%;
-}
-
-popup-content{
-    display: flex;
-    flex-direction: column;
-    align-items: left;
-    height: calc(100% - 60px);
-    overflow-y: auto;
-    overflow-x: hidden;
-    scrollbar-width: thin;
-    scrollbar-color:  var(--secondary-color) var(--second-background-color);
-}
-
-popup-content > .popup-section {
-    margin: 5px;
-    margin-bottom: 10px;
-    font-size: 10pt;
-    font-weight: 100;
-    display: block;
-    border-top-style: solid;
-    border-top-width: 1px;
-    border-top-color: lightgray;
-    width: calc(100%-10px);
-    padding: 10px;
-}
-
-.section-content {
-    display: flex;
-    flex-direction: row;
-    width: 100%;
-    flex-wrap: wrap;
-}
-
-.section-item {
-    display: flex;
-    flex-direction: column;
-    width: 22%;
-    min-width: 300px;
-    height: auto;
-    border-radius: 10px;
-    border-style: solid;
-    border-width: 1px;
-    border-color: lightgray;
-    margin: 7px; 
-    padding: 5px;
-}
-
-.section-item.dyn-height {
-    height: fit-content;
-}
-
-.section-item > .title {
-    font-weight: bold;
-    vertical-align: bottom;
-    line-height: 32px;
-    font-size: 12pt;
-    width: 100%;
-}
-
-a:link {
-    color: inherit;
-    text-decoration: none;
-}
-
-a:visited {
-    color: inherit;
-    text-decoration: none;
-}
-
-a:hover {
-    color: inherit;
-    text-decoration: underline solid var(--secondary-color) 3px;
-}
-
-a:active {
-    color: inherit;
-    text-decoration: none;
-}
-
-.section-item > .title > img {
-    width: auto;
-    height: 32px;
-    margin: 5px;
-    vertical-align: middle;
-    border-radius: 5px;
-}
-
-.section-item > .title > connector-configured {
-    display:block;
-    height: 10px;
-    width: 10px;
-    border-radius: 50%;
-    margin: 5px;
-    float: right;
-    top: 5px;
-    right: 5px;
-}
-
-.section-item > .title > connector-configured::after {
-    display: block;
-    content: attr(configuration);
-    float: right;
-    width: max-content;
-    width: -webkit-max-content;
-    width: -mox-max-content;
-    width: intrinsic;
- 
-    visibility: hidden;
-
-    /*Text Properties*/
-    font-size:8pt;
-    font-weight:bold;
-    color:white;
-    text-align: right;
-
-    /*Size*/
-    padding: 0px 8px;
-    border-radius: 6px;
-    border: 0px;
-    background-color: inherit;
-}
-
-.section-item > .title > connector-configured:hover::after{
-    visibility:visible;
-} 
-
-.section-item > .title > connector-configured[configuration="Active"] {   
-    background-color: limegreen;
-}
-
-.section-item > .title > connector-configured[configuration="Not Configured"] {
-    background-color: gray;
-}
-
-.section-item > input {
-    margin: 2px; 
-    padding: 5px;
-    height: 20px;
-    border-radius: 10px;
-    border-style: solid;
-    outline: none;
-}
-.section-item > input:focus {
-    border-color: var(--secondary-color);
-}
-
-.section-item > row {
-    width: calc(100%-20px);
-    display: flex;
-    flex-direction: row;
-    align-items: center;
-    margin-left: 5px;
-    margin-bottom: 5px;
-}
-
-.section-item > row > input {
-    margin-left: auto;
-    margin-right: 2px; 
-    padding: 5px;
-    height: 20px;
-    border-radius: 10px;
-    border-style: solid;
-    outline: none;
-    flex-grow: 0;
-    text-align: end;
-    float: right;
-    width: 200px;
-}
-.section-item > row > input:focus {
-    border-color: var(--secondary-color);
-}
-
-.section-item > row > select {
-    margin-left: auto;
-    margin-right: 2px; 
-    padding: 2px;
-    height: 30px;
-    border-radius: 10px;
-    border-style: solid;
-    outline: none;
-    flex-grow: 0;
-    text-align: end;
-    float: right;
-    width: 200px;
-}
-
-.section-item > row > select:focus {
-    border-color: var(--secondary-color);
-}
-
-.section-buttons-container {
-    display: flex;
-    flex-direction: row;
-    align-items: center;
-    position: relative;
-    margin-left: auto;
-    margin-top: auto;
-    margin-bottom: 0;
-    margin-right: 0;
-}
-
-.section-buttons-container > .section-button {
-    font-size: 12px;
-    padding: 3px 10px;
-    margin: 3px;
-    border-radius: 5px;
-    border-style: solid;
-    border-width: 1px;
-    border-color: lightgray;
-    font-weight: bold;
-    color: gray;
-    cursor: pointer;
-    -webkit-user-select: none; /* Safari */
-    -ms-user-select: none; /* IE 10 and IE 11 */
-    user-select: none; /* Standard syntax */
-}
-
-.section-button#reset:hover {
-    color: red;
-    border-color: red;
-}
-.section-buttons-container > .section-button:hover {
-    border-color: var(--secondary-color);
-    color: var(--secondary-color);
-}
-
-#newMangaPopup > div {
-  z-index: 3;
-  position: relative;
-}
-
-#newMangaPopup > #newMangaPopupSelector {
-  width: 600px;
-  height: 40px;
-  margin: 80px auto 0;
-}
-
-#newMangaPopup > div > #newMangaConnector, #newMangaTitle, #newMangaTranslatedLanguage {
-  margin: 0;
-  display: inline-block;
-  height: 40px;
-}
-
-#newMangaPopup #newMangaConnector {
-  width: 100px;
-  padding: 0 0 0 5px;
-  border-radius: 5px 0 0 5px;
-  border: 0;
-  border-right: 1px solid darkgray;
-}
-
-#newMangaPopup #newMangaTitle{
-  width: 445px;
-  padding: 0 5px 0 5px;
-  border: 0;
-}
-
-#newMangaPopup #newMangaTranslatedLanguage {
-  width: 45px;
-  border-radius: 0 5px 5px 0;
-  border: 0;
-  border-left: 1px solid darkgray;
-  margin-left: -5px;
-}
-
-#newMangaResult {
-  display: none;
-  flex-direction: row;
-  justify-content: flex-start;
-  margin: 5px auto 0;
-  border-radius: 5px;
-  padding: 5px;
-  width: min-content;
-  max-width: 98%;
-  max-height: 400px;
-  overflow-x: scroll;
-  overflow-y: hidden;
-}
-
-blur-background {
-    width: 100%;
-    height: 100%;
-    position: absolute;
-    left: 0;
-    background: var(--blur-background);
-    box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);
-    backdrop-filter: blur(4.5px);
-    -webkit-backdrop-filter: blur(4.5px);
-}
-
-#publicationViewerPopup{
-    z-index: 5;
-}
-
-publication-viewer{
-    display: block;
-    width: 460px;
-    position: absolute;
-    top: 200px;
-    left: 400px;
-    background-color: var(--accent-color);
-    border-radius: 5px;
-    overflow: hidden;
-    padding: 15px;
-}
-
-publication-viewer::after{
-    content: '';
-    position: absolute;
-    left: 0; top: 0;
-    border-radius: 5px;
-    width: 100%;
-    height: 100%;
-    background: rgba(0,0,0,0.8);
-    backdrop-filter: blur(3px);
-}
-
-publication-viewer img {
-    position: absolute;
-    left: 0;
-    top: 0;
-    height: 100%;
-    width: 100%;
-    object-fit: cover;
-    border-radius: 5px;
-    z-index: 0;
-}
-
-publication-viewer publication-details > * {
-    margin: 5px 0;
-}
-
-publication-viewer publication-details publication-name {
-    width: initial;
-    overflow-x: scroll;
-    white-space: nowrap;
-    scrollbar-width: none;
-}
-
-publication-viewer publication-details publication-tags::before {
-    content: "Tags";
-    display: block;
-    font-weight: bolder;
-}
-
-publication-viewer publication-details publication-tags {
-    overflow-x: scroll;
-    white-space: nowrap;
-    scrollbar-width: none;
-}
-
-publication-viewer publication-details publication-author::before {
-    content: "Author: ";
-    font-weight: bolder;
-}
-
-publication-viewer publication-details publication-description::before {
-    content: "Description";
-    display: block;
-    font-weight: bolder;
-}
-
-publication-viewer publication-details publication-description {
-    font-size: 12pt;
-    margin: 5px 0;
-    height: 145px;
-    overflow-x: scroll;
-}
-
-publication-viewer publication-details publication-interactions {
-    display: flex;
-    flex-direction: row;
-    justify-content: end;
-    align-items: start;
-    width: 100%;
-}
-
-publication-viewer publication-details publication-interactions > * {
-    margin: 0 10px;
-    font-size: 16pt;
-    cursor: pointer;
-}
-
-publication-viewer publication-details publication-interactions publication-starttask {
-    color: var(--secondary-color);
-}
-
-publication-viewer publication-details publication-interactions publication-delete {
-    color: red;
-}
-
-publication-view publication-details publication-interactions publication-canceltask {
-  color: yellow;
-}
-
-publication-viewer publication-details publication-interactions publication-add {
-    color: limegreen;
-}
-
-footer-tag-popup {
-    display: none;
-    padding: 2px 4px;
-    position: fixed;
-    bottom: 58px;
-    left: 20px;
-    background-color: var(--second-background-color);
-    z-index: 8;
-    border-radius: 5px;
-    max-height: 400px;
-}
-
-footer-tag-content{
-    position: relative;
-    max-height: 400px;
-    display: flex;
-    flex-direction: column;
-    flex-wrap: nowrap;
-    overflow-y: scroll;
-}
-
-footer-tag-content > * {
-    margin: 2px 5px;
-}
-
-footer-tag-popup::before{
-    content: "";
-    width: 0;
-    height: 0;
-    position: absolute;
-    border-right: 10px solid var(--second-background-color);
-    border-left: 10px solid transparent;
-    border-top: 10px solid var(--second-background-color);
-    border-bottom: 10px solid transparent;
-    left: 0;
-    bottom: -17px;
-    border-radius: 0 0 0 5px;
-}
-
-#loaderdiv {
-  position: absolute;
-  top: 0px;
-  left: 0px;
-  width: 100%;
-  height: 100%;
-  z-index: 200;
-}
-
-#loader {
-  border: 16px solid transparent;
-  border-top: 16px solid var(--secondary-color);
-  border-bottom: 16px solid var(--primary-color);
-  border-radius: 50%;
-  width: 120px;
-  height: 120px;
-  animation: spin 2s linear infinite;
-  position: absolute;
-  left: calc(50% - 60px);
-  top: calc(50% - 120px);
-  z-index: 201;
-}
-
-#loaderText {
-  position: relative;
-  margin: 0 auto;
-  top: calc(50% + 80px);
-  z-index: 201;
-  text-align: center;
-  color: var(--second-background-color);
-  font-size: 20pt;
-}
-
-@keyframes spin {
-  0% { transform: rotate(0deg); }
-  100% { transform: rotate(360deg); }
-}
-
-#jobStatusRunning > .section-item {
-    flex-direction: row;
-    height: 150px;
-    padding: 0;
-    overflow: hidden;
-}
-
-#jobStatusWaiting > .section-item {
-    flex-direction: row;
-    height: 150px;
-    padding: 0;
-    overflow: hidden;
-}
-
-.section-item > .jobImage {
-  height: 100%;
-  width: auto;
-  left: 0;
-  top: 0;
-  border-radius: 10px;
-}
-
-.jobDetails {
-    display: flex;
-    flex-direction: column;
-    height: 100%;
-    width: 100%;
-}
-
-.section-item > .jobDetails > .jobTitle {
-  margin: 5px;
-  font-size: 11pt;
-  font-weight: bold;
-  text-wrap: wrap;
-}
-
-.section-item > .jobDetails > .jobProgressBar {
-  margin: 5px;
-  height: 10px;
-  border-radius: 7px;
-}
-
-.section-item > .jobDetails > .jobProgressSpan {
-    margin: 5px;
-    margin-left: auto;
-    margin-right: 5px;
-}
-
-.section-item > .jobDetails > .jobCancel {
-  margin-top: auto;
-  margin-bottom: 5px;
-  margin-left: auto;
-  margin-right: 5px;
-  font-size: 12pt;
-  color: var(--secondary-color);
-  cursor: pointer;
-}
\ No newline at end of file
diff --git a/Website/styles/card_hover.css b/Website/styles/card_hover.css
deleted file mode 100644
index 6d830ed..0000000
--- a/Website/styles/card_hover.css
+++ /dev/null
@@ -1,172 +0,0 @@
-#addPublication {
-    cursor: pointer;
-    background-color: var(--secondary-color);
-    width: 180px;
-    height: 300px;
-    border-radius: 5px;
-    margin: 10px 10px;
-    padding: 15px 20px;
-    position: relative;
-}
-
-#addPublication p{
-    width: 100%;
-    text-align: center;
-    font-size: 150pt;
-    vertical-align: middle;
-    line-height: 300px;
-    margin: 0;
-    color: var(--accent-color);
-}
-
-.pill {
-    flex-grow: 0;
-    height: 14pt;
-    font-size: 12pt;
-    border-radius: 9pt;
-    background-color: var(--primary-color);
-    padding: 2pt 17px;
-    color: black;
-}
-
-publication{
-    cursor: pointer;
-    background-color: var(--secondary-color);
-    width: 180px;
-    height: 300px;
-    border-radius: 5px;
-    margin: 10px 10px;
-    padding: 15px 19px;
-    position: relative;
-    flex-shrink: 0;
-}
-
-publication:hover {
-    background-color: black;
-}
-
-publication:hover::after{
-    background: linear-gradient(rgba(0,0,0,0.8), rgba(0, 0, 0, 0.7),rgba(0, 0, 0, 0.2));
-}
-
-publication:hover > publication-information {
-    display: flex;
-    opacity:1;
-}
-
-publication::after{
-    content: '';
-    position: absolute;
-    left: 0; top: 0;
-    border-radius: 5px;
-    width: 100%; height: 100%;
-    background: none;
-}
-
-publication-information {
-    display: none;
-    flex-direction: column;
-    justify-content: start;
-}
-
-publication-information * {
-    z-index: 1;
-    color: white;
-}
-
-connector-name{
-    width: fit-content;
-    margin: 10px 0;
-}
-
-publication-name{
-    width: fit-content;
-    font-size: 16pt;
-    font-weight: bold;
-}
-
-publication-status {
-    display:block;
-    height: 10px;
-    width: 10px;
-    border-radius: 50%;
-    margin: 5px;
-    position: absolute;
-    top: 5px;
-    right: 5px;
-    z-index: 2;
-    box-shadow: rgba(0, 0, 0, 0.16) 0px 1px 10px, rgb(51, 51, 51) 0px 0px 10px 3px;
-}
-
-publication-status::after {
-    content: attr(release-status);
-    position: absolute;
-    top: 0;
-    right: 0;
-
-    visibility: hidden;
-
-    /*Text Properties*/
-    font-size:10pt;
-    font-weight:bold;
-    color:white;
-    text-align: center;
-
-    /*Size*/
-    padding: 3px 8px;
-    border-radius: 6px;
-    border: 0px;
-    background-color: inherit;
-}
-
-publication-status:hover::after{
-    visibility:visible;
-}
-
-
-publication-status[release-status="Ongoing"]{
-    background-color: limegreen;
-}
-
-publication-status[release-status="Completed"]{
-    background-color: blueviolet;
-}
-
-publication-status[release-status="On Hiatus"]{
-    background-color: darkorange;
-}
-
-publication-status[release-status="Cancelled"]{
-    background-color: firebrick;
-}
-
-publication-status[release-status="Upcoming"]{
-    background-color: aqua;
-}
-
-publication-status[release-status="Status Unavailable"]{
-    background-color: gray;
-}
-
-
-publication-details {
-    display: flex;
-    flex-direction: column;
-    justify-content: start;
-}
-
-publication-details * {
-    z-index: 1;
-    color: var(--accent-color);
-}
-
-publication img {
-    position: absolute;
-    top: 0;
-    left: 0;
-    width: 100%;
-    height: 100%;
-    object-fit: cover;
-    z-index: 0;
-    border-radius: 5px;
-}
\ No newline at end of file
diff --git a/Website/styles/footer.css b/Website/styles/footer.css
new file mode 100644
index 0000000..54f5818
--- /dev/null
+++ b/Website/styles/footer.css
@@ -0,0 +1,42 @@
+footer {
+    display: flex;
+    flex-direction: row;
+    flex-wrap: nowrap;
+    width: 100%;
+    height: 40px;
+    align-items: center;
+    justify-content: center;
+    background-color: var(--primary-color);
+    align-content: center;
+    position: fixed;
+    bottom: 0;
+    color: white;
+    z-index: 10;
+}
+
+#madeWith {
+    flex-grow: 1;
+    text-align: right;
+    margin-right: 20px;
+    cursor: url("Website/media/blahaj.png"), grab;
+}
+
+footer .statusBadge {
+    margin: 0 10px;
+    display: flex;
+    align-items: center;
+    justify-items: center;
+    background-color: rgba(255,255,255, 0.3);
+    border-radius: 10px;
+    padding: 2px 5px;
+}
+
+footer > div {
+    display: flex;
+    align-items: center;
+    justify-items: center;
+}
+
+footer .hoverHand {
+    cursor: pointer;
+}
\ No newline at end of file
diff --git a/Website/styles/header.css b/Website/styles/header.css
new file mode 100644
index 0000000..3e5a26f
--- /dev/null
+++ b/Website/styles/header.css
@@ -0,0 +1,33 @@
+header {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    height: var(--topbar-height);
+    background-color: var(--secondary-color);
+    z-index: 100;
+    box-shadow: 0 0 20px black;
+}
+
+header > #titlebox {
+    position: relative;
+    display: flex;
+    margin: 0 0 0 40px;
+    height: 100%;
+    align-items:center;
+    justify-content:center;
+}
+
+header > #titlebox > span{
+    cursor: default;
+    font-size: 24pt;
+    font-weight: bold;
+    background: linear-gradient(150deg, var(--primary-color), var(--accent-color));
+    -webkit-background-clip: text;
+    -webkit-text-fill-color: transparent;
+    margin-left: 20px;
+}
+
+header > #titlebox > img {
+    height: 100%;
+    cursor: grab;
+}
\ No newline at end of file
diff --git a/Website/styles/index.css b/Website/styles/index.css
new file mode 100644
index 0000000..3e577b6
--- /dev/null
+++ b/Website/styles/index.css
@@ -0,0 +1,42 @@
+:root{
+    --background-color: #030304;
+    --second-background-color: white;
+    --primary-color: #f5a9b8;
+    --secondary-color:  #5bcefa;
+    --blur-background: rgba(245, 169, 184, 0.58);
+    --accent-color: #fff;
+    /* --primary-color: green;
+    --secondary-color: gold;
+    --blur-background: rgba(86, 131, 36, 0.8);
+    --accent-color: olive; */
+    --topbar-height: 60px;
+    box-sizing: border-box;
+}
+
+body{
+    padding: 0;
+    margin: 0;
+    height: 100vh;
+    background-color: var(--background-color);
+    font-family: "Inter", sans-serif;
+    overflow-x: hidden;
+    color: var(--primary-color);
+}
+
+.tooltip {
+    position: relative;
+}
+
+.tooltip:hover:before {
+    display: block;
+    content: attr(data-tooltip, "tooltip");
+    background-color: var(--second-background-color);
+    color: var(--secondary-color);
+    border: 1px solid var(--secondary-color);
+    border-radius: 6px;
+    bottom: 1em;
+    max-width: 90%;
+    position: absolute;
+    padding: 3px 7px 1px;
+    z-index: 999;
+}
\ No newline at end of file
diff --git a/Website/styles/monitorMangaList.css b/Website/styles/monitorMangaList.css
new file mode 100644
index 0000000..9343c51
--- /dev/null
+++ b/Website/styles/monitorMangaList.css
@@ -0,0 +1,39 @@
+#MonitorMangaList {
+    position: relative;
+    display: flex;
+    flex-flow: row;
+    flex-wrap: nowrap;
+    flex-grow: 1;
+    height: 100%;
+    overflow-y: scroll;
+    scrollbar-color: var(--accent-color) var(--primary-color);
+    scrollbar-width: thin;
+}
+
+.MangaActionButtons  {
+    position: absolute;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+}
+
+.MangaActionButtons > div {
+    position: absolute;
+    margin: 10px;
+    border: 0;
+    background: none;
+    cursor: pointer;
+}
+
+.DeleteJobButton {
+    bottom: 0;
+    left: 0;
+    filter: invert(21%) sepia(63%) saturate(7443%) hue-rotate(355deg) brightness(93%) contrast(118%);
+}
+
+.StartJobNowButton {
+    bottom: 0;
+    right: 0;
+    filter: invert(58%) sepia(16%) saturate(4393%) hue-rotate(103deg) brightness(102%) contrast(103%);
+}
\ No newline at end of file
diff --git a/Website/styles/popup.css b/Website/styles/popup.css
new file mode 100644
index 0000000..c41a4f8
--- /dev/null
+++ b/Website/styles/popup.css
@@ -0,0 +1,43 @@
+.popup {
+    position: fixed;
+    left: 10%;
+    top: 7.5%;
+    width: 80%;
+    height: 80%;
+    margin: auto;
+    z-index: 100;
+    background-color: var(--second-background-color);
+    border-radius: 10px;
+    overflow: hidden;
+}
+
+.popup .popupHeader {
+    position: absolute;
+    top: 0;
+    left: 0;
+    height: 40px;
+    width: 100%;
+    background-color: var(--primary-color);
+    color: var(--accent-color);
+}
+
+.popup .popupHeader h1 {
+    margin: 4px 10px;
+    font-size: 20pt;
+}
+
+.popup .close {
+    position: absolute;
+    top: 0;
+    right: 0;
+    height: 100%;
+    cursor: pointer;
+}
+
+.popup .popupBody {
+    position: absolute;
+    top: 40px;
+    left: 0;
+    width: 100%;
+    height: calc(100% - 40px);
+}
\ No newline at end of file
diff --git a/Website/styles/queuePopUp.css b/Website/styles/queuePopUp.css
new file mode 100644
index 0000000..f2abf91
--- /dev/null
+++ b/Website/styles/queuePopUp.css
@@ -0,0 +1,81 @@
+#QueuePopUp #QueuePopUpBody {
+    display: flex;
+}
+
+#QueuePopUp #QueuePopUpBody > * {
+    padding: 20px;
+    width: calc(50% - 40px);
+    height: calc(100% - 40px);
+    overflow-y: scroll;
+}
+
+#QueuePopUp #QueuePopUpBody h1 {
+    padding: 0;
+    margin: 0 0 5px 0;
+    color: var(--primary-color);
+}
+
+#QueuePopUp #QueuePopUpBody > *:first-child {
+    border-right: 1px solid var(--primary-color);
+}
+
+#QueuePopUp #QueuePopUpBody .JobQueue {
+    display: flex;
+    flex-direction: column;
+}
+
+.QueueJob {
+    color: black;
+    margin: 5px 0;
+    position: relative;
+    height: 200px;
+    display: grid;
+    grid-template-columns: 150px auto;
+    grid-template-rows: 25% 20% auto 15% 12%;
+    column-gap: 10px;
+    grid-template-areas:
+    "cover name"
+    "cover jobType"
+    "cover additionalInfo"
+    "cover progress"
+    "cover actions"
+}
+
+.QueueJob p {
+    margin: 2px 0;
+}
+
+.QueueJob img{
+    grid-area: cover;
+    height: 100%;
+    max-width: 100%;
+}
+
+.QueueJob .QueueJob-Name{
+    grid-area: name;
+    font-weight: bold;
+    font-size: 14pt;
+}
+
+.QueueJob .JobType{
+    grid-area: jobType;
+}
+
+.QueueJob .QueueJob-additionalInfo {
+    grid-area: additionalInfo;
+}
+
+.QueueJob .QueueJob-actions {
+    grid-area: actions;
+    display: flex;
+    justify-content: space-evenly;
+}
+
+.QueueJob .QueueJob-Cancel {
+    grid-area: actions;
+    width: 150px;
+}
+
+.QueueJob .QueueJob-Progressbar {
+    grid-area: progress;
+}
\ No newline at end of file
diff --git a/Website/styles/react-toggle.css b/Website/styles/react-toggle.css
new file mode 100644
index 0000000..1a139ba
--- /dev/null
+++ b/Website/styles/react-toggle.css
@@ -0,0 +1,143 @@
+/* https://raw.githubusercontent.com/instructure-react/react-toggle/master/style.css */
+
+.react-toggle {
+    touch-action: pan-x;
+
+    display: inline-block;
+    position: relative;
+    cursor: pointer;
+    background-color: transparent;
+    border: 0;
+    padding: 0;
+
+    -webkit-touch-callout: none;
+    -webkit-user-select: none;
+    -khtml-user-select: none;
+    -moz-user-select: none;
+    -ms-user-select: none;
+    user-select: none;
+
+    -webkit-tap-highlight-color: rgba(0,0,0,0);
+    -webkit-tap-highlight-color: transparent;
+}
+
+.react-toggle-screenreader-only {
+    border: 0;
+    clip: rect(0 0 0 0);
+    height: 1px;
+    margin: -1px;
+    overflow: hidden;
+    padding: 0;
+    position: absolute;
+    width: 1px;
+}
+
+.react-toggle--disabled {
+    cursor: not-allowed;
+    opacity: 0.5;
+    -webkit-transition: opacity 0.25s;
+    transition: opacity 0.25s;
+}
+
+.react-toggle-track {
+    width: 50px;
+    height: 24px;
+    padding: 0;
+    border-radius: 30px;
+    background-color: #4D4D4D;
+    -webkit-transition: all 0.2s ease;
+    -moz-transition: all 0.2s ease;
+    transition: all 0.2s ease;
+}
+
+.react-toggle:hover:not(.react-toggle--disabled) .react-toggle-track {
+    background-color: #000000;
+}
+
+.react-toggle--checked .react-toggle-track {
+    background-color: #19AB27;
+}
+
+.react-toggle--checked:hover:not(.react-toggle--disabled) .react-toggle-track {
+    background-color: #128D15;
+}
+
+.react-toggle-track-check {
+    position: absolute;
+    width: 14px;
+    height: 10px;
+    top: 0px;
+    bottom: 0px;
+    margin-top: auto;
+    margin-bottom: auto;
+    line-height: 0;
+    left: 8px;
+    opacity: 0;
+    -webkit-transition: opacity 0.25s ease;
+    -moz-transition: opacity 0.25s ease;
+    transition: opacity 0.25s ease;
+}
+
+.react-toggle--checked .react-toggle-track-check {
+    opacity: 1;
+    -webkit-transition: opacity 0.25s ease;
+    -moz-transition: opacity 0.25s ease;
+    transition: opacity 0.25s ease;
+}
+
+.react-toggle-track-x {
+    position: absolute;
+    width: 10px;
+    height: 10px;
+    top: 0px;
+    bottom: 0px;
+    margin-top: auto;
+    margin-bottom: auto;
+    line-height: 0;
+    right: 10px;
+    opacity: 1;
+    -webkit-transition: opacity 0.25s ease;
+    -moz-transition: opacity 0.25s ease;
+    transition: opacity 0.25s ease;
+}
+
+.react-toggle--checked .react-toggle-track-x {
+    opacity: 0;
+}
+
+.react-toggle-thumb {
+    transition: all 0.5s cubic-bezier(0.23, 1, 0.32, 1) 0ms;
+    position: absolute;
+    top: 1px;
+    left: 1px;
+    width: 22px;
+    height: 22px;
+    border: 1px solid #4D4D4D;
+    border-radius: 50%;
+    background-color: #FAFAFA;
+
+    -webkit-box-sizing: border-box;
+    -moz-box-sizing: border-box;
+    box-sizing: border-box;
+
+    -webkit-transition: all 0.25s ease;
+    -moz-transition: all 0.25s ease;
+    transition: all 0.25s ease;
+}
+
+.react-toggle--checked .react-toggle-thumb {
+    left: 27px;
+    border-color: #19AB27;
+}
+
+.react-toggle--focus .react-toggle-thumb {
+    -webkit-box-shadow: 0px 0px 3px 2px #0099E0;
+    -moz-box-shadow: 0px 0px 3px 2px #0099E0;
+    box-shadow: 0px 0px 2px 3px #0099E0;
+}
+
+.react-toggle:active:not(.react-toggle--disabled) .react-toggle-thumb {
+    -webkit-box-shadow: 0px 0px 5px 5px #0099E0;
+    -moz-box-shadow: 0px 0px 5px 5px #0099E0;
+    box-shadow: 0px 0px 5px 5px #0099E0;
+}
diff --git a/Website/styles/search.css b/Website/styles/search.css
new file mode 100644
index 0000000..270a58f
--- /dev/null
+++ b/Website/styles/search.css
@@ -0,0 +1,58 @@
+#Search{
+    position: relative;
+    width: 98vw;
+    margin: auto;
+}
+
+#SearchBox{
+    display: flex;
+    align-content: center;
+    justify-content: center;
+    margin: 10px 0;
+}
+
+#SearchResults {
+    width: 100%;
+}
+
+#SearchBox select, #SearchBox button, #SearchBox input {
+    border-color: var(--primary-color);
+    border-style: solid;
+    border-width: 0;
+    border-bottom-width: 2px;
+    border-top-width: 2px;
+    padding: 2px 5px;
+    font-size: 12pt;
+}
+
+#Searchbox-Manganame {
+    border-bottom-left-radius: 2px;
+    border-top-left-radius: 2px;
+    border-left-width: 2px !important;
+    min-width: 300px;
+    max-width: 50vw;
+}
+
+#Searchbox-connector {
+    width: max-content;
+}
+
+#Searchbox-language {
+    width: 90px;
+}
+
+#Searchbox-button {
+    border-bottom-right-radius: 2px;
+    border-top-right-radius: 2px;
+    border-right-width: 2px !important;
+    width: 90px;
+}
+
+#closeSearch {
+    position: absolute;
+    top: 0;
+    right: 0;
+    width: 30px;
+    height: 30px;
+    filter: brightness(0) saturate(100%) invert(100%) sepia(100%) saturate(1%) hue-rotate(20deg) brightness(103%) contrast(101%);
+}
\ No newline at end of file
diff --git a/Website/styles/settings.css b/Website/styles/settings.css
new file mode 100644
index 0000000..85a515f
--- /dev/null
+++ b/Website/styles/settings.css
@@ -0,0 +1,101 @@
+#Settings {
+    position: relative;
+    height: 100%;
+}
+
+#SettingsIcon {
+    height: calc(100% - 26px);
+    margin: 13px;
+    filter: invert(100%) sepia(65%) saturate(2%) hue-rotate(215deg) brightness(113%) contrast(101%);
+}
+
+#settingsPopupBody {
+    display: flex;
+    flex-direction: column;
+    overflow-y: scroll;
+}
+
+.settings-section {
+    margin: 5px 5px 10px;
+    font-size: 10pt;
+    font-weight: 100;
+    display: block;
+    border-top-style: solid;
+    border-top-width: 1px;
+    border-top-color: lightgray;
+    width: calc(100% - 30px);
+    padding: 10px;
+}
+
+.settings-section-content {
+    display: flex;
+    flex-direction: row;
+    width: 100%;
+    flex-wrap: wrap;
+
+}
+
+.section-item {
+    position: relative;
+    display: flex;
+    flex-direction: column;
+    width: 22%;
+    min-width: 300px;
+    height: auto;
+    border-radius: 10px;
+    border-style: solid;
+    border-width: 1px;
+    border-color: lightgray;
+    margin: 7px;
+    padding: 5px 5px 35px;
+}
+
+.section-item > * {
+    margin: 2px 0;
+}
+
+.section-item[connector-status="Not Configured"]{
+    border-color: var(--primary-color);
+}
+
+.section-item[connector-status="Configured"]{
+    border-color: green;
+}
+
+.section-item > .settings-section-title {
+    font-weight: bold;
+    vertical-align: bottom;
+    line-height: 32px;
+    font-size: 12pt;
+    width: 100%;
+}
+
+.section-item > .settings-section-title > img {
+    width: auto;
+    height: 32px;
+    margin: 5px;
+    vertical-align: middle;
+    border-radius: 5px;
+}
+
+.section-item .section-actions {
+    position: absolute;
+    bottom: 0;
+    display: flex;
+    justify-content: space-around;
+    margin: 5px;
+    width: calc(100% - 20px);
+}
+
+.section-actions > span, #resetUserAgent {
+    border: 1px solid lightgray;
+    padding: 3px 5px 2px;
+    width: 10ch;
+    text-align: center;
+    border-radius: 3px;
+}
+
+#resetUserAgent{
+    align-self: flex-end;
+    margin-top: 3px;
+}
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..b151042
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,3213 @@
+{
+  "name": "tranga-website",
+  "lockfileVersion": 3,
+  "requires": true,
+  "packages": {
+    "": {
+      "dependencies": {
+        "@ramonak/react-progress-bar": "^5.3.0",
+        "@uiw/react-markdown-preview": "^5.1.3"
+      },
+      "devDependencies": {
+        "@mdi/js": "^7.4.47",
+        "@mdi/react": "^1.6.1",
+        "@types/react": "^18.2.0",
+        "@types/react-toggle": "^4.0.5",
+        "react": "^18.3.1",
+        "react-cookie": "^7.2.1",
+        "react-dom": "^18.3.1",
+        "react-toggle": "^4.1.3",
+        "typescript": "^5.6.3",
+        "vite": "^5.4.9"
+      }
+    },
+    "node_modules/@babel/runtime": {
+      "version": "7.25.7",
+      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.7.tgz",
+      "integrity": "sha512-FjoyLe754PMiYsFaN5C94ttGiOmBNYTf6pLr4xXHAT5uctHb092PBszndLDR5XA/jghQvn4n7JMHl7dmTgbm9w==",
+      "license": "MIT",
+      "dependencies": {
+        "regenerator-runtime": "^0.14.0"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@esbuild/aix-ppc64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
+      "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "aix"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/android-arm": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz",
+      "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/android-arm64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz",
+      "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/android-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz",
+      "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/darwin-arm64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz",
+      "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/darwin-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz",
+      "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/freebsd-arm64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz",
+      "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/freebsd-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz",
+      "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-arm": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz",
+      "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-arm64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz",
+      "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-ia32": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz",
+      "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-loong64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz",
+      "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==",
+      "cpu": [
+        "loong64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-mips64el": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz",
+      "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==",
+      "cpu": [
+        "mips64el"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-ppc64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz",
+      "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-riscv64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz",
+      "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-s390x": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz",
+      "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==",
+      "cpu": [
+        "s390x"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
+      "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/netbsd-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
+      "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "netbsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/openbsd-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
+      "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "openbsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/sunos-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
+      "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "sunos"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/win32-arm64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz",
+      "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/win32-ia32": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz",
+      "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/win32-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz",
+      "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@mdi/js": {
+      "version": "7.4.47",
+      "resolved": "https://registry.npmjs.org/@mdi/js/-/js-7.4.47.tgz",
+      "integrity": "sha512-KPnNOtm5i2pMabqZxpUz7iQf+mfrYZyKCZ8QNz85czgEt7cuHcGorWfdzUMWYA0SD+a6Hn4FmJ+YhzzzjkTZrQ==",
+      "dev": true,
+      "license": "Apache-2.0"
+    },
+    "node_modules/@mdi/react": {
+      "version": "1.6.1",
+      "resolved": "https://registry.npmjs.org/@mdi/react/-/react-1.6.1.tgz",
+      "integrity": "sha512-4qZeDcluDFGFTWkHs86VOlHkm6gnKaMql13/gpIcUQ8kzxHgpj31NuCkD8abECVfbULJ3shc7Yt4HJ6Wu6SN4w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "prop-types": "^15.7.2"
+      }
+    },
+    "node_modules/@ramonak/react-progress-bar": {
+      "version": "5.3.0",
+      "resolved": "https://registry.npmjs.org/@ramonak/react-progress-bar/-/react-progress-bar-5.3.0.tgz",
+      "integrity": "sha512-PjpOcSBAVSQNyx2cvYyBCI14Tg2eFM0psC9m2ic33PYBIdOzO9/DieWndq9BUQTSjIIarhSpa/lqJ33W/mFJMw==",
+      "license": "MIT",
+      "peerDependencies": {
+        "react": "^16.0.0 || ^17 || ^18",
+        "react-dom": "^16.0.0 || ^17 || ^18"
+      }
+    },
+    "node_modules/@rollup/rollup-android-arm-eabi": {
+      "version": "4.24.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.0.tgz",
+      "integrity": "sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ]
+    },
+    "node_modules/@rollup/rollup-android-arm64": {
+      "version": "4.24.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.0.tgz",
+      "integrity": "sha512-ijLnS1qFId8xhKjT81uBHuuJp2lU4x2yxa4ctFPtG+MqEE6+C5f/+X/bStmxapgmwLwiL3ih122xv8kVARNAZA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ]
+    },
+    "node_modules/@rollup/rollup-darwin-arm64": {
+      "version": "4.24.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.0.tgz",
+      "integrity": "sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@rollup/rollup-darwin-x64": {
+      "version": "4.24.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.0.tgz",
+      "integrity": "sha512-X6/nOwoFN7RT2svEQWUsW/5C/fYMBe4fnLK9DQk4SX4mgVBiTA9h64kjUYPvGQ0F/9xwJ5U5UfTbl6BEjaQdBQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+      "version": "4.24.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.0.tgz",
+      "integrity": "sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+      "version": "4.24.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.0.tgz",
+      "integrity": "sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm64-gnu": {
+      "version": "4.24.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.0.tgz",
+      "integrity": "sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm64-musl": {
+      "version": "4.24.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.0.tgz",
+      "integrity": "sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
+      "version": "4.24.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.0.tgz",
+      "integrity": "sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+      "version": "4.24.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.0.tgz",
+      "integrity": "sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-s390x-gnu": {
+      "version": "4.24.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.0.tgz",
+      "integrity": "sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g==",
+      "cpu": [
+        "s390x"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-x64-gnu": {
+      "version": "4.24.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.0.tgz",
+      "integrity": "sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-x64-musl": {
+      "version": "4.24.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.0.tgz",
+      "integrity": "sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-win32-arm64-msvc": {
+      "version": "4.24.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.0.tgz",
+      "integrity": "sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@rollup/rollup-win32-ia32-msvc": {
+      "version": "4.24.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.0.tgz",
+      "integrity": "sha512-xrNcGDU0OxVcPTH/8n/ShH4UevZxKIO6HJFK0e15XItZP2UcaiLFd5kiX7hJnqCbSztUF8Qot+JWBC/QXRPYWQ==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@rollup/rollup-win32-x64-msvc": {
+      "version": "4.24.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.0.tgz",
+      "integrity": "sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@types/cookie": {
+      "version": "0.6.0",
+      "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz",
+      "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@types/debug": {
+      "version": "4.1.12",
+      "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz",
+      "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/ms": "*"
+      }
+    },
+    "node_modules/@types/estree": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
+      "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
+      "license": "MIT"
+    },
+    "node_modules/@types/estree-jsx": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz",
+      "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/estree": "*"
+      }
+    },
+    "node_modules/@types/hast": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz",
+      "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/unist": "*"
+      }
+    },
+    "node_modules/@types/hoist-non-react-statics": {
+      "version": "3.3.5",
+      "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz",
+      "integrity": "sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/react": "*",
+        "hoist-non-react-statics": "^3.3.0"
+      }
+    },
+    "node_modules/@types/mdast": {
+      "version": "4.0.4",
+      "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz",
+      "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/unist": "*"
+      }
+    },
+    "node_modules/@types/ms": {
+      "version": "0.7.34",
+      "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz",
+      "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==",
+      "license": "MIT"
+    },
+    "node_modules/@types/prismjs": {
+      "version": "1.26.4",
+      "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.4.tgz",
+      "integrity": "sha512-rlAnzkW2sZOjbqZ743IHUhFcvzaGbqijwOu8QZnZCjfQzBqFE3s4lOTJEsxikImav9uzz/42I+O7YUs1mWgMlg==",
+      "license": "MIT"
+    },
+    "node_modules/@types/prop-types": {
+      "version": "15.7.13",
+      "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz",
+      "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==",
+      "license": "MIT"
+    },
+    "node_modules/@types/react": {
+      "version": "18.2.0",
+      "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.0.tgz",
+      "integrity": "sha512-0FLj93y5USLHdnhIhABk83rm8XEGA7kH3cr+YUlvxoUGp1xNt/DINUMvqPxLyOQMzLmZe8i4RTHbvb8MC7NmrA==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/prop-types": "*",
+        "@types/scheduler": "*",
+        "csstype": "^3.0.2"
+      }
+    },
+    "node_modules/@types/react-toggle": {
+      "version": "4.0.5",
+      "resolved": "https://registry.npmjs.org/@types/react-toggle/-/react-toggle-4.0.5.tgz",
+      "integrity": "sha512-MHHEDe7GnF/EhLtI5sT70Dqab8rwlgjRZtu/u6gmfbYd+HeYxWiUSRog16+1BCfkz7Wy2VU6+TPU2oCsDtqDzA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/react": "*"
+      }
+    },
+    "node_modules/@types/scheduler": {
+      "version": "0.23.0",
+      "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.23.0.tgz",
+      "integrity": "sha512-YIoDCTH3Af6XM5VuwGG/QL/CJqga1Zm3NkU3HZ4ZHK2fRMPYP1VczsTUqtsf43PH/iJNVlPHAo2oWX7BSdB2Hw==",
+      "license": "MIT"
+    },
+    "node_modules/@types/unist": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
+      "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
+      "license": "MIT"
+    },
+    "node_modules/@uiw/copy-to-clipboard": {
+      "version": "1.0.17",
+      "resolved": "https://registry.npmjs.org/@uiw/copy-to-clipboard/-/copy-to-clipboard-1.0.17.tgz",
+      "integrity": "sha512-O2GUHV90Iw2VrSLVLK0OmNIMdZ5fgEg4NhvtwINsX+eZ/Wf6DWD0TdsK9xwV7dNRnK/UI2mQtl0a2/kRgm1m1A==",
+      "license": "MIT",
+      "funding": {
+        "url": "https://jaywcjlove.github.io/#/sponsor"
+      }
+    },
+    "node_modules/@uiw/react-markdown-preview": {
+      "version": "5.1.3",
+      "resolved": "https://registry.npmjs.org/@uiw/react-markdown-preview/-/react-markdown-preview-5.1.3.tgz",
+      "integrity": "sha512-jV02wO4XHWFk54kz7sLqOkdPgJLttSfKLyen47XgjcyGgQXU2I4WJBygmdpV2AT9m/MiQ8qrN1Y+E5Syv9ZDpw==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/runtime": "^7.17.2",
+        "@uiw/copy-to-clipboard": "~1.0.12",
+        "react-markdown": "~9.0.1",
+        "rehype-attr": "~3.0.1",
+        "rehype-autolink-headings": "~7.1.0",
+        "rehype-ignore": "^2.0.0",
+        "rehype-prism-plus": "2.0.0",
+        "rehype-raw": "^7.0.0",
+        "rehype-rewrite": "~4.0.0",
+        "rehype-slug": "~6.0.0",
+        "remark-gfm": "~4.0.0",
+        "remark-github-blockquote-alert": "^1.0.0",
+        "unist-util-visit": "^5.0.0"
+      },
+      "funding": {
+        "url": "https://jaywcjlove.github.io/#/sponsor"
+      },
+      "peerDependencies": {
+        "react": ">=16.8.0",
+        "react-dom": ">=16.8.0"
+      }
+    },
+    "node_modules/@ungap/structured-clone": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
+      "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==",
+      "license": "ISC"
+    },
+    "node_modules/bail": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz",
+      "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==",
+      "license": "MIT",
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/wooorm"
+      }
+    },
+    "node_modules/bcp-47-match": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/bcp-47-match/-/bcp-47-match-2.0.3.tgz",
+      "integrity": "sha512-JtTezzbAibu8G0R9op9zb3vcWZd9JF6M0xOYGPn0fNCd7wOpRB1mU2mH9T8gaBGbAAyIIVgB2G7xG0GP98zMAQ==",
+      "license": "MIT",
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/wooorm"
+      }
+    },
+    "node_modules/boolbase": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
+      "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
+      "license": "ISC"
+    },
+    "node_modules/ccount": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz",
+      "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==",
+      "license": "MIT",
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/wooorm"
+      }
+    },
+    "node_modules/character-entities": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz",
+      "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==",
+      "license": "MIT",
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/wooorm"
+      }
+    },
+    "node_modules/character-entities-html4": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz",
+      "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==",
+      "license": "MIT",
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/wooorm"
+      }
+    },
+    "node_modules/character-entities-legacy": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz",
+      "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==",
+      "license": "MIT",
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/wooorm"
+      }
+    },
+    "node_modules/character-reference-invalid": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz",
+      "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==",
+      "license": "MIT",
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/wooorm"
+      }
+    },
+    "node_modules/classnames": {
+      "version": "2.5.1",
+      "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz",
+      "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/comma-separated-tokens": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz",
+      "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==",
+      "license": "MIT",
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/wooorm"
+      }
+    },
+    "node_modules/cookie": {
+      "version": "0.7.2",
+      "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
+      "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/css-selector-parser": {
+      "version": "3.0.5",
+      "resolved": "https://registry.npmjs.org/css-selector-parser/-/css-selector-parser-3.0.5.tgz",
+      "integrity": "sha512-3itoDFbKUNx1eKmVpYMFyqKX04Ww9osZ+dLgrk6GEv6KMVeXUhUnp4I5X+evw+u3ZxVU6RFXSSRxlTeMh8bA+g==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/mdevils"
+        },
+        {
+          "type": "patreon",
+          "url": "https://patreon.com/mdevils"
+        }
+      ],
+      "license": "MIT"
+    },
+    "node_modules/csstype": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
+      "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
+      "license": "MIT"
+    },
+    "node_modules/debug": {
+      "version": "4.3.7",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
+      "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
+      "license": "MIT",
+      "dependencies": {
+        "ms": "^2.1.3"
+      },
+      "engines": {
+        "node": ">=6.0"
+      },
+      "peerDependenciesMeta": {
+        "supports-color": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/decode-named-character-reference": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz",
+      "integrity": "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==",
+      "license": "MIT",
+      "dependencies": {
+        "character-entities": "^2.0.0"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/wooorm"
+      }
+    },
+    "node_modules/dequal": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
+      "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/devlop": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz",
+      "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==",
+      "license": "MIT",
+      "dependencies": {
+        "dequal": "^2.0.0"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/wooorm"
+      }
+    },
+    "node_modules/direction": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/direction/-/direction-2.0.1.tgz",
+      "integrity": "sha512-9S6m9Sukh1cZNknO1CWAr2QAWsbKLafQiyM5gZ7VgXHeuaoUwffKN4q6NC4A/Mf9iiPlOXQEKW/Mv/mh9/3YFA==",
+      "license": "MIT",
+      "bin": {
+        "direction": "cli.js"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/wooorm"
+      }
+    },
+    "node_modules/entities": {
+      "version": "4.5.0",
+      "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
+      "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=0.12"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/entities?sponsor=1"
+      }
+    },
+    "node_modules/esbuild": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
+      "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
+      "dev": true,
+      "hasInstallScript": true,
+      "license": "MIT",
+      "bin": {
+        "esbuild": "bin/esbuild"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "optionalDependencies": {
+        "@esbuild/aix-ppc64": "0.21.5",
+        "@esbuild/android-arm": "0.21.5",
+        "@esbuild/android-arm64": "0.21.5",
+        "@esbuild/android-x64": "0.21.5",
+        "@esbuild/darwin-arm64": "0.21.5",
+        "@esbuild/darwin-x64": "0.21.5",
+        "@esbuild/freebsd-arm64": "0.21.5",
+        "@esbuild/freebsd-x64": "0.21.5",
+        "@esbuild/linux-arm": "0.21.5",
+        "@esbuild/linux-arm64": "0.21.5",
+        "@esbuild/linux-ia32": "0.21.5",
+        "@esbuild/linux-loong64": "0.21.5",
+        "@esbuild/linux-mips64el": "0.21.5",
+        "@esbuild/linux-ppc64": "0.21.5",
+        "@esbuild/linux-riscv64": "0.21.5",
+        "@esbuild/linux-s390x": "0.21.5",
+        "@esbuild/linux-x64": "0.21.5",
+        "@esbuild/netbsd-x64": "0.21.5",
+        "@esbuild/openbsd-x64": "0.21.5",
+        "@esbuild/sunos-x64": "0.21.5",
+        "@esbuild/win32-arm64": "0.21.5",
+        "@esbuild/win32-ia32": "0.21.5",
+        "@esbuild/win32-x64": "0.21.5"
+      }
+    },
+    "node_modules/escape-string-regexp": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz",
+      "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/estree-util-is-identifier-name": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz",
+      "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==",
+      "license": "MIT",
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/extend": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+      "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+      "license": "MIT"
+    },
+    "node_modules/fsevents": {
+      "version": "2.3.3",
+      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+      "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+      "dev": true,
+      "hasInstallScript": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+      }
+    },
+    "node_modules/github-slugger": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz",
+      "integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==",
+      "license": "ISC"
+    },
+    "node_modules/hast-util-from-html": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/hast-util-from-html/-/hast-util-from-html-2.0.3.tgz",
+      "integrity": "sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/hast": "^3.0.0",
+        "devlop": "^1.1.0",
+        "hast-util-from-parse5": "^8.0.0",
+        "parse5": "^7.0.0",
+        "vfile": "^6.0.0",
+        "vfile-message": "^4.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/hast-util-from-parse5": {
+      "version": "8.0.1",
+      "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.1.tgz",
+      "integrity": "sha512-Er/Iixbc7IEa7r/XLtuG52zoqn/b3Xng/w6aZQ0xGVxzhw5xUFxcRqdPzP6yFi/4HBYRaifaI5fQ1RH8n0ZeOQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/hast": "^3.0.0",
+        "@types/unist": "^3.0.0",
+        "devlop": "^1.0.0",
+        "hastscript": "^8.0.0",
+        "property-information": "^6.0.0",
+        "vfile": "^6.0.0",
+        "vfile-location": "^5.0.0",
+        "web-namespaces": "^2.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/hast-util-from-parse5/node_modules/hast-util-parse-selector": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz",
+      "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/hast": "^3.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/hast-util-from-parse5/node_modules/hastscript": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-8.0.0.tgz",
+      "integrity": "sha512-dMOtzCEd3ABUeSIISmrETiKuyydk1w0pa+gE/uormcTpSYuaNJPbX1NU3JLyscSLjwAQM8bWMhhIlnCqnRvDTw==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/hast": "^3.0.0",
+        "comma-separated-tokens": "^2.0.0",
+        "hast-util-parse-selector": "^4.0.0",
+        "property-information": "^6.0.0",
+        "space-separated-tokens": "^2.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/hast-util-has-property": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/hast-util-has-property/-/hast-util-has-property-3.0.0.tgz",
+      "integrity": "sha512-MNilsvEKLFpV604hwfhVStK0usFY/QmM5zX16bo7EjnAEGofr5YyI37kzopBlZJkHD4t887i+q/C8/tr5Q94cA==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/hast": "^3.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/hast-util-heading-rank": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/hast-util-heading-rank/-/hast-util-heading-rank-3.0.0.tgz",
+      "integrity": "sha512-EJKb8oMUXVHcWZTDepnr+WNbfnXKFNf9duMesmr4S8SXTJBJ9M4Yok08pu9vxdJwdlGRhVumk9mEhkEvKGifwA==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/hast": "^3.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/hast-util-is-element": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-3.0.0.tgz",
+      "integrity": "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/hast": "^3.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/hast-util-parse-selector": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-3.1.1.tgz",
+      "integrity": "sha512-jdlwBjEexy1oGz0aJ2f4GKMaVKkA9jwjr4MjAAI22E5fM/TXVZHuS5OpONtdeIkRKqAaryQ2E9xNQxijoThSZA==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/hast": "^2.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/hast-util-parse-selector/node_modules/@types/hast": {
+      "version": "2.3.10",
+      "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.10.tgz",
+      "integrity": "sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/unist": "^2"
+      }
+    },
+    "node_modules/hast-util-parse-selector/node_modules/@types/unist": {
+      "version": "2.0.11",
+      "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz",
+      "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==",
+      "license": "MIT"
+    },
+    "node_modules/hast-util-raw": {
+      "version": "9.0.4",
+      "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.0.4.tgz",
+      "integrity": "sha512-LHE65TD2YiNsHD3YuXcKPHXPLuYh/gjp12mOfU8jxSrm1f/yJpsb0F/KKljS6U9LJoP0Ux+tCe8iJ2AsPzTdgA==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/hast": "^3.0.0",
+        "@types/unist": "^3.0.0",
+        "@ungap/structured-clone": "^1.0.0",
+        "hast-util-from-parse5": "^8.0.0",
+        "hast-util-to-parse5": "^8.0.0",
+        "html-void-elements": "^3.0.0",
+        "mdast-util-to-hast": "^13.0.0",
+        "parse5": "^7.0.0",
+        "unist-util-position": "^5.0.0",
+        "unist-util-visit": "^5.0.0",
+        "vfile": "^6.0.0",
+        "web-namespaces": "^2.0.0",
+        "zwitch": "^2.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/hast-util-select": {
+      "version": "6.0.3",
+      "resolved": "https://registry.npmjs.org/hast-util-select/-/hast-util-select-6.0.3.tgz",
+      "integrity": "sha512-OVRQlQ1XuuLP8aFVLYmC2atrfWHS5UD3shonxpnyrjcCkwtvmt/+N6kYJdcY4mkMJhxp4kj2EFIxQ9kvkkt/eQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/hast": "^3.0.0",
+        "@types/unist": "^3.0.0",
+        "bcp-47-match": "^2.0.0",
+        "comma-separated-tokens": "^2.0.0",
+        "css-selector-parser": "^3.0.0",
+        "devlop": "^1.0.0",
+        "direction": "^2.0.0",
+        "hast-util-has-property": "^3.0.0",
+        "hast-util-to-string": "^3.0.0",
+        "hast-util-whitespace": "^3.0.0",
+        "nth-check": "^2.0.0",
+        "property-information": "^6.0.0",
+        "space-separated-tokens": "^2.0.0",
+        "unist-util-visit": "^5.0.0",
+        "zwitch": "^2.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/hast-util-to-jsx-runtime": {
+      "version": "2.3.2",
+      "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.2.tgz",
+      "integrity": "sha512-1ngXYb+V9UT5h+PxNRa1O1FYguZK/XL+gkeqvp7EdHlB9oHUG0eYRo/vY5inBdcqo3RkPMC58/H94HvkbfGdyg==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/estree": "^1.0.0",
+        "@types/hast": "^3.0.0",
+        "@types/unist": "^3.0.0",
+        "comma-separated-tokens": "^2.0.0",
+        "devlop": "^1.0.0",
+        "estree-util-is-identifier-name": "^3.0.0",
+        "hast-util-whitespace": "^3.0.0",
+        "mdast-util-mdx-expression": "^2.0.0",
+        "mdast-util-mdx-jsx": "^3.0.0",
+        "mdast-util-mdxjs-esm": "^2.0.0",
+        "property-information": "^6.0.0",
+        "space-separated-tokens": "^2.0.0",
+        "style-to-object": "^1.0.0",
+        "unist-util-position": "^5.0.0",
+        "vfile-message": "^4.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/hast-util-to-parse5": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-8.0.0.tgz",
+      "integrity": "sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/hast": "^3.0.0",
+        "comma-separated-tokens": "^2.0.0",
+        "devlop": "^1.0.0",
+        "property-information": "^6.0.0",
+        "space-separated-tokens": "^2.0.0",
+        "web-namespaces": "^2.0.0",
+        "zwitch": "^2.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/hast-util-to-string": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/hast-util-to-string/-/hast-util-to-string-3.0.1.tgz",
+      "integrity": "sha512-XelQVTDWvqcl3axRfI0xSeoVKzyIFPwsAGSLIsKdJKQMXDYJS4WYrBNF/8J7RdhIcFI2BOHgAifggsvsxp/3+A==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/hast": "^3.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/hast-util-whitespace": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz",
+      "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/hast": "^3.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/hastscript": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-7.2.0.tgz",
+      "integrity": "sha512-TtYPq24IldU8iKoJQqvZOuhi5CyCQRAbvDOX0x1eW6rsHSxa/1i2CCiptNTotGHJ3VoHRGmqiv6/D3q113ikkw==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/hast": "^2.0.0",
+        "comma-separated-tokens": "^2.0.0",
+        "hast-util-parse-selector": "^3.0.0",
+        "property-information": "^6.0.0",
+        "space-separated-tokens": "^2.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/hastscript/node_modules/@types/hast": {
+      "version": "2.3.10",
+      "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.10.tgz",
+      "integrity": "sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/unist": "^2"
+      }
+    },
+    "node_modules/hastscript/node_modules/@types/unist": {
+      "version": "2.0.11",
+      "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz",
+      "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==",
+      "license": "MIT"
+    },
+    "node_modules/hoist-non-react-statics": {
+      "version": "3.3.2",
+      "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
+      "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "react-is": "^16.7.0"
+      }
+    },
+    "node_modules/html-url-attributes": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz",
+      "integrity": "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==",
+      "license": "MIT",
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/html-void-elements": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz",
+      "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==",
+      "license": "MIT",
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/wooorm"
+      }
+    },
+    "node_modules/inline-style-parser": {
+      "version": "0.2.4",
+      "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.4.tgz",
+      "integrity": "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==",
+      "license": "MIT"
+    },
+    "node_modules/is-alphabetical": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz",
+      "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==",
+      "license": "MIT",
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/wooorm"
+      }
+    },
+    "node_modules/is-alphanumerical": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz",
+      "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==",
+      "license": "MIT",
+      "dependencies": {
+        "is-alphabetical": "^2.0.0",
+        "is-decimal": "^2.0.0"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/wooorm"
+      }
+    },
+    "node_modules/is-decimal": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz",
+      "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==",
+      "license": "MIT",
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/wooorm"
+      }
+    },
+    "node_modules/is-hexadecimal": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz",
+      "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==",
+      "license": "MIT",
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/wooorm"
+      }
+    },
+    "node_modules/is-plain-obj": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz",
+      "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/js-tokens": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+      "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+      "license": "MIT"
+    },
+    "node_modules/longest-streak": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz",
+      "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==",
+      "license": "MIT",
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/wooorm"
+      }
+    },
+    "node_modules/loose-envify": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+      "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+      "license": "MIT",
+      "dependencies": {
+        "js-tokens": "^3.0.0 || ^4.0.0"
+      },
+      "bin": {
+        "loose-envify": "cli.js"
+      }
+    },
+    "node_modules/markdown-table": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.3.tgz",
+      "integrity": "sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw==",
+      "license": "MIT",
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/wooorm"
+      }
+    },
+    "node_modules/mdast-util-find-and-replace": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.1.tgz",
+      "integrity": "sha512-SG21kZHGC3XRTSUhtofZkBzZTJNM5ecCi0SK2IMKmSXR8vO3peL+kb1O0z7Zl83jKtutG4k5Wv/W7V3/YHvzPA==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/mdast": "^4.0.0",
+        "escape-string-regexp": "^5.0.0",
+        "unist-util-is": "^6.0.0",
+        "unist-util-visit-parents": "^6.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/mdast-util-from-markdown": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.1.tgz",
+      "integrity": "sha512-aJEUyzZ6TzlsX2s5B4Of7lN7EQtAxvtradMMglCQDyaTFgse6CmtmdJ15ElnVRlCg1vpNyVtbem0PWzlNieZsA==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/mdast": "^4.0.0",
+        "@types/unist": "^3.0.0",
+        "decode-named-character-reference": "^1.0.0",
+        "devlop": "^1.0.0",
+        "mdast-util-to-string": "^4.0.0",
+        "micromark": "^4.0.0",
+        "micromark-util-decode-numeric-character-reference": "^2.0.0",
+        "micromark-util-decode-string": "^2.0.0",
+        "micromark-util-normalize-identifier": "^2.0.0",
+        "micromark-util-symbol": "^2.0.0",
+        "micromark-util-types": "^2.0.0",
+        "unist-util-stringify-position": "^4.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/mdast-util-gfm": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.0.0.tgz",
+      "integrity": "sha512-dgQEX5Amaq+DuUqf26jJqSK9qgixgd6rYDHAv4aTBuA92cTknZlKpPfa86Z/s8Dj8xsAQpFfBmPUHWJBWqS4Bw==",
+      "license": "MIT",
+      "dependencies": {
+        "mdast-util-from-markdown": "^2.0.0",
+        "mdast-util-gfm-autolink-literal": "^2.0.0",
+        "mdast-util-gfm-footnote": "^2.0.0",
+        "mdast-util-gfm-strikethrough": "^2.0.0",
+        "mdast-util-gfm-table": "^2.0.0",
+        "mdast-util-gfm-task-list-item": "^2.0.0",
+        "mdast-util-to-markdown": "^2.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/mdast-util-gfm-autolink-literal": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz",
+      "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/mdast": "^4.0.0",
+        "ccount": "^2.0.0",
+        "devlop": "^1.0.0",
+        "mdast-util-find-and-replace": "^3.0.0",
+        "micromark-util-character": "^2.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/mdast-util-gfm-footnote": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.0.0.tgz",
+      "integrity": "sha512-5jOT2boTSVkMnQ7LTrd6n/18kqwjmuYqo7JUPe+tRCY6O7dAuTFMtTPauYYrMPpox9hlN0uOx/FL8XvEfG9/mQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/mdast": "^4.0.0",
+        "devlop": "^1.1.0",
+        "mdast-util-from-markdown": "^2.0.0",
+        "mdast-util-to-markdown": "^2.0.0",
+        "micromark-util-normalize-identifier": "^2.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/mdast-util-gfm-strikethrough": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz",
+      "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/mdast": "^4.0.0",
+        "mdast-util-from-markdown": "^2.0.0",
+        "mdast-util-to-markdown": "^2.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/mdast-util-gfm-table": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz",
+      "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/mdast": "^4.0.0",
+        "devlop": "^1.0.0",
+        "markdown-table": "^3.0.0",
+        "mdast-util-from-markdown": "^2.0.0",
+        "mdast-util-to-markdown": "^2.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/mdast-util-gfm-task-list-item": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz",
+      "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/mdast": "^4.0.0",
+        "devlop": "^1.0.0",
+        "mdast-util-from-markdown": "^2.0.0",
+        "mdast-util-to-markdown": "^2.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/mdast-util-mdx-expression": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz",
+      "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/estree-jsx": "^1.0.0",
+        "@types/hast": "^3.0.0",
+        "@types/mdast": "^4.0.0",
+        "devlop": "^1.0.0",
+        "mdast-util-from-markdown": "^2.0.0",
+        "mdast-util-to-markdown": "^2.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/mdast-util-mdx-jsx": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.1.3.tgz",
+      "integrity": "sha512-bfOjvNt+1AcbPLTFMFWY149nJz0OjmewJs3LQQ5pIyVGxP4CdOqNVJL6kTaM5c68p8q82Xv3nCyFfUnuEcH3UQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/estree-jsx": "^1.0.0",
+        "@types/hast": "^3.0.0",
+        "@types/mdast": "^4.0.0",
+        "@types/unist": "^3.0.0",
+        "ccount": "^2.0.0",
+        "devlop": "^1.1.0",
+        "mdast-util-from-markdown": "^2.0.0",
+        "mdast-util-to-markdown": "^2.0.0",
+        "parse-entities": "^4.0.0",
+        "stringify-entities": "^4.0.0",
+        "unist-util-stringify-position": "^4.0.0",
+        "vfile-message": "^4.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/mdast-util-mdxjs-esm": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz",
+      "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/estree-jsx": "^1.0.0",
+        "@types/hast": "^3.0.0",
+        "@types/mdast": "^4.0.0",
+        "devlop": "^1.0.0",
+        "mdast-util-from-markdown": "^2.0.0",
+        "mdast-util-to-markdown": "^2.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/mdast-util-phrasing": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz",
+      "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/mdast": "^4.0.0",
+        "unist-util-is": "^6.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/mdast-util-to-hast": {
+      "version": "13.2.0",
+      "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz",
+      "integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/hast": "^3.0.0",
+        "@types/mdast": "^4.0.0",
+        "@ungap/structured-clone": "^1.0.0",
+        "devlop": "^1.0.0",
+        "micromark-util-sanitize-uri": "^2.0.0",
+        "trim-lines": "^3.0.0",
+        "unist-util-position": "^5.0.0",
+        "unist-util-visit": "^5.0.0",
+        "vfile": "^6.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/mdast-util-to-markdown": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.0.tgz",
+      "integrity": "sha512-SR2VnIEdVNCJbP6y7kVTJgPLifdr8WEU440fQec7qHoHOUz/oJ2jmNRqdDQ3rbiStOXb2mCDGTuwsK5OPUgYlQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/mdast": "^4.0.0",
+        "@types/unist": "^3.0.0",
+        "longest-streak": "^3.0.0",
+        "mdast-util-phrasing": "^4.0.0",
+        "mdast-util-to-string": "^4.0.0",
+        "micromark-util-decode-string": "^2.0.0",
+        "unist-util-visit": "^5.0.0",
+        "zwitch": "^2.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/mdast-util-to-string": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz",
+      "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/mdast": "^4.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/micromark": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.0.tgz",
+      "integrity": "sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ==",
+      "funding": [
+        {
+          "type": "GitHub Sponsors",
+          "url": "https://github.com/sponsors/unifiedjs"
+        },
+        {
+          "type": "OpenCollective",
+          "url": "https://opencollective.com/unified"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "@types/debug": "^4.0.0",
+        "debug": "^4.0.0",
+        "decode-named-character-reference": "^1.0.0",
+        "devlop": "^1.0.0",
+        "micromark-core-commonmark": "^2.0.0",
+        "micromark-factory-space": "^2.0.0",
+        "micromark-util-character": "^2.0.0",
+        "micromark-util-chunked": "^2.0.0",
+        "micromark-util-combine-extensions": "^2.0.0",
+        "micromark-util-decode-numeric-character-reference": "^2.0.0",
+        "micromark-util-encode": "^2.0.0",
+        "micromark-util-normalize-identifier": "^2.0.0",
+        "micromark-util-resolve-all": "^2.0.0",
+        "micromark-util-sanitize-uri": "^2.0.0",
+        "micromark-util-subtokenize": "^2.0.0",
+        "micromark-util-symbol": "^2.0.0",
+        "micromark-util-types": "^2.0.0"
+      }
+    },
+    "node_modules/micromark-core-commonmark": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.1.tgz",
+      "integrity": "sha512-CUQyKr1e///ZODyD1U3xit6zXwy1a8q2a1S1HKtIlmgvurrEpaw/Y9y6KSIbF8P59cn/NjzHyO+Q2fAyYLQrAA==",
+      "funding": [
+        {
+          "type": "GitHub Sponsors",
+          "url": "https://github.com/sponsors/unifiedjs"
+        },
+        {
+          "type": "OpenCollective",
+          "url": "https://opencollective.com/unified"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "decode-named-character-reference": "^1.0.0",
+        "devlop": "^1.0.0",
+        "micromark-factory-destination": "^2.0.0",
+        "micromark-factory-label": "^2.0.0",
+        "micromark-factory-space": "^2.0.0",
+        "micromark-factory-title": "^2.0.0",
+        "micromark-factory-whitespace": "^2.0.0",
+        "micromark-util-character": "^2.0.0",
+        "micromark-util-chunked": "^2.0.0",
+        "micromark-util-classify-character": "^2.0.0",
+        "micromark-util-html-tag-name": "^2.0.0",
+        "micromark-util-normalize-identifier": "^2.0.0",
+        "micromark-util-resolve-all": "^2.0.0",
+        "micromark-util-subtokenize": "^2.0.0",
+        "micromark-util-symbol": "^2.0.0",
+        "micromark-util-types": "^2.0.0"
+      }
+    },
+    "node_modules/micromark-extension-gfm": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz",
+      "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==",
+      "license": "MIT",
+      "dependencies": {
+        "micromark-extension-gfm-autolink-literal": "^2.0.0",
+        "micromark-extension-gfm-footnote": "^2.0.0",
+        "micromark-extension-gfm-strikethrough": "^2.0.0",
+        "micromark-extension-gfm-table": "^2.0.0",
+        "micromark-extension-gfm-tagfilter": "^2.0.0",
+        "micromark-extension-gfm-task-list-item": "^2.0.0",
+        "micromark-util-combine-extensions": "^2.0.0",
+        "micromark-util-types": "^2.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/micromark-extension-gfm-autolink-literal": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz",
+      "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==",
+      "license": "MIT",
+      "dependencies": {
+        "micromark-util-character": "^2.0.0",
+        "micromark-util-sanitize-uri": "^2.0.0",
+        "micromark-util-symbol": "^2.0.0",
+        "micromark-util-types": "^2.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/micromark-extension-gfm-footnote": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz",
+      "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==",
+      "license": "MIT",
+      "dependencies": {
+        "devlop": "^1.0.0",
+        "micromark-core-commonmark": "^2.0.0",
+        "micromark-factory-space": "^2.0.0",
+        "micromark-util-character": "^2.0.0",
+        "micromark-util-normalize-identifier": "^2.0.0",
+        "micromark-util-sanitize-uri": "^2.0.0",
+        "micromark-util-symbol": "^2.0.0",
+        "micromark-util-types": "^2.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/micromark-extension-gfm-strikethrough": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz",
+      "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==",
+      "license": "MIT",
+      "dependencies": {
+        "devlop": "^1.0.0",
+        "micromark-util-chunked": "^2.0.0",
+        "micromark-util-classify-character": "^2.0.0",
+        "micromark-util-resolve-all": "^2.0.0",
+        "micromark-util-symbol": "^2.0.0",
+        "micromark-util-types": "^2.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/micromark-extension-gfm-table": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.0.tgz",
+      "integrity": "sha512-Ub2ncQv+fwD70/l4ou27b4YzfNaCJOvyX4HxXU15m7mpYY+rjuWzsLIPZHJL253Z643RpbcP1oeIJlQ/SKW67g==",
+      "license": "MIT",
+      "dependencies": {
+        "devlop": "^1.0.0",
+        "micromark-factory-space": "^2.0.0",
+        "micromark-util-character": "^2.0.0",
+        "micromark-util-symbol": "^2.0.0",
+        "micromark-util-types": "^2.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/micromark-extension-gfm-tagfilter": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz",
+      "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==",
+      "license": "MIT",
+      "dependencies": {
+        "micromark-util-types": "^2.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/micromark-extension-gfm-task-list-item": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz",
+      "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==",
+      "license": "MIT",
+      "dependencies": {
+        "devlop": "^1.0.0",
+        "micromark-factory-space": "^2.0.0",
+        "micromark-util-character": "^2.0.0",
+        "micromark-util-symbol": "^2.0.0",
+        "micromark-util-types": "^2.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/micromark-factory-destination": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.0.tgz",
+      "integrity": "sha512-j9DGrQLm/Uhl2tCzcbLhy5kXsgkHUrjJHg4fFAeoMRwJmJerT9aw4FEhIbZStWN8A3qMwOp1uzHr4UL8AInxtA==",
+      "funding": [
+        {
+          "type": "GitHub Sponsors",
+          "url": "https://github.com/sponsors/unifiedjs"
+        },
+        {
+          "type": "OpenCollective",
+          "url": "https://opencollective.com/unified"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "micromark-util-character": "^2.0.0",
+        "micromark-util-symbol": "^2.0.0",
+        "micromark-util-types": "^2.0.0"
+      }
+    },
+    "node_modules/micromark-factory-label": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.0.tgz",
+      "integrity": "sha512-RR3i96ohZGde//4WSe/dJsxOX6vxIg9TimLAS3i4EhBAFx8Sm5SmqVfR8E87DPSR31nEAjZfbt91OMZWcNgdZw==",
+      "funding": [
+        {
+          "type": "GitHub Sponsors",
+          "url": "https://github.com/sponsors/unifiedjs"
+        },
+        {
+          "type": "OpenCollective",
+          "url": "https://opencollective.com/unified"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "devlop": "^1.0.0",
+        "micromark-util-character": "^2.0.0",
+        "micromark-util-symbol": "^2.0.0",
+        "micromark-util-types": "^2.0.0"
+      }
+    },
+    "node_modules/micromark-factory-space": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz",
+      "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==",
+      "funding": [
+        {
+          "type": "GitHub Sponsors",
+          "url": "https://github.com/sponsors/unifiedjs"
+        },
+        {
+          "type": "OpenCollective",
+          "url": "https://opencollective.com/unified"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "micromark-util-character": "^2.0.0",
+        "micromark-util-types": "^2.0.0"
+      }
+    },
+    "node_modules/micromark-factory-title": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.0.tgz",
+      "integrity": "sha512-jY8CSxmpWLOxS+t8W+FG3Xigc0RDQA9bKMY/EwILvsesiRniiVMejYTE4wumNc2f4UbAa4WsHqe3J1QS1sli+A==",
+      "funding": [
+        {
+          "type": "GitHub Sponsors",
+          "url": "https://github.com/sponsors/unifiedjs"
+        },
+        {
+          "type": "OpenCollective",
+          "url": "https://opencollective.com/unified"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "micromark-factory-space": "^2.0.0",
+        "micromark-util-character": "^2.0.0",
+        "micromark-util-symbol": "^2.0.0",
+        "micromark-util-types": "^2.0.0"
+      }
+    },
+    "node_modules/micromark-factory-whitespace": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.0.tgz",
+      "integrity": "sha512-28kbwaBjc5yAI1XadbdPYHX/eDnqaUFVikLwrO7FDnKG7lpgxnvk/XGRhX/PN0mOZ+dBSZ+LgunHS+6tYQAzhA==",
+      "funding": [
+        {
+          "type": "GitHub Sponsors",
+          "url": "https://github.com/sponsors/unifiedjs"
+        },
+        {
+          "type": "OpenCollective",
+          "url": "https://opencollective.com/unified"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "micromark-factory-space": "^2.0.0",
+        "micromark-util-character": "^2.0.0",
+        "micromark-util-symbol": "^2.0.0",
+        "micromark-util-types": "^2.0.0"
+      }
+    },
+    "node_modules/micromark-util-character": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz",
+      "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==",
+      "funding": [
+        {
+          "type": "GitHub Sponsors",
+          "url": "https://github.com/sponsors/unifiedjs"
+        },
+        {
+          "type": "OpenCollective",
+          "url": "https://opencollective.com/unified"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "micromark-util-symbol": "^2.0.0",
+        "micromark-util-types": "^2.0.0"
+      }
+    },
+    "node_modules/micromark-util-chunked": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.0.tgz",
+      "integrity": "sha512-anK8SWmNphkXdaKgz5hJvGa7l00qmcaUQoMYsBwDlSKFKjc6gjGXPDw3FNL3Nbwq5L8gE+RCbGqTw49FK5Qyvg==",
+      "funding": [
+        {
+          "type": "GitHub Sponsors",
+          "url": "https://github.com/sponsors/unifiedjs"
+        },
+        {
+          "type": "OpenCollective",
+          "url": "https://opencollective.com/unified"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "micromark-util-symbol": "^2.0.0"
+      }
+    },
+    "node_modules/micromark-util-classify-character": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.0.tgz",
+      "integrity": "sha512-S0ze2R9GH+fu41FA7pbSqNWObo/kzwf8rN/+IGlW/4tC6oACOs8B++bh+i9bVyNnwCcuksbFwsBme5OCKXCwIw==",
+      "funding": [
+        {
+          "type": "GitHub Sponsors",
+          "url": "https://github.com/sponsors/unifiedjs"
+        },
+        {
+          "type": "OpenCollective",
+          "url": "https://opencollective.com/unified"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "micromark-util-character": "^2.0.0",
+        "micromark-util-symbol": "^2.0.0",
+        "micromark-util-types": "^2.0.0"
+      }
+    },
+    "node_modules/micromark-util-combine-extensions": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.0.tgz",
+      "integrity": "sha512-vZZio48k7ON0fVS3CUgFatWHoKbbLTK/rT7pzpJ4Bjp5JjkZeasRfrS9wsBdDJK2cJLHMckXZdzPSSr1B8a4oQ==",
+      "funding": [
+        {
+          "type": "GitHub Sponsors",
+          "url": "https://github.com/sponsors/unifiedjs"
+        },
+        {
+          "type": "OpenCollective",
+          "url": "https://opencollective.com/unified"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "micromark-util-chunked": "^2.0.0",
+        "micromark-util-types": "^2.0.0"
+      }
+    },
+    "node_modules/micromark-util-decode-numeric-character-reference": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.1.tgz",
+      "integrity": "sha512-bmkNc7z8Wn6kgjZmVHOX3SowGmVdhYS7yBpMnuMnPzDq/6xwVA604DuOXMZTO1lvq01g+Adfa0pE2UKGlxL1XQ==",
+      "funding": [
+        {
+          "type": "GitHub Sponsors",
+          "url": "https://github.com/sponsors/unifiedjs"
+        },
+        {
+          "type": "OpenCollective",
+          "url": "https://opencollective.com/unified"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "micromark-util-symbol": "^2.0.0"
+      }
+    },
+    "node_modules/micromark-util-decode-string": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.0.tgz",
+      "integrity": "sha512-r4Sc6leeUTn3P6gk20aFMj2ntPwn6qpDZqWvYmAG6NgvFTIlj4WtrAudLi65qYoaGdXYViXYw2pkmn7QnIFasA==",
+      "funding": [
+        {
+          "type": "GitHub Sponsors",
+          "url": "https://github.com/sponsors/unifiedjs"
+        },
+        {
+          "type": "OpenCollective",
+          "url": "https://opencollective.com/unified"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "decode-named-character-reference": "^1.0.0",
+        "micromark-util-character": "^2.0.0",
+        "micromark-util-decode-numeric-character-reference": "^2.0.0",
+        "micromark-util-symbol": "^2.0.0"
+      }
+    },
+    "node_modules/micromark-util-encode": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.0.tgz",
+      "integrity": "sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==",
+      "funding": [
+        {
+          "type": "GitHub Sponsors",
+          "url": "https://github.com/sponsors/unifiedjs"
+        },
+        {
+          "type": "OpenCollective",
+          "url": "https://opencollective.com/unified"
+        }
+      ],
+      "license": "MIT"
+    },
+    "node_modules/micromark-util-html-tag-name": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.0.tgz",
+      "integrity": "sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw==",
+      "funding": [
+        {
+          "type": "GitHub Sponsors",
+          "url": "https://github.com/sponsors/unifiedjs"
+        },
+        {
+          "type": "OpenCollective",
+          "url": "https://opencollective.com/unified"
+        }
+      ],
+      "license": "MIT"
+    },
+    "node_modules/micromark-util-normalize-identifier": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.0.tgz",
+      "integrity": "sha512-2xhYT0sfo85FMrUPtHcPo2rrp1lwbDEEzpx7jiH2xXJLqBuy4H0GgXk5ToU8IEwoROtXuL8ND0ttVa4rNqYK3w==",
+      "funding": [
+        {
+          "type": "GitHub Sponsors",
+          "url": "https://github.com/sponsors/unifiedjs"
+        },
+        {
+          "type": "OpenCollective",
+          "url": "https://opencollective.com/unified"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "micromark-util-symbol": "^2.0.0"
+      }
+    },
+    "node_modules/micromark-util-resolve-all": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.0.tgz",
+      "integrity": "sha512-6KU6qO7DZ7GJkaCgwBNtplXCvGkJToU86ybBAUdavvgsCiG8lSSvYxr9MhwmQ+udpzywHsl4RpGJsYWG1pDOcA==",
+      "funding": [
+        {
+          "type": "GitHub Sponsors",
+          "url": "https://github.com/sponsors/unifiedjs"
+        },
+        {
+          "type": "OpenCollective",
+          "url": "https://opencollective.com/unified"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "micromark-util-types": "^2.0.0"
+      }
+    },
+    "node_modules/micromark-util-sanitize-uri": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.0.tgz",
+      "integrity": "sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==",
+      "funding": [
+        {
+          "type": "GitHub Sponsors",
+          "url": "https://github.com/sponsors/unifiedjs"
+        },
+        {
+          "type": "OpenCollective",
+          "url": "https://opencollective.com/unified"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "micromark-util-character": "^2.0.0",
+        "micromark-util-encode": "^2.0.0",
+        "micromark-util-symbol": "^2.0.0"
+      }
+    },
+    "node_modules/micromark-util-subtokenize": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.1.tgz",
+      "integrity": "sha512-jZNtiFl/1aY73yS3UGQkutD0UbhTt68qnRpw2Pifmz5wV9h8gOVsN70v+Lq/f1rKaU/W8pxRe8y8Q9FX1AOe1Q==",
+      "funding": [
+        {
+          "type": "GitHub Sponsors",
+          "url": "https://github.com/sponsors/unifiedjs"
+        },
+        {
+          "type": "OpenCollective",
+          "url": "https://opencollective.com/unified"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "devlop": "^1.0.0",
+        "micromark-util-chunked": "^2.0.0",
+        "micromark-util-symbol": "^2.0.0",
+        "micromark-util-types": "^2.0.0"
+      }
+    },
+    "node_modules/micromark-util-symbol": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz",
+      "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==",
+      "funding": [
+        {
+          "type": "GitHub Sponsors",
+          "url": "https://github.com/sponsors/unifiedjs"
+        },
+        {
+          "type": "OpenCollective",
+          "url": "https://opencollective.com/unified"
+        }
+      ],
+      "license": "MIT"
+    },
+    "node_modules/micromark-util-types": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.0.tgz",
+      "integrity": "sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==",
+      "funding": [
+        {
+          "type": "GitHub Sponsors",
+          "url": "https://github.com/sponsors/unifiedjs"
+        },
+        {
+          "type": "OpenCollective",
+          "url": "https://opencollective.com/unified"
+        }
+      ],
+      "license": "MIT"
+    },
+    "node_modules/ms": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+      "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+      "license": "MIT"
+    },
+    "node_modules/nanoid": {
+      "version": "3.3.7",
+      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
+      "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "license": "MIT",
+      "bin": {
+        "nanoid": "bin/nanoid.cjs"
+      },
+      "engines": {
+        "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+      }
+    },
+    "node_modules/nth-check": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
+      "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "boolbase": "^1.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/nth-check?sponsor=1"
+      }
+    },
+    "node_modules/object-assign": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+      "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/parse-entities": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.1.tgz",
+      "integrity": "sha512-SWzvYcSJh4d/SGLIOQfZ/CoNv6BTlI6YEQ7Nj82oDVnRpwe/Z/F1EMx42x3JAOwGBlCjeCH0BRJQbQ/opHL17w==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/unist": "^2.0.0",
+        "character-entities": "^2.0.0",
+        "character-entities-legacy": "^3.0.0",
+        "character-reference-invalid": "^2.0.0",
+        "decode-named-character-reference": "^1.0.0",
+        "is-alphanumerical": "^2.0.0",
+        "is-decimal": "^2.0.0",
+        "is-hexadecimal": "^2.0.0"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/wooorm"
+      }
+    },
+    "node_modules/parse-entities/node_modules/@types/unist": {
+      "version": "2.0.11",
+      "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz",
+      "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==",
+      "license": "MIT"
+    },
+    "node_modules/parse-numeric-range": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/parse-numeric-range/-/parse-numeric-range-1.3.0.tgz",
+      "integrity": "sha512-twN+njEipszzlMJd4ONUYgSfZPDxgHhT9Ahed5uTigpQn90FggW4SA/AIPq/6a149fTbE9qBEcSwE3FAEp6wQQ==",
+      "license": "ISC"
+    },
+    "node_modules/parse5": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.0.tgz",
+      "integrity": "sha512-ZkDsAOcxsUMZ4Lz5fVciOehNcJ+Gb8gTzcA4yl3wnc273BAybYWrQ+Ks/OjCjSEpjvQkDSeZbybK9qj2VHHdGA==",
+      "license": "MIT",
+      "dependencies": {
+        "entities": "^4.5.0"
+      },
+      "funding": {
+        "url": "https://github.com/inikulin/parse5?sponsor=1"
+      }
+    },
+    "node_modules/picocolors": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+      "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/postcss": {
+      "version": "8.4.47",
+      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz",
+      "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/postcss/"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/postcss"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "nanoid": "^3.3.7",
+        "picocolors": "^1.1.0",
+        "source-map-js": "^1.2.1"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >=14"
+      }
+    },
+    "node_modules/prop-types": {
+      "version": "15.8.1",
+      "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
+      "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "loose-envify": "^1.4.0",
+        "object-assign": "^4.1.1",
+        "react-is": "^16.13.1"
+      }
+    },
+    "node_modules/property-information": {
+      "version": "6.5.0",
+      "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz",
+      "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==",
+      "license": "MIT",
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/wooorm"
+      }
+    },
+    "node_modules/react": {
+      "version": "18.3.1",
+      "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
+      "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
+      "license": "MIT",
+      "dependencies": {
+        "loose-envify": "^1.1.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/react-cookie": {
+      "version": "7.2.1",
+      "resolved": "https://registry.npmjs.org/react-cookie/-/react-cookie-7.2.1.tgz",
+      "integrity": "sha512-UcE6njAy5UmKzU9RywIvvtbHIQydhh6JP55yVbGwQ8Tp/wSPp2HOYVZdRI55zfbkW6zQv9SEs42mS3a9cIUQ0g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/hoist-non-react-statics": "^3.3.5",
+        "hoist-non-react-statics": "^3.3.2",
+        "universal-cookie": "^7.0.0"
+      },
+      "peerDependencies": {
+        "react": ">= 16.3.0"
+      }
+    },
+    "node_modules/react-dom": {
+      "version": "18.3.1",
+      "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
+      "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
+      "license": "MIT",
+      "dependencies": {
+        "loose-envify": "^1.1.0",
+        "scheduler": "^0.23.2"
+      },
+      "peerDependencies": {
+        "react": "^18.3.1"
+      }
+    },
+    "node_modules/react-is": {
+      "version": "16.13.1",
+      "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+      "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/react-markdown": {
+      "version": "9.0.1",
+      "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-9.0.1.tgz",
+      "integrity": "sha512-186Gw/vF1uRkydbsOIkcGXw7aHq0sZOCRFFjGrr7b9+nVZg4UfA4enXCaxm4fUzecU38sWfrNDitGhshuU7rdg==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/hast": "^3.0.0",
+        "devlop": "^1.0.0",
+        "hast-util-to-jsx-runtime": "^2.0.0",
+        "html-url-attributes": "^3.0.0",
+        "mdast-util-to-hast": "^13.0.0",
+        "remark-parse": "^11.0.0",
+        "remark-rehype": "^11.0.0",
+        "unified": "^11.0.0",
+        "unist-util-visit": "^5.0.0",
+        "vfile": "^6.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      },
+      "peerDependencies": {
+        "@types/react": ">=18",
+        "react": ">=18"
+      }
+    },
+    "node_modules/react-toggle": {
+      "version": "4.1.3",
+      "resolved": "https://registry.npmjs.org/react-toggle/-/react-toggle-4.1.3.tgz",
+      "integrity": "sha512-WoPrvbwfQSvoagbrDnXPrlsxwzuhQIrs+V0I162j/s+4XPgY/YDAUmHSeWiroznfI73wj+MBydvW95zX8ABbSg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "classnames": "^2.2.5"
+      },
+      "peerDependencies": {
+        "prop-types": ">= 15.3.0 < 19",
+        "react": ">= 15.3.0 < 19",
+        "react-dom": ">= 15.3.0 < 19"
+      }
+    },
+    "node_modules/refractor": {
+      "version": "4.8.1",
+      "resolved": "https://registry.npmjs.org/refractor/-/refractor-4.8.1.tgz",
+      "integrity": "sha512-/fk5sI0iTgFYlmVGYVew90AoYnNMP6pooClx/XKqyeeCQXrL0Kvgn8V0VEht5ccdljbzzF1i3Q213gcntkRExg==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/hast": "^2.0.0",
+        "@types/prismjs": "^1.0.0",
+        "hastscript": "^7.0.0",
+        "parse-entities": "^4.0.0"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/wooorm"
+      }
+    },
+    "node_modules/refractor/node_modules/@types/hast": {
+      "version": "2.3.10",
+      "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.10.tgz",
+      "integrity": "sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/unist": "^2"
+      }
+    },
+    "node_modules/refractor/node_modules/@types/unist": {
+      "version": "2.0.11",
+      "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz",
+      "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==",
+      "license": "MIT"
+    },
+    "node_modules/regenerator-runtime": {
+      "version": "0.14.1",
+      "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
+      "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==",
+      "license": "MIT"
+    },
+    "node_modules/rehype-attr": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/rehype-attr/-/rehype-attr-3.0.3.tgz",
+      "integrity": "sha512-Up50Xfra8tyxnkJdCzLBIBtxOcB2M1xdeKe1324U06RAvSjYm7ULSeoM+b/nYPQPVd7jsXJ9+39IG1WAJPXONw==",
+      "license": "MIT",
+      "dependencies": {
+        "unified": "~11.0.0",
+        "unist-util-visit": "~5.0.0"
+      },
+      "engines": {
+        "node": ">=16"
+      },
+      "funding": {
+        "url": "https://jaywcjlove.github.io/#/sponsor"
+      }
+    },
+    "node_modules/rehype-autolink-headings": {
+      "version": "7.1.0",
+      "resolved": "https://registry.npmjs.org/rehype-autolink-headings/-/rehype-autolink-headings-7.1.0.tgz",
+      "integrity": "sha512-rItO/pSdvnvsP4QRB1pmPiNHUskikqtPojZKJPPPAVx9Hj8i8TwMBhofrrAYRhYOOBZH9tgmG5lPqDLuIWPWmw==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/hast": "^3.0.0",
+        "@ungap/structured-clone": "^1.0.0",
+        "hast-util-heading-rank": "^3.0.0",
+        "hast-util-is-element": "^3.0.0",
+        "unified": "^11.0.0",
+        "unist-util-visit": "^5.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/rehype-ignore": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/rehype-ignore/-/rehype-ignore-2.0.2.tgz",
+      "integrity": "sha512-BpAT/3lU9DMJ2siYVD/dSR0A/zQgD6Fb+fxkJd4j+wDVy6TYbYpK+FZqu8eM9EuNKGvi4BJR7XTZ/+zF02Dq8w==",
+      "license": "MIT",
+      "dependencies": {
+        "hast-util-select": "^6.0.0",
+        "unified": "^11.0.0",
+        "unist-util-visit": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=16"
+      },
+      "funding": {
+        "url": "https://jaywcjlove.github.io/#/sponsor"
+      }
+    },
+    "node_modules/rehype-parse": {
+      "version": "9.0.1",
+      "resolved": "https://registry.npmjs.org/rehype-parse/-/rehype-parse-9.0.1.tgz",
+      "integrity": "sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/hast": "^3.0.0",
+        "hast-util-from-html": "^2.0.0",
+        "unified": "^11.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/rehype-prism-plus": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/rehype-prism-plus/-/rehype-prism-plus-2.0.0.tgz",
+      "integrity": "sha512-FeM/9V2N7EvDZVdR2dqhAzlw5YI49m9Tgn7ZrYJeYHIahM6gcXpH0K1y2gNnKanZCydOMluJvX2cB9z3lhY8XQ==",
+      "license": "MIT",
+      "dependencies": {
+        "hast-util-to-string": "^3.0.0",
+        "parse-numeric-range": "^1.3.0",
+        "refractor": "^4.8.0",
+        "rehype-parse": "^9.0.0",
+        "unist-util-filter": "^5.0.0",
+        "unist-util-visit": "^5.0.0"
+      }
+    },
+    "node_modules/rehype-raw": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/rehype-raw/-/rehype-raw-7.0.0.tgz",
+      "integrity": "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/hast": "^3.0.0",
+        "hast-util-raw": "^9.0.0",
+        "vfile": "^6.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/rehype-rewrite": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/rehype-rewrite/-/rehype-rewrite-4.0.2.tgz",
+      "integrity": "sha512-rjLJ3z6fIV11phwCqHp/KRo8xuUCO8o9bFJCNw5o6O2wlLk6g8r323aRswdGBQwfXPFYeSuZdAjp4tzo6RGqEg==",
+      "license": "MIT",
+      "dependencies": {
+        "hast-util-select": "^6.0.0",
+        "unified": "^11.0.3",
+        "unist-util-visit": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=16.0.0"
+      },
+      "funding": {
+        "url": "https://jaywcjlove.github.io/#/sponsor"
+      }
+    },
+    "node_modules/rehype-slug": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/rehype-slug/-/rehype-slug-6.0.0.tgz",
+      "integrity": "sha512-lWyvf/jwu+oS5+hL5eClVd3hNdmwM1kAC0BUvEGD19pajQMIzcNUd/k9GsfQ+FfECvX+JE+e9/btsKH0EjJT6A==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/hast": "^3.0.0",
+        "github-slugger": "^2.0.0",
+        "hast-util-heading-rank": "^3.0.0",
+        "hast-util-to-string": "^3.0.0",
+        "unist-util-visit": "^5.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/remark-gfm": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.0.tgz",
+      "integrity": "sha512-U92vJgBPkbw4Zfu/IiW2oTZLSL3Zpv+uI7My2eq8JxKgqraFdU8YUGicEJCEgSbeaG+QDFqIcwwfMTOEelPxuA==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/mdast": "^4.0.0",
+        "mdast-util-gfm": "^3.0.0",
+        "micromark-extension-gfm": "^3.0.0",
+        "remark-parse": "^11.0.0",
+        "remark-stringify": "^11.0.0",
+        "unified": "^11.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/remark-github-blockquote-alert": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/remark-github-blockquote-alert/-/remark-github-blockquote-alert-1.2.1.tgz",
+      "integrity": "sha512-qNf2mSAoZgh3Cl23/9Y1L7S4Kbf9NsdHvYK398ab/52yEsDPDU5I4cuTcgDRrdIX7Ltc6RK+KCLRtWkbFnL6Dg==",
+      "license": "MIT",
+      "dependencies": {
+        "unist-util-visit": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=16"
+      },
+      "funding": {
+        "url": "https://jaywcjlove.github.io/#/sponsor"
+      }
+    },
+    "node_modules/remark-parse": {
+      "version": "11.0.0",
+      "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz",
+      "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/mdast": "^4.0.0",
+        "mdast-util-from-markdown": "^2.0.0",
+        "micromark-util-types": "^2.0.0",
+        "unified": "^11.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/remark-rehype": {
+      "version": "11.1.1",
+      "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.1.tgz",
+      "integrity": "sha512-g/osARvjkBXb6Wo0XvAeXQohVta8i84ACbenPpoSsxTOQH/Ae0/RGP4WZgnMH5pMLpsj4FG7OHmcIcXxpza8eQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/hast": "^3.0.0",
+        "@types/mdast": "^4.0.0",
+        "mdast-util-to-hast": "^13.0.0",
+        "unified": "^11.0.0",
+        "vfile": "^6.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/remark-stringify": {
+      "version": "11.0.0",
+      "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz",
+      "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/mdast": "^4.0.0",
+        "mdast-util-to-markdown": "^2.0.0",
+        "unified": "^11.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/rollup": {
+      "version": "4.24.0",
+      "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.0.tgz",
+      "integrity": "sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/estree": "1.0.6"
+      },
+      "bin": {
+        "rollup": "dist/bin/rollup"
+      },
+      "engines": {
+        "node": ">=18.0.0",
+        "npm": ">=8.0.0"
+      },
+      "optionalDependencies": {
+        "@rollup/rollup-android-arm-eabi": "4.24.0",
+        "@rollup/rollup-android-arm64": "4.24.0",
+        "@rollup/rollup-darwin-arm64": "4.24.0",
+        "@rollup/rollup-darwin-x64": "4.24.0",
+        "@rollup/rollup-linux-arm-gnueabihf": "4.24.0",
+        "@rollup/rollup-linux-arm-musleabihf": "4.24.0",
+        "@rollup/rollup-linux-arm64-gnu": "4.24.0",
+        "@rollup/rollup-linux-arm64-musl": "4.24.0",
+        "@rollup/rollup-linux-powerpc64le-gnu": "4.24.0",
+        "@rollup/rollup-linux-riscv64-gnu": "4.24.0",
+        "@rollup/rollup-linux-s390x-gnu": "4.24.0",
+        "@rollup/rollup-linux-x64-gnu": "4.24.0",
+        "@rollup/rollup-linux-x64-musl": "4.24.0",
+        "@rollup/rollup-win32-arm64-msvc": "4.24.0",
+        "@rollup/rollup-win32-ia32-msvc": "4.24.0",
+        "@rollup/rollup-win32-x64-msvc": "4.24.0",
+        "fsevents": "~2.3.2"
+      }
+    },
+    "node_modules/scheduler": {
+      "version": "0.23.2",
+      "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
+      "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
+      "license": "MIT",
+      "dependencies": {
+        "loose-envify": "^1.1.0"
+      }
+    },
+    "node_modules/source-map-js": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+      "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/space-separated-tokens": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz",
+      "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==",
+      "license": "MIT",
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/wooorm"
+      }
+    },
+    "node_modules/stringify-entities": {
+      "version": "4.0.4",
+      "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz",
+      "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==",
+      "license": "MIT",
+      "dependencies": {
+        "character-entities-html4": "^2.0.0",
+        "character-entities-legacy": "^3.0.0"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/wooorm"
+      }
+    },
+    "node_modules/style-to-object": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.8.tgz",
+      "integrity": "sha512-xT47I/Eo0rwJmaXC4oilDGDWLohVhR6o/xAQcPQN8q6QBuZVL8qMYL85kLmST5cPjAorwvqIA4qXTRQoYHaL6g==",
+      "license": "MIT",
+      "dependencies": {
+        "inline-style-parser": "0.2.4"
+      }
+    },
+    "node_modules/trim-lines": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz",
+      "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==",
+      "license": "MIT",
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/wooorm"
+      }
+    },
+    "node_modules/trough": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz",
+      "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==",
+      "license": "MIT",
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/wooorm"
+      }
+    },
+    "node_modules/typescript": {
+      "version": "5.6.3",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz",
+      "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "bin": {
+        "tsc": "bin/tsc",
+        "tsserver": "bin/tsserver"
+      },
+      "engines": {
+        "node": ">=14.17"
+      }
+    },
+    "node_modules/unified": {
+      "version": "11.0.5",
+      "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz",
+      "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/unist": "^3.0.0",
+        "bail": "^2.0.0",
+        "devlop": "^1.0.0",
+        "extend": "^3.0.0",
+        "is-plain-obj": "^4.0.0",
+        "trough": "^2.0.0",
+        "vfile": "^6.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/unist-util-filter": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/unist-util-filter/-/unist-util-filter-5.0.1.tgz",
+      "integrity": "sha512-pHx7D4Zt6+TsfwylH9+lYhBhzyhEnCXs/lbq/Hstxno5z4gVdyc2WEW0asfjGKPyG4pEKrnBv5hdkO6+aRnQJw==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/unist": "^3.0.0",
+        "unist-util-is": "^6.0.0",
+        "unist-util-visit-parents": "^6.0.0"
+      }
+    },
+    "node_modules/unist-util-is": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz",
+      "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/unist": "^3.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/unist-util-position": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz",
+      "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/unist": "^3.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/unist-util-stringify-position": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz",
+      "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/unist": "^3.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/unist-util-visit": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz",
+      "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/unist": "^3.0.0",
+        "unist-util-is": "^6.0.0",
+        "unist-util-visit-parents": "^6.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/unist-util-visit-parents": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz",
+      "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/unist": "^3.0.0",
+        "unist-util-is": "^6.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/universal-cookie": {
+      "version": "7.2.1",
+      "resolved": "https://registry.npmjs.org/universal-cookie/-/universal-cookie-7.2.1.tgz",
+      "integrity": "sha512-GEKneQ0sz8qbobkYM5s9elAx6l7GQDNVl3Siqmc7bt/YccyyXWDPn+fht3J1qMcaLwPrzkty3i+dNfPGP2/9hA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/cookie": "^0.6.0",
+        "cookie": "^0.7.2"
+      }
+    },
+    "node_modules/vfile": {
+      "version": "6.0.3",
+      "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz",
+      "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/unist": "^3.0.0",
+        "vfile-message": "^4.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/vfile-location": {
+      "version": "5.0.3",
+      "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.3.tgz",
+      "integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/unist": "^3.0.0",
+        "vfile": "^6.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/vfile-message": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz",
+      "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/unist": "^3.0.0",
+        "unist-util-stringify-position": "^4.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/vite": {
+      "version": "5.4.9",
+      "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.9.tgz",
+      "integrity": "sha512-20OVpJHh0PAM0oSOELa5GaZNWeDjcAvQjGXy2Uyr+Tp+/D2/Hdz6NLgpJLsarPTA2QJ6v8mX2P1ZfbsSKvdMkg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "esbuild": "^0.21.3",
+        "postcss": "^8.4.43",
+        "rollup": "^4.20.0"
+      },
+      "bin": {
+        "vite": "bin/vite.js"
+      },
+      "engines": {
+        "node": "^18.0.0 || >=20.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/vitejs/vite?sponsor=1"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.3.3"
+      },
+      "peerDependencies": {
+        "@types/node": "^18.0.0 || >=20.0.0",
+        "less": "*",
+        "lightningcss": "^1.21.0",
+        "sass": "*",
+        "sass-embedded": "*",
+        "stylus": "*",
+        "sugarss": "*",
+        "terser": "^5.4.0"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        },
+        "less": {
+          "optional": true
+        },
+        "lightningcss": {
+          "optional": true
+        },
+        "sass": {
+          "optional": true
+        },
+        "sass-embedded": {
+          "optional": true
+        },
+        "stylus": {
+          "optional": true
+        },
+        "sugarss": {
+          "optional": true
+        },
+        "terser": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/web-namespaces": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz",
+      "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==",
+      "license": "MIT",
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/wooorm"
+      }
+    },
+    "node_modules/zwitch": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz",
+      "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==",
+      "license": "MIT",
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/wooorm"
+      }
+    }
+  }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..fa7913b
--- /dev/null
+++ b/package.json
@@ -0,0 +1,23 @@
+{
+  "devDependencies": {
+    "@mdi/js": "^7.4.47",
+    "@mdi/react": "^1.6.1",
+    "@types/react": "^18.2.0",
+    "@types/react-toggle": "^4.0.5",
+    "react": "^18.3.1",
+    "react-cookie": "^7.2.1",
+    "react-dom": "^18.3.1",
+    "react-toggle": "^4.1.3",
+    "typescript": "^5.6.3",
+    "vite": "^5.4.9"
+  },
+  "scripts": {
+    "dev": "vite",
+    "build": "vite build",
+    "preview": "vite preview"
+  },
+  "dependencies": {
+    "@ramonak/react-progress-bar": "^5.3.0",
+    "@uiw/react-markdown-preview": "^5.1.3"
+  }
+}
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..231dd12
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,15 @@
+{
+  "compilerOptions": {
+    "target": "es2020",
+    "module": "commonjs",
+    "esModuleInterop": true,
+    "forceConsistentCasingInFileNames": true,
+    "strict": true,
+    "skipLibCheck": true,
+    "lib": [
+      "es2020",
+      "dom"
+    ],
+    "jsx": "react-jsx"
+  }
+}
diff --git a/vite.config.js b/vite.config.js
new file mode 100644
index 0000000..267cee6
--- /dev/null
+++ b/vite.config.js
@@ -0,0 +1,9 @@
+// vite.config.js
+import { defineConfig } from 'vite'
+
+export default defineConfig({
+    server: {
+        port: '8080'
+    },
+    root: 'Website'
+})
\ No newline at end of file