diff --git a/configs/webpack.config.main.ts b/configs/webpack.config.main.ts
index 8222c6fc..1829f96a 100644
--- a/configs/webpack.config.main.ts
+++ b/configs/webpack.config.main.ts
@@ -5,6 +5,9 @@ export const config: webpack.Configuration = {
   mode: 'development',
   entry: join(__dirname, '../src/client-electron/main/main.ts'),
   target: 'electron-main',
+  resolve: {
+    extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'],
+  },
   output: {
     path: join(__dirname, '../dist'),
     filename: 'main.js',
diff --git a/configs/webpack.config.renderer.ts b/configs/webpack.config.renderer.ts
index 0fbd2539..19b30bc5 100644
--- a/configs/webpack.config.renderer.ts
+++ b/configs/webpack.config.renderer.ts
@@ -1,4 +1,5 @@
 import HtmlWebpackPlugin from 'html-webpack-plugin';
+import { TsconfigPathsPlugin } from 'tsconfig-paths-webpack-plugin';
 import { join } from 'path';
 import webpack from 'webpack';
 
@@ -12,6 +13,7 @@ export const config: webpack.Configuration = {
   },
   resolve: {
     extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'],
+    plugins: [new TsconfigPathsPlugin()],
   },
   module: {
     rules: [
@@ -20,10 +22,6 @@ export const config: webpack.Configuration = {
         exclude: /node_modules/,
         loader: 'ts-loader',
       },
-      {
-        test: /\.s[ac]ss$/,
-        use: ['style-loader', 'css-loader', 'sass-loader'],
-      },
       {
         test: /\.ttf$/,
         loader: 'file-loader',
diff --git a/package-lock.json b/package-lock.json
index fcd732ef..3a0b6837 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -14,7 +14,6 @@
       "version": "7.12.11",
       "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz",
       "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==",
-      "dev": true,
       "requires": {
         "@babel/highlight": "^7.10.4"
       }
@@ -54,7 +53,6 @@
       "version": "7.12.11",
       "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.11.tgz",
       "integrity": "sha512-Ggg6WPOJtSi8yYQvLVjG8F/TlpWDlKx0OpS4Kt+xMQPs5OaGYWy+v1A+1TvxI6sAMGZpKWWoAQ1DaeQbImlItA==",
-      "dev": true,
       "requires": {
         "@babel/types": "^7.12.11",
         "jsesc": "^2.5.1",
@@ -64,8 +62,26 @@
         "source-map": {
           "version": "0.5.7",
           "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
-          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
-          "dev": true
+          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
+        }
+      }
+    },
+    "@babel/helper-annotate-as-pure": {
+      "version": "7.12.13",
+      "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.12.13.tgz",
+      "integrity": "sha512-7YXfX5wQ5aYM/BOlbSccHDbuXXFPxeoUmfWtz8le2yTkTZc+BxsiEnENFoi2SlmA8ewDkG2LgIMIVzzn2h8kfw==",
+      "requires": {
+        "@babel/types": "^7.12.13"
+      },
+      "dependencies": {
+        "@babel/types": {
+          "version": "7.13.17",
+          "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.17.tgz",
+          "integrity": "sha512-RawydLgxbOPDlTLJNtoIypwdmAy//uQIzlKt2+iBiJaRlVuI6QLUxVAyWGNfOzp8Yu4L4lLIacoCyTNtpb4wiA==",
+          "requires": {
+            "@babel/helper-validator-identifier": "^7.12.11",
+            "to-fast-properties": "^2.0.0"
+          }
         }
       }
     },
@@ -73,7 +89,6 @@
       "version": "7.12.11",
       "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.11.tgz",
       "integrity": "sha512-AtQKjtYNolKNi6nNNVLQ27CP6D9oFR6bq/HPYSizlzbp7uC1M59XJe8L+0uXjbIaZaUJF99ruHqVGiKXU/7ybA==",
-      "dev": true,
       "requires": {
         "@babel/helper-get-function-arity": "^7.12.10",
         "@babel/template": "^7.12.7",
@@ -84,7 +99,6 @@
       "version": "7.12.10",
       "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.10.tgz",
       "integrity": "sha512-mm0n5BPjR06wh9mPQaDdXWDoll/j5UpCAPl1x8fS71GHm7HA6Ua2V4ylG1Ju8lvcTOietbPNNPaSilKj+pj+Ag==",
-      "dev": true,
       "requires": {
         "@babel/types": "^7.12.10"
       }
@@ -102,7 +116,6 @@
       "version": "7.12.5",
       "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz",
       "integrity": "sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA==",
-      "dev": true,
       "requires": {
         "@babel/types": "^7.12.5"
       }
@@ -164,7 +177,6 @@
       "version": "7.12.11",
       "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.11.tgz",
       "integrity": "sha512-LsIVN8j48gHgwzfocYUSkO/hjYAOJqlpJEc7tGXcIm4cubjVUf8LGW6eWRyxEu7gA25q02p0rQUWoCI33HNS5g==",
-      "dev": true,
       "requires": {
         "@babel/types": "^7.12.11"
       }
@@ -172,8 +184,7 @@
     "@babel/helper-validator-identifier": {
       "version": "7.12.11",
       "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz",
-      "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==",
-      "dev": true
+      "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw=="
     },
     "@babel/helpers": {
       "version": "7.12.5",
@@ -190,7 +201,6 @@
       "version": "7.10.4",
       "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz",
       "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==",
-      "dev": true,
       "requires": {
         "@babel/helper-validator-identifier": "^7.10.4",
         "chalk": "^2.0.0",
@@ -201,7 +211,6 @@
           "version": "3.2.1",
           "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
           "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
-          "dev": true,
           "requires": {
             "color-convert": "^1.9.0"
           }
@@ -210,7 +219,6 @@
           "version": "2.4.2",
           "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
           "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
-          "dev": true,
           "requires": {
             "ansi-styles": "^3.2.1",
             "escape-string-regexp": "^1.0.5",
@@ -221,7 +229,6 @@
           "version": "1.9.3",
           "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
           "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
-          "dev": true,
           "requires": {
             "color-name": "1.1.3"
           }
@@ -229,20 +236,17 @@
         "color-name": {
           "version": "1.1.3",
           "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-          "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
-          "dev": true
+          "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
         },
         "has-flag": {
           "version": "3.0.0",
           "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-          "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
-          "dev": true
+          "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
         },
         "supports-color": {
           "version": "5.5.0",
           "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
           "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
-          "dev": true,
           "requires": {
             "has-flag": "^3.0.0"
           }
@@ -252,8 +256,7 @@
     "@babel/parser": {
       "version": "7.12.11",
       "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.11.tgz",
-      "integrity": "sha512-N3UxG+uuF4CMYoNj8AhnbAcJF0PiuJ9KHuy1lQmkYsxTer/MAH9UBNHsBoAX/4s6NvlDD047No8mYVGGzLL4hg==",
-      "dev": true
+      "integrity": "sha512-N3UxG+uuF4CMYoNj8AhnbAcJF0PiuJ9KHuy1lQmkYsxTer/MAH9UBNHsBoAX/4s6NvlDD047No8mYVGGzLL4hg=="
     },
     "@babel/plugin-syntax-async-generators": {
       "version": "7.8.4",
@@ -367,7 +370,6 @@
       "version": "7.12.7",
       "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.7.tgz",
       "integrity": "sha512-GkDzmHS6GV7ZeXfJZ0tLRBhZcMcY0/Lnb+eEbXDBfCAcZCjrZKe6p3J4we/D24O9Y8enxWAg1cWwof59yLh2ow==",
-      "dev": true,
       "requires": {
         "@babel/code-frame": "^7.10.4",
         "@babel/parser": "^7.12.7",
@@ -378,7 +380,6 @@
       "version": "7.12.12",
       "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.12.tgz",
       "integrity": "sha512-s88i0X0lPy45RrLM8b9mz8RPH5FqO9G9p7ti59cToE44xFm1Q+Pjh5Gq4SXBbtb88X7Uy7pexeqRIQDDMNkL0w==",
-      "dev": true,
       "requires": {
         "@babel/code-frame": "^7.12.11",
         "@babel/generator": "^7.12.11",
@@ -395,7 +396,6 @@
       "version": "7.12.12",
       "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.12.tgz",
       "integrity": "sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ==",
-      "dev": true,
       "requires": {
         "@babel/helper-validator-identifier": "^7.12.11",
         "lodash": "^4.17.19",
@@ -489,6 +489,29 @@
         "fs-extra": "^9.0.1"
       }
     },
+    "@emotion/is-prop-valid": {
+      "version": "0.8.8",
+      "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz",
+      "integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==",
+      "requires": {
+        "@emotion/memoize": "0.7.4"
+      }
+    },
+    "@emotion/memoize": {
+      "version": "0.7.4",
+      "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz",
+      "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw=="
+    },
+    "@emotion/stylis": {
+      "version": "0.8.5",
+      "resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz",
+      "integrity": "sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ=="
+    },
+    "@emotion/unitless": {
+      "version": "0.7.5",
+      "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz",
+      "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg=="
+    },
     "@istanbuljs/load-nyc-config": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
@@ -1091,6 +1114,16 @@
         "@types/node": "*"
       }
     },
+    "@types/hoist-non-react-statics": {
+      "version": "3.3.1",
+      "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz",
+      "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==",
+      "dev": true,
+      "requires": {
+        "@types/react": "*",
+        "hoist-non-react-statics": "^3.3.0"
+      }
+    },
     "@types/html-minifier-terser": {
       "version": "5.1.1",
       "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz",
@@ -1265,6 +1298,17 @@
       "integrity": "sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==",
       "dev": true
     },
+    "@types/styled-components": {
+      "version": "5.1.9",
+      "resolved": "https://registry.npmjs.org/@types/styled-components/-/styled-components-5.1.9.tgz",
+      "integrity": "sha512-kbEG6YlwK8rucITpKEr6pA4Ho9KSQHUUOzZ9lY3va1mtcjvS3D0wDciFyHEiNHKLL/npZCKDQJqm0x44sPO9oA==",
+      "dev": true,
+      "requires": {
+        "@types/hoist-non-react-statics": "*",
+        "@types/react": "*",
+        "csstype": "^3.0.2"
+      }
+    },
     "@types/through": {
       "version": "0.0.30",
       "resolved": "https://registry.npmjs.org/@types/through/-/through-0.0.30.tgz",
@@ -1274,6 +1318,12 @@
         "@types/node": "*"
       }
     },
+    "@types/uuid": {
+      "version": "8.3.0",
+      "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.0.tgz",
+      "integrity": "sha512-eQ9qFW/fhfGJF8WKHGEHZEyVWfZxrT+6CLIJGBcZPfxUh/+BnEj+UCGYMlr9qZuX/2AltsvwrGqp0LhEW8D0zQ==",
+      "dev": true
+    },
     "@types/verror": {
       "version": "1.10.4",
       "resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.4.tgz",
@@ -1553,6 +1603,32 @@
         "uri-js": "^4.2.2"
       }
     },
+    "ajv-formats": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.0.2.tgz",
+      "integrity": "sha512-Brah4Uo5/U8v76c6euTwtjVFFaVishwnJrQBYpev1JRh4vjA1F4HY3UzQez41YUCszUCXKagG8v6eVRBHV1gkw==",
+      "requires": {
+        "ajv": "^8.0.0"
+      },
+      "dependencies": {
+        "ajv": {
+          "version": "8.2.0",
+          "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.2.0.tgz",
+          "integrity": "sha512-WSNGFuyWd//XO8n/m/EaOlNLtO0yL8EXT/74LqT4khdhpZjP7lkj/kT5uwRmGitKEVp/Oj7ZUHeGfPtgHhQ5CA==",
+          "requires": {
+            "fast-deep-equal": "^3.1.1",
+            "json-schema-traverse": "^1.0.0",
+            "require-from-string": "^2.0.2",
+            "uri-js": "^4.2.2"
+          }
+        },
+        "json-schema-traverse": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+          "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="
+        }
+      }
+    },
     "ajv-keywords": {
       "version": "3.5.2",
       "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
@@ -2023,6 +2099,11 @@
       "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
       "dev": true
     },
+    "atomically": {
+      "version": "1.7.0",
+      "resolved": "https://registry.npmjs.org/atomically/-/atomically-1.7.0.tgz",
+      "integrity": "sha512-Xcz9l0z7y9yQ9rdDaxlmaI4uJHf/T8g9hOEzJcsEqX2SjCj4J20uK7+ldkDHMbpJDK76wF7xEIgxc/vSlsfw5w=="
+    },
     "aws-sign2": {
       "version": "0.7.0",
       "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
@@ -2074,6 +2155,22 @@
         "@types/babel__traverse": "^7.0.6"
       }
     },
+    "babel-plugin-styled-components": {
+      "version": "1.12.0",
+      "resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-1.12.0.tgz",
+      "integrity": "sha512-FEiD7l5ZABdJPpLssKXjBUJMYqzbcNzBowfXDCdJhOpbhWiewapUaY+LZGT8R4Jg2TwOjGjG4RKeyrO5p9sBkA==",
+      "requires": {
+        "@babel/helper-annotate-as-pure": "^7.0.0",
+        "@babel/helper-module-imports": "^7.0.0",
+        "babel-plugin-syntax-jsx": "^6.18.0",
+        "lodash": "^4.17.11"
+      }
+    },
+    "babel-plugin-syntax-jsx": {
+      "version": "6.18.0",
+      "resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz",
+      "integrity": "sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY="
+    },
     "babel-preset-current-node-syntax": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz",
@@ -2548,6 +2645,11 @@
         "quick-lru": "^4.0.1"
       }
     },
+    "camelize": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz",
+      "integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs="
+    },
     "caniuse-lite": {
       "version": "1.0.30001216",
       "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001216.tgz",
@@ -3169,6 +3271,57 @@
         }
       }
     },
+    "conf": {
+      "version": "10.0.1",
+      "resolved": "https://registry.npmjs.org/conf/-/conf-10.0.1.tgz",
+      "integrity": "sha512-QClEoNcruwBL84QgMEPHibL3ERxWIrRKhbjJKG1VsFBadm5QpS0jsu4QjY/maxUvhyAKXeyrs+ws+lC6PajnEg==",
+      "requires": {
+        "ajv": "^8.1.0",
+        "ajv-formats": "^2.0.2",
+        "atomically": "^1.7.0",
+        "debounce-fn": "^4.0.0",
+        "dot-prop": "^6.0.1",
+        "env-paths": "^2.2.1",
+        "json-schema-typed": "^7.0.3",
+        "onetime": "^5.1.2",
+        "pkg-up": "^3.1.0",
+        "semver": "^7.3.5"
+      },
+      "dependencies": {
+        "ajv": {
+          "version": "8.2.0",
+          "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.2.0.tgz",
+          "integrity": "sha512-WSNGFuyWd//XO8n/m/EaOlNLtO0yL8EXT/74LqT4khdhpZjP7lkj/kT5uwRmGitKEVp/Oj7ZUHeGfPtgHhQ5CA==",
+          "requires": {
+            "fast-deep-equal": "^3.1.1",
+            "json-schema-traverse": "^1.0.0",
+            "require-from-string": "^2.0.2",
+            "uri-js": "^4.2.2"
+          }
+        },
+        "dot-prop": {
+          "version": "6.0.1",
+          "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz",
+          "integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==",
+          "requires": {
+            "is-obj": "^2.0.0"
+          }
+        },
+        "json-schema-traverse": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+          "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="
+        },
+        "semver": {
+          "version": "7.3.5",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
+          "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
+          "requires": {
+            "lru-cache": "^6.0.0"
+          }
+        }
+      }
+    },
     "config-chain": {
       "version": "1.1.12",
       "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.12.tgz",
@@ -3951,41 +4104,10 @@
       "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==",
       "dev": true
     },
-    "css-loader": {
-      "version": "5.2.4",
-      "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-5.2.4.tgz",
-      "integrity": "sha512-OFYGyINCKkdQsTrSYxzGSFnGS4gNjcXkKkQgWxK138jgnPt+lepxdjSZNc8sHAl5vP3DhsJUxufWIjOwI8PMMw==",
-      "dev": true,
-      "requires": {
-        "camelcase": "^6.2.0",
-        "icss-utils": "^5.1.0",
-        "loader-utils": "^2.0.0",
-        "postcss": "^8.2.10",
-        "postcss-modules-extract-imports": "^3.0.0",
-        "postcss-modules-local-by-default": "^4.0.0",
-        "postcss-modules-scope": "^3.0.0",
-        "postcss-modules-values": "^4.0.0",
-        "postcss-value-parser": "^4.1.0",
-        "schema-utils": "^3.0.0",
-        "semver": "^7.3.5"
-      },
-      "dependencies": {
-        "camelcase": {
-          "version": "6.2.0",
-          "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz",
-          "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==",
-          "dev": true
-        },
-        "semver": {
-          "version": "7.3.5",
-          "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
-          "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
-          "dev": true,
-          "requires": {
-            "lru-cache": "^6.0.0"
-          }
-        }
-      }
+    "css-color-keywords": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz",
+      "integrity": "sha1-/qJhbcZ2spYmhrOvjb2+GAskTgU="
     },
     "css-select": {
       "version": "2.1.0",
@@ -3999,18 +4121,22 @@
         "nth-check": "^1.0.2"
       }
     },
+    "css-to-react-native": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.0.0.tgz",
+      "integrity": "sha512-Ro1yETZA813eoyUp2GDBhG2j+YggidUmzO1/v9eYBKR2EHVEniE2MI/NqpTQ954BMpTPZFsGNPm46qFB9dpaPQ==",
+      "requires": {
+        "camelize": "^1.0.0",
+        "css-color-keywords": "^1.0.0",
+        "postcss-value-parser": "^4.0.2"
+      }
+    },
     "css-what": {
       "version": "3.4.2",
       "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz",
       "integrity": "sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==",
       "dev": true
     },
-    "cssesc": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
-      "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
-      "dev": true
-    },
     "cssom": {
       "version": "0.4.4",
       "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz",
@@ -4085,11 +4211,25 @@
       "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==",
       "dev": true
     },
+    "debounce-fn": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/debounce-fn/-/debounce-fn-4.0.0.tgz",
+      "integrity": "sha512-8pYCQiL9Xdcg0UPSD3d+0KMlOjp+KGU5EPwYddgzQ7DATsg4fuUDjQtsYLmWjnk2obnNHgV3vE2Y4jejSOJVBQ==",
+      "requires": {
+        "mimic-fn": "^3.0.0"
+      },
+      "dependencies": {
+        "mimic-fn": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-3.1.0.tgz",
+          "integrity": "sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ=="
+        }
+      }
+    },
     "debug": {
       "version": "4.3.1",
       "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
       "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
-      "dev": true,
       "requires": {
         "ms": "2.1.2"
       }
@@ -5134,6 +5274,22 @@
         "mime": "^2.5.0"
       }
     },
+    "electron-store": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/electron-store/-/electron-store-8.0.0.tgz",
+      "integrity": "sha512-ZgRPUZkfrrjWSqxZeaxu7lEvmYf6tgl49dLMqxXGnEmliSiwv3u4rJPG+mH3fBQP9PBqgSh4TCuxHZImMMUgWg==",
+      "requires": {
+        "conf": "^10.0.0",
+        "type-fest": "^1.0.2"
+      },
+      "dependencies": {
+        "type-fest": {
+          "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.0.2.tgz",
+          "integrity": "sha512-a720oz3Kjbp3ll0zkeN9qjRhO7I34MKMhPGQiQJAmaZQZQ1lo+NWThK322f7sXV+kTg9B1Ybt16KgBXWgteT8w=="
+        }
+      }
+    },
     "electron-to-chromium": {
       "version": "1.3.720",
       "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.720.tgz",
@@ -5200,8 +5356,7 @@
     "env-paths": {
       "version": "2.2.1",
       "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
-      "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
-      "dev": true
+      "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A=="
     },
     "envinfo": {
       "version": "7.8.1",
@@ -6390,8 +6545,7 @@
     "globals": {
       "version": "11.12.0",
       "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
-      "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
-      "dev": true
+      "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA=="
     },
     "globalthis": {
       "version": "1.0.2",
@@ -6590,6 +6744,21 @@
       "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
       "dev": true
     },
+    "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==",
+      "requires": {
+        "react-is": "^16.7.0"
+      },
+      "dependencies": {
+        "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=="
+        }
+      }
+    },
     "homedir-polyfill": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz",
@@ -6754,12 +6923,6 @@
         "safer-buffer": ">= 2.1.2 < 3"
       }
     },
-    "icss-utils": {
-      "version": "5.1.0",
-      "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz",
-      "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==",
-      "dev": true
-    },
     "idb-keyval": {
       "version": "3.2.0",
       "resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-3.2.0.tgz",
@@ -7167,8 +7330,7 @@
     "is-obj": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz",
-      "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==",
-      "dev": true
+      "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w=="
     },
     "is-object": {
       "version": "1.0.2",
@@ -8445,8 +8607,7 @@
     "jsesc": {
       "version": "2.5.2",
       "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
-      "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
-      "dev": true
+      "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA=="
     },
     "json-buffer": {
       "version": "3.0.0",
@@ -8476,6 +8637,11 @@
       "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
       "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
     },
+    "json-schema-typed": {
+      "version": "7.0.3",
+      "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-7.0.3.tgz",
+      "integrity": "sha512-7DE8mpG+/fVw+dTpjbxnx47TaMnDfOI1jwft9g1VybltZCduyRQPJPvc+zzKY9WPHxhPWczyFuYa6I8Mw4iU5A=="
+    },
     "json-stringify-safe": {
       "version": "5.0.1",
       "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
@@ -8546,12 +8712,6 @@
       "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==",
       "dev": true
     },
-    "klona": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.4.tgz",
-      "integrity": "sha512-ZRbnvdg/NxqzC7L9Uyqzf4psi1OM4Cuc+sJAkQPjO6XkQIJTNbfK2Rsmbw8fx1p2mkZdp2FZYo2+LwXYY/uwIA==",
-      "dev": true
-    },
     "latest-version": {
       "version": "5.1.0",
       "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz",
@@ -8688,8 +8848,7 @@
     "lodash": {
       "version": "4.17.20",
       "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
-      "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
-      "dev": true
+      "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA=="
     },
     "lodash._reinterpolate": {
       "version": "3.0.0",
@@ -9083,20 +9242,13 @@
     "ms": {
       "version": "2.1.2",
       "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
-      "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
-      "dev": true
+      "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
     },
     "mute-stream": {
       "version": "0.0.8",
       "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
       "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA=="
     },
-    "nanoid": {
-      "version": "3.1.22",
-      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.22.tgz",
-      "integrity": "sha512-/2ZUaJX2ANuLtTvqTlgqBQNJoQO398KyJgZloL0PZkC0dpysjncRUPsFe3DUPzz/y3h+u7C46np8RMuvF3jsSQ==",
-      "dev": true
-    },
     "nanomatch": {
       "version": "1.2.13",
       "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
@@ -9839,7 +9991,6 @@
       "version": "2.3.0",
       "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
       "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
-      "dev": true,
       "requires": {
         "p-try": "^2.0.0"
       }
@@ -9865,8 +10016,7 @@
     "p-try": {
       "version": "2.2.0",
       "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
-      "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
-      "dev": true
+      "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="
     },
     "package-json": {
       "version": "6.5.0",
@@ -10151,6 +10301,46 @@
         "find-up": "^4.0.0"
       }
     },
+    "pkg-up": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz",
+      "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==",
+      "requires": {
+        "find-up": "^3.0.0"
+      },
+      "dependencies": {
+        "find-up": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
+          "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+          "requires": {
+            "locate-path": "^3.0.0"
+          }
+        },
+        "locate-path": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
+          "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+          "requires": {
+            "p-locate": "^3.0.0",
+            "path-exists": "^3.0.0"
+          }
+        },
+        "p-locate": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
+          "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+          "requires": {
+            "p-limit": "^2.0.0"
+          }
+        },
+        "path-exists": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+          "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU="
+        }
+      }
+    },
     "plist": {
       "version": "3.0.2",
       "resolved": "https://registry.npmjs.org/plist/-/plist-3.0.2.tgz",
@@ -10178,67 +10368,10 @@
       "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=",
       "dev": true
     },
-    "postcss": {
-      "version": "8.2.13",
-      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.2.13.tgz",
-      "integrity": "sha512-FCE5xLH+hjbzRdpbRb1IMCvPv9yZx2QnDarBEYSN0N0HYk+TcXsEhwdFcFb+SRWOKzKGErhIEbBK2ogyLdTtfQ==",
-      "dev": true,
-      "requires": {
-        "colorette": "^1.2.2",
-        "nanoid": "^3.1.22",
-        "source-map": "^0.6.1"
-      }
-    },
-    "postcss-modules-extract-imports": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz",
-      "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==",
-      "dev": true
-    },
-    "postcss-modules-local-by-default": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz",
-      "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==",
-      "dev": true,
-      "requires": {
-        "icss-utils": "^5.0.0",
-        "postcss-selector-parser": "^6.0.2",
-        "postcss-value-parser": "^4.1.0"
-      }
-    },
-    "postcss-modules-scope": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz",
-      "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==",
-      "dev": true,
-      "requires": {
-        "postcss-selector-parser": "^6.0.4"
-      }
-    },
-    "postcss-modules-values": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz",
-      "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==",
-      "dev": true,
-      "requires": {
-        "icss-utils": "^5.0.0"
-      }
-    },
-    "postcss-selector-parser": {
-      "version": "6.0.5",
-      "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.5.tgz",
-      "integrity": "sha512-aFYPoYmXbZ1V6HZaSvat08M97A8HqO6Pjz+PiNpw/DhuRrC72XWAdp3hL6wusDCN31sSmcZyMGa2hZEuX+Xfhg==",
-      "dev": true,
-      "requires": {
-        "cssesc": "^3.0.0",
-        "util-deprecate": "^1.0.2"
-      }
-    },
     "postcss-value-parser": {
       "version": "4.1.0",
       "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz",
-      "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==",
-      "dev": true
+      "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ=="
     },
     "prebuild-install": {
       "version": "6.1.1",
@@ -10607,6 +10740,11 @@
         "scheduler": "^0.20.2"
       }
     },
+    "react-icons": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.2.0.tgz",
+      "integrity": "sha512-rmzEDFt+AVXRzD7zDE21gcxyBizD/3NqjbX6cmViAgdqfJ2UiLer8927/QhhrXQV7dEj/1EGuOTPp7JnLYVJKQ=="
+    },
     "react-is": {
       "version": "17.0.1",
       "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.1.tgz",
@@ -10880,6 +11018,11 @@
       "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
       "dev": true
     },
+    "require-from-string": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
+      "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="
+    },
     "require-main-filename": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
@@ -11210,25 +11353,6 @@
         "truncate-utf8-bytes": "^1.0.0"
       }
     },
-    "sass": {
-      "version": "1.32.11",
-      "resolved": "https://registry.npmjs.org/sass/-/sass-1.32.11.tgz",
-      "integrity": "sha512-O9tRcob/fegUVSIV1ihLLZcftIOh0AF1VpKgusUfLqnb2jQ0GLDwI5ivv1FYWivGv8eZ/AwntTyTzjcHu0c/qw==",
-      "dev": true,
-      "requires": {
-        "chokidar": ">=3.0.0 <4.0.0"
-      }
-    },
-    "sass-loader": {
-      "version": "11.0.1",
-      "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-11.0.1.tgz",
-      "integrity": "sha512-Vp1LcP4slTsTNLEiDkTcm8zGN/XYYrZz2BZybQbliWA8eXveqA/AxsEjllQTpJbg2MzCsx/qNO48sHdZtOaxTw==",
-      "dev": true,
-      "requires": {
-        "klona": "^2.0.4",
-        "neo-async": "^2.6.2"
-      }
-    },
     "sax": {
       "version": "1.2.4",
       "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
@@ -11384,6 +11508,11 @@
         "kind-of": "^6.0.2"
       }
     },
+    "shallowequal": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz",
+      "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ=="
+    },
     "sharp": {
       "version": "0.28.1",
       "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.28.1.tgz",
@@ -12163,14 +12292,36 @@
         "escape-string-regexp": "^1.0.2"
       }
     },
-    "style-loader": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-2.0.0.tgz",
-      "integrity": "sha512-Z0gYUJmzZ6ZdRUqpg1r8GsaFKypE+3xAzuFeMuoHgjc9KZv3wMyCRjQIWEbhoFSq7+7yoHXySDJyyWQaPajeiQ==",
-      "dev": true,
+    "styled-components": {
+      "version": "5.2.3",
+      "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.2.3.tgz",
+      "integrity": "sha512-BlR+KrLW3NL1yhvEB+9Nu9Dt51CuOnHoxd+Hj+rYPdtyR8X11uIW9rvhpy3Dk4dXXBsiW1u5U78f00Lf/afGoA==",
       "requires": {
-        "loader-utils": "^2.0.0",
-        "schema-utils": "^3.0.0"
+        "@babel/helper-module-imports": "^7.0.0",
+        "@babel/traverse": "^7.4.5",
+        "@emotion/is-prop-valid": "^0.8.8",
+        "@emotion/stylis": "^0.8.4",
+        "@emotion/unitless": "^0.7.4",
+        "babel-plugin-styled-components": ">= 1.12.0",
+        "css-to-react-native": "^3.0.0",
+        "hoist-non-react-statics": "^3.0.0",
+        "shallowequal": "^1.1.0",
+        "supports-color": "^5.5.0"
+      },
+      "dependencies": {
+        "has-flag": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+          "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
+        },
+        "supports-color": {
+          "version": "5.5.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+          "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+          "requires": {
+            "has-flag": "^3.0.0"
+          }
+        }
       }
     },
     "sumchecker": {
@@ -12452,8 +12603,7 @@
     "to-fast-properties": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
-      "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=",
-      "dev": true
+      "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4="
     },
     "to-object-path": {
       "version": "0.3.0",
@@ -13111,9 +13261,7 @@
     "uuid": {
       "version": "8.3.2",
       "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
-      "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
-      "dev": true,
-      "optional": true
+      "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
     },
     "v8-compile-cache": {
       "version": "2.3.0",
diff --git a/package.json b/package.json
index 7c1756d1..2dc9d531 100644
--- a/package.json
+++ b/package.json
@@ -64,10 +64,11 @@
     "@types/react": "^17.0.4",
     "@types/react-dom": "^17.0.3",
     "@types/sharp": "^0.27.1",
+    "@types/styled-components": "^5.1.9",
+    "@types/uuid": "^8.3.0",
     "@types/webpack": "^5.28.0",
     "concurrently": "^5.3.0",
     "copy-webpack-plugin": "^8.1.1",
-    "css-loader": "^5.2.4",
     "electron": "^12.0.5",
     "electron-builder": "^22.10.5",
     "file-loader": "^6.2.0",
@@ -77,10 +78,7 @@
     "nexe": "^4.0.0-beta.18",
     "nodemon": "^2.0.7",
     "patch-package": "^6.4.7",
-    "sass": "^1.32.11",
-    "sass-loader": "^11.0.1",
     "standard-version": "^9.1.0",
-    "style-loader": "^2.0.0",
     "ts-jest": "^26.4.4",
     "ts-loader": "^9.1.1",
     "ts-node": "^9.1.1",
@@ -96,6 +94,7 @@
   "dependencies": {
     "@octokit/rest": "^18.4.0",
     "commander": "^7.2.0",
+    "electron-store": "^8.0.0",
     "inquirer": "^8.0.0",
     "iohook": "^0.6.6",
     "is-wsl": "^2.2.0",
@@ -103,10 +102,13 @@
     "ora": "^5.4.0",
     "react": "^17.0.2",
     "react-dom": "^17.0.2",
+    "react-icons": "^4.2.0",
     "sanitize-filename": "^1.6.3",
     "screenshot-desktop": "^1.12.3",
     "sharp": "^0.28.1",
     "sound-play": "^1.1.0",
-    "tesseract.js": "^2.1.4"
+    "styled-components": "^5.2.3",
+    "tesseract.js": "^2.1.4",
+    "uuid": "^8.3.2"
   }
 }
diff --git a/src/client-electron/common.ts b/src/client-electron/common.ts
new file mode 100644
index 00000000..2ba3e290
--- /dev/null
+++ b/src/client-electron/common.ts
@@ -0,0 +1,80 @@
+import { IpcRendererEvent, ipcRenderer as ipc } from 'electron';
+import { ScreenshotDisplayOutput } from 'screenshot-desktop';
+import { v4 as uuidv4 } from 'uuid';
+
+export enum WorkerStatus {
+  BOOTSTRAP,
+  READY,
+  WORKING,
+}
+
+export interface HistoryEntry {}
+
+export interface AppSettings {}
+
+export interface State {
+  history: HistoryEntry[];
+  displays: ScreenshotDisplayOutput[];
+  activeDisplay: ScreenshotDisplayOutput;
+  settings: {};
+  status: WorkerStatus;
+}
+
+type Origin = 'worker' | 'renderer';
+
+export interface Action<T = any> {
+  type: string;
+  payload?: T;
+  origin: Origin;
+}
+
+export interface Request<T = any> {
+  type: string;
+  data?: T;
+  uuid: string;
+  origin: Origin;
+}
+
+export interface Response<T = any> {
+  data: T;
+  uuid: string;
+  origin: Origin;
+}
+
+export function createDispatcher(origin: Origin) {
+  return <TRes, TReq = any>(action: Omit<Request<TReq>, 'origin' | 'uuid'>) =>
+    new Promise<TRes>((resolve) => {
+      const uuid = uuidv4();
+      const req: Request<TReq> = { ...action, origin, uuid };
+
+      function onAsyncResponse(e: IpcRendererEvent, res: Response<TRes>) {
+        if (res.uuid !== uuid) return;
+
+        ipc.removeListener('async-response', onAsyncResponse);
+
+        resolve(res.data);
+      }
+
+      ipc.on('async-response', onAsyncResponse);
+      ipc.send('async-request', req);
+    });
+}
+
+export const rendererDispatcher = createDispatcher('renderer');
+
+export function createListener(origin: Origin) {
+  return (handler: (req: Request) => Promise<any>) => {
+    ipc.on('async-request', async (event, req: Request) => {
+      const data = await handler(req);
+      const res: Response = { data, uuid: req.uuid, origin };
+
+      event.sender.send('async-response', res);
+    });
+
+    return () => {
+      ipc.removeAllListeners('async-request');
+    };
+  };
+}
+
+export const workerListener = createListener('worker');
diff --git a/src/client-electron/main/main.ts b/src/client-electron/main/main.ts
index 2c633050..aa1b332e 100644
--- a/src/client-electron/main/main.ts
+++ b/src/client-electron/main/main.ts
@@ -1,56 +1,26 @@
-import { app, BrowserWindow, globalShortcut, ipcMain as ipc } from 'electron';
-import { join } from 'path';
-
-function createRendererWindow() {
-  const window = new BrowserWindow({
-    minWidth: 1280,
-    minHeight: 720,
-    webPreferences: {
-      nodeIntegration: true,
-      contextIsolation: false,
-    },
-  });
-
-  window.webContents.openDevTools();
-  window.loadFile(join(__dirname, './renderer.html'));
-
-  return window;
-}
-
-function createWorkerWindow() {
-  const window = new BrowserWindow({
-    show: false,
-    webPreferences: {
-      nodeIntegration: true,
-      contextIsolation: false,
-      backgroundThrottling: false,
-    },
-  });
-
-  window.loadFile(join(__dirname, './worker.html'));
-
-  return window;
-}
+import { app, globalShortcut, ipcMain as ipc } from 'electron';
+import { Store } from './store';
+import { createBrowserWindows } from './windows';
 
 async function main() {
   await app.whenReady();
 
-  const window = createRendererWindow();
-  const worker = createWorkerWindow();
+  const { worker, renderer } = createBrowserWindows();
+  const store = new Store(worker.webContents, renderer.webContents);
 
-  window.on('closed', () => app.exit());
+  renderer.on('closed', () => app.exit());
 
   ipc.once('worker:ready', () => {
-    window.webContents.send('worker:ready');
-
     globalShortcut.register('CommandOrControl+numdec', () => {
       worker.webContents.send('worker:solve');
     });
   });
+
+  ipc.on('before-exit', () => {
+    store.dispose();
+
+    globalShortcut.unregisterAll();
+  });
 }
 
 main();
-
-ipc.on('before-exit', () => {
-  globalShortcut.unregisterAll();
-});
diff --git a/src/client-electron/main/store.ts b/src/client-electron/main/store.ts
new file mode 100644
index 00000000..9d3d86ba
--- /dev/null
+++ b/src/client-electron/main/store.ts
@@ -0,0 +1,105 @@
+import { ipcMain as ipc, IpcMainEvent, WebContents } from 'electron';
+import ElectronStore from 'electron-store';
+import {
+  Action,
+  AppSettings,
+  HistoryEntry,
+  Request,
+  Response,
+  State,
+} from '../common';
+
+function reducer<T>({ type, payload }: Action<T>, state: State) {
+  switch (type) {
+    case 'SET_ACTIVE_DISPLAY':
+      return {
+        ...state,
+        activeDisplay: payload,
+      };
+    case 'SET_DISPLAYS':
+      return {
+        ...state,
+        displays: payload,
+      };
+    case 'SET_STATUS':
+      return { ...state, status: payload };
+  }
+}
+
+export class Store {
+  private settings = new ElectronStore<AppSettings>({
+    name: 'settings',
+    defaults: {},
+  });
+  private history = new ElectronStore<{ data: HistoryEntry[] }>({
+    name: 'history',
+    defaults: { data: [] },
+  });
+
+  private state = this.getInitialState();
+
+  constructor(private worker: WebContents, private renderer: WebContents) {
+    this.registerStoreListeners();
+  }
+
+  private getInitialState(): State {
+    return {
+      history: this.history.get('data'),
+      displays: [],
+      activeDisplay: null,
+      settings: {},
+      status: null,
+    };
+  }
+
+  private registerStoreListeners() {
+    ipc.on('state', this.onState.bind(this));
+    ipc.on('get-state', this.onGetState.bind(this));
+    ipc.on('async-request', this.onAsyncRequest.bind(this));
+  }
+
+  private onState(event: IpcMainEvent, action: Action) {
+    const dest = this.getDest(action);
+    this.state = reducer(action, this.state);
+
+    dest.send('state', this.state);
+    dest.send(action.type, action.payload);
+  }
+
+  private onGetState(event: IpcMainEvent) {
+    event.returnValue = this.state;
+  }
+
+  private onAsyncRequest(event: IpcMainEvent, req: Request) {
+    function onAsyncResponse(e: IpcMainEvent, res: Response) {
+      if (res.uuid !== req.uuid) return;
+
+      ipc.removeListener('async-response', onAsyncResponse);
+
+      event.sender.send('async-response', res);
+    }
+
+    ipc.on('async-response', onAsyncResponse);
+
+    const dest = this.getDest(req);
+
+    dest.send('async-request', req);
+  }
+
+  private getDest(action: Action) {
+    return action.origin === 'worker' ? this.renderer : this.worker;
+  }
+
+  getState() {
+    return this.state;
+  }
+
+  dispose() {
+    this.history.set('data', this.state.history);
+
+    ipc.removeAllListeners('state');
+    ipc.removeAllListeners('async-request');
+    ipc.removeAllListeners('async-response');
+    ipc.removeAllListeners('get-state');
+  }
+}
diff --git a/src/client-electron/main/windows.ts b/src/client-electron/main/windows.ts
new file mode 100644
index 00000000..ed9f98b6
--- /dev/null
+++ b/src/client-electron/main/windows.ts
@@ -0,0 +1,37 @@
+import { BrowserWindow, BrowserWindowConstructorOptions } from 'electron';
+import { join } from 'path';
+
+function createWindow(name: string, options: BrowserWindowConstructorOptions) {
+  const window = new BrowserWindow(options);
+
+  window.loadFile(join(__dirname, `./${name}.html`));
+
+  return window;
+}
+
+const workerOptions = {
+  show: false,
+  webPreferences: {
+    nodeIntegration: true,
+    contextIsolation: false,
+    backgroundThrottling: false,
+  },
+};
+
+const rendererOptions = {
+  minWidth: 1280,
+  minHeight: 720,
+  webPreferences: {
+    nodeIntegration: true,
+    contextIsolation: false,
+  },
+};
+
+export function createBrowserWindows() {
+  const worker = createWindow('worker', workerOptions);
+  const renderer = createWindow('renderer', rendererOptions);
+
+  renderer.webContents.openDevTools();
+
+  return { worker, renderer };
+}
diff --git a/src/client-electron/renderer/app.tsx b/src/client-electron/renderer/app.tsx
index 78bfde32..436915af 100644
--- a/src/client-electron/renderer/app.tsx
+++ b/src/client-electron/renderer/app.tsx
@@ -1,5 +1,45 @@
-import { ipcRenderer as ipc } from 'electron';
+import { ipcRenderer as ipc, IpcRendererEvent } from 'electron';
+import { useEffect, useState } from 'react';
+import styled from 'styled-components';
+import { State } from '../common';
+import { StatusBar } from './components/StatusBar';
 
-ipc.once('worker:ready', () => console.log('worker ready'));
+const Main = styled.main`
+  flex-grow: 1;
+`;
 
-export const App = () => <div className="app">BreachProtocolAutosolver</div>;
+function useIpcEvent<T>(channel: string, initialValue?: T) {
+  const [value, setValue] = useState<T>(initialValue);
+
+  useEffect(() => {
+    function handleEvent(e: IpcRendererEvent, value: T) {
+      setValue(value);
+    }
+
+    ipc.on(channel, handleEvent);
+
+    return () => {
+      ipc.removeListener(channel, handleEvent);
+    };
+  }, []);
+
+  return value;
+}
+
+function useStore() {
+  const initialState = ipc.sendSync('get-state');
+  const state = useIpcEvent<State>('state', initialState);
+
+  return state;
+}
+
+export const App = () => {
+  const state = useStore();
+
+  return (
+    <>
+      <Main />
+      <StatusBar display={state?.activeDisplay} status={state?.status} />
+    </>
+  );
+};
diff --git a/src/client-electron/renderer/assets/fonts/Rajdhani/OFL.txt b/src/client-electron/renderer/assets/fonts/Rajdhani/OFL.txt
new file mode 100755
index 00000000..6dd80965
--- /dev/null
+++ b/src/client-electron/renderer/assets/fonts/Rajdhani/OFL.txt
@@ -0,0 +1,93 @@
+Copyright (c) 2014, Indian Type Foundry (info@indiantypefoundry.com).
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at:
+http://scripts.sil.org/OFL
+
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded, 
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/src/client-electron/renderer/assets/fonts/Rajdhani/Rajdhani-Bold.ttf b/src/client-electron/renderer/assets/fonts/Rajdhani/Rajdhani-Bold.ttf
new file mode 100755
index 00000000..6c0454c0
Binary files /dev/null and b/src/client-electron/renderer/assets/fonts/Rajdhani/Rajdhani-Bold.ttf differ
diff --git a/src/client-electron/renderer/assets/fonts/Rajdhani/Rajdhani-Light.ttf b/src/client-electron/renderer/assets/fonts/Rajdhani/Rajdhani-Light.ttf
new file mode 100755
index 00000000..1c7a3613
Binary files /dev/null and b/src/client-electron/renderer/assets/fonts/Rajdhani/Rajdhani-Light.ttf differ
diff --git a/src/client-electron/renderer/assets/fonts/Rajdhani/Rajdhani-Medium.ttf b/src/client-electron/renderer/assets/fonts/Rajdhani/Rajdhani-Medium.ttf
new file mode 100755
index 00000000..e52b403e
Binary files /dev/null and b/src/client-electron/renderer/assets/fonts/Rajdhani/Rajdhani-Medium.ttf differ
diff --git a/src/client-electron/renderer/assets/fonts/Rajdhani/Rajdhani-Regular.ttf b/src/client-electron/renderer/assets/fonts/Rajdhani/Rajdhani-Regular.ttf
new file mode 100755
index 00000000..37663a5c
Binary files /dev/null and b/src/client-electron/renderer/assets/fonts/Rajdhani/Rajdhani-Regular.ttf differ
diff --git a/src/client-electron/renderer/assets/fonts/Rajdhani/Rajdhani-SemiBold.ttf b/src/client-electron/renderer/assets/fonts/Rajdhani/Rajdhani-SemiBold.ttf
new file mode 100755
index 00000000..f7ec9bb5
Binary files /dev/null and b/src/client-electron/renderer/assets/fonts/Rajdhani/Rajdhani-SemiBold.ttf differ
diff --git a/src/client-electron/renderer/components/StatusBar.tsx b/src/client-electron/renderer/components/StatusBar.tsx
new file mode 100644
index 00000000..bb8c7e7e
--- /dev/null
+++ b/src/client-electron/renderer/components/StatusBar.tsx
@@ -0,0 +1,71 @@
+import { WorkerStatus } from '@/client-electron/common';
+import { FC } from 'react';
+import { MdDone } from 'react-icons/md';
+import { ScreenshotDisplayOutput } from 'screenshot-desktop';
+import styled from 'styled-components';
+
+const Spacer = styled.div`
+  flex-grow: 1;
+`;
+
+const StatusBarItem = styled.div`
+  height: 100%;
+  display: flex;
+  align-items: center;
+  display: inline-flex;
+  padding: 0 8px;
+`;
+
+const InteractiveStatusBarItem = styled(StatusBarItem)`
+  user-select: none;
+
+  &:hover {
+    color: var(--accent);
+    cursor: pointer;
+  }
+`;
+
+const StatusBarStyled = styled.footer`
+  height: 40px;
+  background: var(--background);
+  color: #fff;
+  font-size: 18px;
+  font-weight: 500;
+  text-transform: uppercase;
+  padding: 0 12px;
+  gap: 0.5rem;
+  display: flex;
+`;
+
+function renderStatusBarContent(status: WorkerStatus) {
+  switch (status) {
+    case WorkerStatus.BOOTSTRAP:
+      return <StatusBarItem>Loading...</StatusBarItem>;
+    case WorkerStatus.READY:
+      return <StatusBarItem>Ready</StatusBarItem>;
+    case WorkerStatus.WORKING:
+      return <StatusBarItem>Working...</StatusBarItem>;
+    default:
+      return <StatusBarItem>Initializing..</StatusBarItem>;
+  }
+}
+
+// TODO: add missing props and replace placeholders.
+interface StatusBarProps {
+  status: WorkerStatus;
+  display: ScreenshotDisplayOutput;
+}
+
+export const StatusBar: FC<StatusBarProps> = ({ status, display }) => {
+  return (
+    <StatusBarStyled>
+      <StatusBarItem>
+        <MdDone style={{ marginRight: '4px' }} /> placeholder
+      </StatusBarItem>
+      <InteractiveStatusBarItem>{display?.id}</InteractiveStatusBarItem>
+      {renderStatusBarContent(status)}
+      <Spacer />
+      <StatusBarItem>placeholder</StatusBarItem>
+    </StatusBarStyled>
+  );
+};
diff --git a/src/client-electron/renderer/renderer.tsx b/src/client-electron/renderer/renderer.tsx
index 40ceebe5..245590c9 100644
--- a/src/client-electron/renderer/renderer.tsx
+++ b/src/client-electron/renderer/renderer.tsx
@@ -1,4 +1,11 @@
 import ReactDOM from 'react-dom';
 import { App } from './app';
+import { GlobalStyles } from './styles/global';
 
-ReactDOM.render(<App />, document.getElementById('root'));
+ReactDOM.render(
+  <>
+    <GlobalStyles />
+    <App />
+  </>,
+  document.getElementById('root')
+);
diff --git a/src/client-electron/renderer/styles/fonts.tsx b/src/client-electron/renderer/styles/fonts.tsx
new file mode 100644
index 00000000..f473c9cf
--- /dev/null
+++ b/src/client-electron/renderer/styles/fonts.tsx
@@ -0,0 +1,24 @@
+import RajdhaniLight from '../assets/fonts/Rajdhani/Rajdhani-Light.ttf';
+import RajdhaniRegular from '../assets/fonts/Rajdhani/Rajdhani-Regular.ttf';
+import RajdhaniMedium from '../assets/fonts/Rajdhani/Rajdhani-Medium.ttf';
+import RajdhaniSemiBold from '../assets/fonts/Rajdhani/Rajdhani-SemiBold.ttf';
+import RajdhaniBold from '../assets/fonts/Rajdhani/Rajdhani-Bold.ttf';
+
+const rajdhaniFontFamily = [
+  { url: RajdhaniLight, weight: 300 },
+  { url: RajdhaniRegular, weight: 400 },
+  { url: RajdhaniMedium, weight: 500 },
+  { url: RajdhaniSemiBold, weight: 600 },
+  { url: RajdhaniBold, weight: 700 },
+];
+
+export const fonts = rajdhaniFontFamily.map(
+  ({ url, weight }) => `
+    @font-face {
+      font-family: Rajdhani;
+      src: url(${url});
+      font-weight: ${weight};
+      font-style: normal;
+    }
+  `
+);
diff --git a/src/client-electron/renderer/styles/global.tsx b/src/client-electron/renderer/styles/global.tsx
new file mode 100644
index 00000000..d5d31df0
--- /dev/null
+++ b/src/client-electron/renderer/styles/global.tsx
@@ -0,0 +1,29 @@
+import { createGlobalStyle } from 'styled-components';
+import { fonts } from './fonts';
+
+export const GlobalStyles = createGlobalStyle`
+  ${fonts}
+
+  :root {
+    --primary: #ff5851;
+    --background: #121018;
+    --accent: #5FF6FF;
+  }
+
+  html,
+  body {
+    height: 100%;
+    font-family: Rajdhani;
+  }
+
+  body {
+    margin: 0;
+    background: linear-gradient(180deg, #3a1216 0%, var(--background) 82%);
+  }
+
+  #root {
+    display: flex;
+    flex-direction: column;
+    height: 100%;
+  }
+`;
diff --git a/src/client-electron/worker/worker.ts b/src/client-electron/worker/worker.ts
index a4f215eb..c397844d 100644
--- a/src/client-electron/worker/worker.ts
+++ b/src/client-electron/worker/worker.ts
@@ -3,21 +3,52 @@ import { BreachProtocolOCRFragment } from '@/core';
 import { solveBreachProtocol } from '@/platform-node/solve';
 import { ipcRenderer as ipc } from 'electron';
 import { listDisplays } from 'screenshot-desktop';
+import { Action, Request, workerListener, WorkerStatus } from '../common';
+
+function updateStatus(payload: WorkerStatus) {
+  dispatch({ type: 'SET_STATUS', payload });
+}
+
+function dispatch(action: Omit<Action, 'origin'>) {
+  ipc.send('state', { ...action, origin: 'worker' });
+}
+
+ipc.on('SET_ACTIVE_DISPLAY', (event, state) => {
+  screenId = state.id;
+});
+
+const disposeAsyncRequestListener = workerListener(handleAsyncRequest);
+
+async function handleAsyncRequest(req: Request) {
+  switch (req.type) {
+    default:
+  }
+}
 
 let screenId: string = null;
 
 async function bootstrap() {
+  updateStatus(WorkerStatus.BOOTSTRAP);
+
   setLang('en');
   const displays = await listDisplays();
   screenId = displays[0].id;
 
+  dispatch({ type: 'SET_DISPLAYS', payload: displays });
+  dispatch({ type: 'SET_ACTIVE_DISPLAY', payload: displays[0] });
+
   await BreachProtocolOCRFragment.initScheduler();
 
   ipc.send('worker:ready');
+  updateStatus(WorkerStatus.READY);
 }
 
 bootstrap();
 
-ipc.on('worker:solve', async () => {
+ipc.on('worker:solve', async (event) => {
+  updateStatus(WorkerStatus.WORKING);
   await solveBreachProtocol(screenId);
+
+  event.sender.send('worker:solved');
+  updateStatus(WorkerStatus.READY);
 });
diff --git a/types/fonts/index.d.ts b/types/fonts/index.d.ts
new file mode 100644
index 00000000..1764f41e
--- /dev/null
+++ b/types/fonts/index.d.ts
@@ -0,0 +1 @@
+declare module '*.ttf';