about summary refs log tree commit diff stats
path: root/src/pages
diff options
context:
space:
mode:
Diffstat (limited to 'src/pages')
-rw-r--r--src/pages/background/background.html10
-rw-r--r--src/pages/background/background.js663
-rw-r--r--src/pages/components/Button.svelte35
-rw-r--r--src/pages/components/Checkbox.svelte52
-rw-r--r--src/pages/components/Input.svelte46
-rw-r--r--src/pages/components/Label.svelte18
-rw-r--r--src/pages/components/Row.svelte12
-rw-r--r--src/pages/components/Select.svelte34
-rw-r--r--src/pages/fonts/Inter-VariableFont_slnt,wght.ttf (renamed from src/pages/stylesheets/Inter-VariableFont_slnt,wght.ttf)bin803384 -> 803384 bytes
-rw-r--r--src/pages/fonts/Vazirmatn-VariableFont_wght.ttf (renamed from src/pages/stylesheets/Vazirmatn-VariableFont_wght.ttf)bin285620 -> 285620 bytes
-rw-r--r--src/pages/fonts/styles.css13
-rw-r--r--src/pages/icons/AboutIcon.svelte11
-rw-r--r--src/pages/icons/AddIcon.svelte3
-rw-r--r--src/pages/icons/AutoPickIcon.svelte11
-rw-r--r--src/pages/icons/CloseIcon.svelte3
-rw-r--r--src/pages/icons/CopyIcon.svelte12
-rw-r--r--src/pages/icons/ExportIcon.svelte12
-rw-r--r--src/pages/icons/GeneralIcon.svelte13
-rw-r--r--src/pages/icons/ImportIcon.svelte12
-rw-r--r--src/pages/icons/PingIcon.svelte12
-rw-r--r--src/pages/icons/RedirectIcon.svelte10
-rw-r--r--src/pages/icons/RedirectToOriginalIcon.svelte13
-rw-r--r--src/pages/icons/ResetIcon.svelte16
-rw-r--r--src/pages/icons/ServicesIcon.svelte11
-rw-r--r--src/pages/icons/SettingsIcon.svelte15
-rw-r--r--src/pages/icons/SwitchInstanceIcon.svelte15
-rw-r--r--src/pages/messages/index.html14
-rw-r--r--src/pages/messages/no_instance.html26
-rw-r--r--src/pages/messages_src/App.svelte200
-rw-r--r--src/pages/messages_src/main.js7
-rw-r--r--src/pages/messages_src/stores.js5
-rw-r--r--src/pages/options/index.html17
-rw-r--r--src/pages/options/index.js399
-rw-r--r--src/pages/options/index.pug10
-rw-r--r--src/pages/options/init.js54
-rw-r--r--src/pages/options/widgets/general.js219
-rw-r--r--src/pages/options/widgets/general.pug88
-rw-r--r--src/pages/options/widgets/services.pug83
-rw-r--r--src/pages/options_src/App.svelte101
-rw-r--r--src/pages/options_src/General/Exceptions.svelte110
-rw-r--r--src/pages/options_src/General/General.svelte98
-rw-r--r--src/pages/options_src/General/SettingsButtons.svelte112
-rw-r--r--src/pages/options_src/Services/FrontendIcon.svelte41
-rw-r--r--src/pages/options_src/Services/Instances.svelte261
-rw-r--r--src/pages/options_src/Services/RedirectType.svelte102
-rw-r--r--src/pages/options_src/Services/ServiceIcon.svelte40
-rw-r--r--src/pages/options_src/Services/Services.svelte260
-rw-r--r--src/pages/options_src/Sidebar.svelte69
-rw-r--r--src/pages/options_src/main.js7
-rw-r--r--src/pages/options_src/stores.js4
-rw-r--r--src/pages/options_src/url.js38
-rw-r--r--src/pages/popup/index.html17
-rw-r--r--src/pages/popup/popup.js113
-rw-r--r--src/pages/popup/popup.pug51
-rw-r--r--src/pages/popup/style.css53
-rw-r--r--src/pages/popup/switches.pug14
-rw-r--r--src/pages/popup_src/App.svelte78
-rw-r--r--src/pages/popup_src/Buttons.svelte145
-rw-r--r--src/pages/popup_src/components/Row.svelte13
-rw-r--r--src/pages/popup_src/components/ServiceIcon.svelte40
-rw-r--r--src/pages/popup_src/components/Switch.svelte81
-rw-r--r--src/pages/popup_src/main.js7
-rw-r--r--src/pages/popup_src/stores.js5
-rw-r--r--src/pages/stylesheets/styles.css391
-rw-r--r--src/pages/widgets/head.pug7
-rw-r--r--src/pages/widgets/links.pug22
66 files changed, 2607 insertions, 1847 deletions
diff --git a/src/pages/background/background.html b/src/pages/background/background.html
index 542df18b..787d419e 100644
--- a/src/pages/background/background.html
+++ b/src/pages/background/background.html
@@ -1,7 +1,7 @@
-<!DOCTYPE html>
+<!doctype html>
 <html>
-	<head>
-		<meta charset="utf-8" />
-		<script type="module" src="background.js"></script>
-	</head>
+  <head>
+    <meta charset="utf-8" />
+    <script type="module" src="background.js"></script>
+  </head>
 </html>
diff --git a/src/pages/background/background.js b/src/pages/background/background.js
index 4b8f1ca2..1fcba190 100644
--- a/src/pages/background/background.js
+++ b/src/pages/background/background.js
@@ -7,338 +7,377 @@ const isChrome = browser.runtime.getBrowserInfo === undefined
 window.browser = window.browser || window.chrome
 
 browser.runtime.onInstalled.addListener(async details => {
-	if (details.previousVersion != browser.runtime.getManifest().version) {
-		// ^Used to prevent this running when debugging with auto-reload
-		if (details.reason == "install") {
-			if (!(await utils.getOptions())) {
-				await servicesHelper.initDefaults()
-			}
-			browser.runtime.openOptionsPage()
-		}
-		else if (details.reason == "update") {
-			if (details.previousVersion == '2.5.2') {
-				await servicesHelper.upgradeOptions()
-				await servicesHelper.processUpdate()
-			} else {
-				await servicesHelper.processUpdate()
-			}
-		}
-	}
+  if (details.previousVersion != browser.runtime.getManifest().version) {
+    // ^Used to prevent this running when debugging with auto-reload
+    if (details.reason == "install") {
+      if (!(await utils.getOptions())) {
+        await servicesHelper.initDefaults()
+      }
+    } else if (details.reason == "update") {
+      await servicesHelper.processUpdate()
+    }
+  }
 })
 
+// true to redirect, false to bypass
 let tabIdRedirects = {}
 
 // true == Always redirect, false == Never redirect, null/undefined == follow options for services
 browser.webRequest.onBeforeRequest.addListener(
-	details => {
-		const url = new URL(details.url)
-		if (new RegExp(/^chrome-extension:\/{2}.*\/instances\/.*.json$/).test(url.href) && details.type == "xmlhttprequest") return
-		let initiator
-		try {
-			if (details.originUrl) initiator = new URL(details.originUrl)
-			else if (details.initiator && details.initiator !== "null") initiator = new URL(details.initiator)
-		} catch {
-			return null
-		}
-		if (tabIdRedirects[details.tabId] == false) return null
-		let newUrl = servicesHelper.redirect(url, details.type, initiator, tabIdRedirects[details.tabId], details.incognito)
+  details => {
+    const old_href = details.url
+    const url = new URL(details.url)
+    if (new RegExp(/^chrome-extension:\/{2}.*\/instances\/.*.json$/).test(url.href) && details.type == "xmlhttprequest")
+      return null
 
-		if (details.frameAncestors && details.frameAncestors.length > 0 && servicesHelper.isException(new URL(details.frameAncestors[0].url))) newUrl = null
+    // if url is previously bypassed
+    if (tabIdRedirects[details.tabId] == false) return null
 
-		if (servicesHelper.isException(url)) {
-			if (details.type == "main_frame")
-				newUrl = "BYPASSTAB"
-			else
-				return null
-		}
+    // Bypass embeds from excepted urls
+    if (
+      details.frameAncestors &&
+      details.frameAncestors.length >= 1 &&
+      servicesHelper.isException(new URL(details.frameAncestors[0].url))
+    )
+      return null
 
-		if (!newUrl) {
-			const match = url.href.match(/^https?:\/{2}.*\.libredirect\.invalid.*/)
-			if (match) {
-				browser.tabs.update({
-					url: browser.runtime.getURL(`/pages/messages/no_instance.html`)
-				});
-			}
-		}
+    if (servicesHelper.isException(url)) {
+      if (details.type == "main_frame") {
+        console.log(`Bypassing ${details.tabId} ${url}`)
+        tabIdRedirects[details.tabId] = false
+      }
+      return null
+    }
 
-		if (newUrl) {
-			if (newUrl === "CANCEL") {
-				console.log(`Canceled ${url}`)
-				return { cancel: true }
-			}
-			if (newUrl === "BYPASSTAB") {
-				console.log(`Bypassed ${details.tabId} ${url}`)
-				if (tabIdRedirects[details.tabId] != false) tabIdRedirects[details.tabId] = false
-				return null
-			}
-			console.info("Redirecting", url.href, "=>", newUrl)
-			return { redirectUrl: newUrl }
-		}
-		return null
-	},
-	{ urls: ["<all_urls>"] },
-	["blocking"]
-)
+    let originUrl
+    let documentUrl
+    try {
+      if (details.originUrl) originUrl = new URL(details.originUrl)
+      if (details.documentUrl) documentUrl = new URL(details.documentUrl)
+    } catch {
+      return null
+    }
 
-browser.tabs.onRemoved.addListener(tabId => {
-	if (tabIdRedirects[tabId] != undefined) {
-		delete tabIdRedirects[tabId]
-		console.log(`Removed tab ${tabId} from tabIdRedirects`)
-	}
-})
+    let newUrl = servicesHelper.redirect(
+      url,
+      details.type,
+      originUrl,
+      documentUrl,
+      details.incognito,
+      tabIdRedirects[details.tabId]
+    )
 
-browser.commands.onCommand.addListener(async command => {
-	browser.tabs.query({ active: true, currentWindow: true }, async tabs => {
-		const url = new URL(tabs[0].url)
-		switch (command) {
-			case "switchInstance":
-				const newUrl = await servicesHelper.switchInstance(url)
-				if (newUrl) browser.tabs.update({ url: newUrl })
-				break
-			case "copyRaw": {
-				servicesHelper.copyRaw(url)
-				break
-			}
-			case "redirect": {
-				browser.tabs.query({ active: true, currentWindow: true }, async tabs => {
-					if (tabs[0].url) {
-						const url = new URL(tabs[0].url)
-						const newUrl = servicesHelper.redirect(url, "main_frame", null, true)
-						if (newUrl) {
-							browser.tabs.update(tabs[0].id, { url: newUrl }, () => {
-								tabIdRedirects[tabs[0].id] = true
-							})
-						}
-					}
-				})
-				break
-			}
-			case "reverse": {
-				browser.tabs.query({ active: true, currentWindow: true }, async tabs => {
-					if (tabs[0].url) {
-						const url = new URL(tabs[0].url)
-						const newUrl = await servicesHelper.reverse(url)
-						if (newUrl) {
-							browser.tabs.update(tabs[0].id, { url: newUrl }, () => {
-								tabIdRedirects[tabs[0].id] = false
-							})
-						}
-					}
-				})
-				break
-			}
-		}
-	})
-})
+    if (
+      (newUrl && newUrl.startsWith("https://no-instance.libredirect.invalid")) ||
+      (!newUrl && url.href.startsWith("https://no-instance.libredirect.invalid"))
+    ) {
+      newUrl = newUrl ? new URL(newUrl) : url
+      const frontend = newUrl.searchParams.get("frontend")
+      const oldUrl = new URL(newUrl.searchParams.get("url"))
+      const params = new URLSearchParams({
+        message: "no_instance",
+        url: oldUrl,
+        frontend: frontend,
+      })
+      browser.tabs.update({
+        url: browser.runtime.getURL(`/pages/messages/index.html?${params.toString()}`),
+      })
+      return { cancel: true }
+    }
 
-browser.contextMenus.create({ id: "settingsTab", title: browser.i18n.getMessage("settings"), contexts: ["browser_action"] })
-browser.contextMenus.create({ id: "switchInstanceTab", title: browser.i18n.getMessage("switchInstance"), contexts: ["browser_action"] })
-browser.contextMenus.create({ id: "copyReverseTab", title: 'Copy Original', contexts: ["browser_action"] })
-browser.contextMenus.create({ id: "redirectTab", title: 'Redirect', contexts: ["browser_action"] })
-browser.contextMenus.create({ id: "reverseTab", title: 'Redirect To Original', contexts: ["browser_action"] })
+    if (!newUrl) {
+      if (url.href.match(/^https?:\/{2}(.*\.)?libredirect\.invalid.*/)) {
+        const params = new URLSearchParams({
+          message: "disabled",
+          url: url.href,
+        })
+        browser.tabs.update({
+          url: browser.runtime.getURL(`/pages/messages/index.html?${params.toString()}`),
+        })
+        return { cancel: true }
+      }
+    }
 
-browser.contextMenus.create({ id: "redirectLink", title: 'Redirect', contexts: ["link"] })
-browser.contextMenus.create({ id: "redirectLinkInNewTab", title: 'Redirect In New Tab', contexts: ["link"] })
-browser.contextMenus.create({ id: "reverseLink", title: 'Redirect To Original', contexts: ["link"] })
-browser.contextMenus.create({ id: "reverseLinkInNewTab", title: 'Redirect To Original In New Tab', contexts: ["link"] })
-browser.contextMenus.create({ id: "copyReverseLink", title: 'Copy Original', contexts: ["link"] })
-browser.contextMenus.create({ id: "bypassLink", title: 'Bypass', contexts: ["link"] })
-browser.contextMenus.create({ id: "bypassLinkInNewTab", title: 'Bypass In New Tab', contexts: ["link"] })
+    if (newUrl === "CANCEL") {
+      console.log(`Cancelling ${url}`)
+      return { cancel: true }
+    }
+    if (newUrl === "BYPASSTAB") {
+      console.log(`Bypassing ${details.tabId} ${url}`)
+      tabIdRedirects[details.tabId] = false
+      return null
+    }
+    if (newUrl) {
+      console.log("Redirecting", old_href, "=>", newUrl)
+      return { redirectUrl: newUrl }
+    }
+    return null
+  },
+  { urls: ["<all_urls>"] },
+  ["blocking"]
+)
 
-if (!isChrome) {
-	browser.contextMenus.create({ id: "redirectBookmark", title: 'Redirect', contexts: ["bookmark"] })
-	browser.contextMenus.create({ id: "redirectBookmarkInNewTab", title: 'Redirect In New Tab', contexts: ["bookmark"] })
-	browser.contextMenus.create({ id: "reverseBookmark", title: 'Redirect To Original', contexts: ["bookmark"] })
-	browser.contextMenus.create({ id: "reverseBookmarkInNewTab", title: 'Redirect To Original In New Tab', contexts: ["bookmark"] })
-	browser.contextMenus.create({ id: "copyReverseBookmark", title: 'Copy Original', contexts: ["bookmark"] })
-	browser.contextMenus.create({ id: "bypassBookmark", title: 'Bypass', contexts: ["bookmark"] })
-	browser.contextMenus.create({ id: "bypassBookmarkInNewTab", title: 'Bypass In New Tab', contexts: ["bookmark"] })
-}
+browser.webRequest.onHeadersReceived.addListener(
+  details => {
+    if (details.statusCode >= 501 || details.statusCode == 429 || details.statusCode == 403) {
+      const url = new URL(details.url)
+      const { service, frontend } = servicesHelper.computeFrontend(url)
+      if (!service) return
+      const params = new URLSearchParams({
+        message: "server_error",
+        code: details.statusCode,
+        url: url.href,
+        frontend: frontend,
+        service: service,
+      })
+      setTimeout(() => {
+        browser.tabs.update({
+          url: browser.runtime.getURL(`/pages/messages/index.html?${params.toString()}`),
+        })
+      }, 2000)
+    }
+  },
+  { urls: ["<all_urls>"] }
+)
 
-browser.contextMenus.onClicked.addListener(async (info) => {
-	switch (info.menuItemId) {
-		case 'switchInstanceTab': {
-			const url = new URL(info.pageUrl)
-			const newUrl = await servicesHelper.switchInstance(url)
-			if (newUrl) {
-				browser.tabs.update({ url: newUrl })
-			}
-			return
-		}
-		case 'settingsTab': {
-			browser.runtime.openOptionsPage()
-			return
-		}
-		case 'copyReverseTab': {
-			browser.tabs.query({ active: true, currentWindow: true }, async tabs => {
-				if (tabs[0].url) {
-					const url = new URL(tabs[0].url)
-					servicesHelper.copyRaw(url)
-				}
-			})
-			return
-		}
-		case 'reverseTab': {
-			browser.tabs.query({ active: true, currentWindow: true }, async tabs => {
-				if (tabs[0].url) {
-					const url = new URL(tabs[0].url)
-					const newUrl = await servicesHelper.reverse(url)
-					if (newUrl) {
-						browser.tabs.update(tabs[0].id, { url: newUrl }, () => {
-							tabIdRedirects[tabs[0].id] = false
-						})
-					}
-				}
-			})
-			return
-		}
-		case 'redirectTab': {
-			browser.tabs.query({ active: true, currentWindow: true }, async tabs => {
-				if (tabs[0].url) {
-					const url = new URL(tabs[0].url)
-					const newUrl = servicesHelper.redirect(url, "main_frame", null, true)
-					if (newUrl) {
-						browser.tabs.update(tabs[0].id, { url: newUrl }, () => {
-							tabIdRedirects[tabs[0].id] = true
-						})
-					}
-				}
-			})
-			return
-		}
+browser.tabs.onRemoved.addListener(tabId => {
+  if (tabIdRedirects[tabId] != undefined) {
+    delete tabIdRedirects[tabId]
+    console.log(`Removed tab ${tabId} from tabIdRedirects`)
+  }
+})
 
-		case 'copyReverseLink': {
-			const url = new URL(info.linkUrl)
-			await servicesHelper.copyRaw(url)
-			return
-		}
-		case 'redirectLink':
-		case 'redirectLinkInNewTab': {
-			const url = new URL(info.linkUrl)
-			const newUrl = servicesHelper.redirect(url, "main_frame", null, true)
-			if (newUrl) {
-				if (info.menuItemId == "redirectLink") browser.tabs.update({ url: newUrl })
-				else browser.tabs.create({ url: newUrl })
-			}
-			return
-		}
-		case 'reverseLink':
-		case 'reverseLinkInNewTab': {
-			const url = new URL(info.linkUrl)
-			const newUrl = await servicesHelper.reverse(url)
-			if (newUrl) {
-				if (info.menuItemId == "reverseLink") {
-					browser.tabs.update({ url: newUrl }, tab => {
-						tabIdRedirects[tab.id] = false
-					})
-				} else {
-					browser.tabs.create({ url: newUrl }, tab => {
-						tabIdRedirects[tab.id] = false
-					})
-				}
-			}
-			return
-		}
+browser.runtime.getPlatformInfo(r => {
+  if (r.os != "fuchsia" && r.os != "ios" && r.os != "android") {
+    browser.commands.onCommand.addListener(async command => {
+      browser.tabs.query({ active: true, currentWindow: true }, async tabs => {
+        const url = new URL(tabs[0].url)
+        switch (command) {
+          case "switchInstance": {
+            const newUrl = await servicesHelper.switchInstance(url)
+            if (newUrl) browser.tabs.update({ url: newUrl })
+            break
+          }
+          case "copyRaw":
+            servicesHelper.copyRaw(url)
+            break
+          case "redirect":
+            browser.tabs.query({ active: true, currentWindow: true }, async tabs => {
+              if (tabs[0].url) {
+                const url = new URL(tabs[0].url)
+                const newUrl = servicesHelper.redirect(url, "main_frame", null, null, false, true)
+                if (newUrl) {
+                  browser.tabs.update(tabs[0].id, { url: newUrl }, () => {
+                    tabIdRedirects[tabs[0].id] = true
+                  })
+                }
+              }
+            })
+            break
+          case "reverse":
+            browser.tabs.query({ active: true, currentWindow: true }, async tabs => {
+              if (tabs[0].url) {
+                const url = new URL(tabs[0].url)
+                const newUrl = await servicesHelper.reverse(url)
+                if (newUrl) {
+                  browser.tabs.update(tabs[0].id, { url: newUrl }, () => {
+                    tabIdRedirects[tabs[0].id] = false
+                  })
+                }
+              }
+            })
+            break
+        }
+      })
+    })
 
-		case 'bypassLink':
-		case 'bypassLinkInNewTab': {
-			const url = new URL(info.linkUrl)
-			if (info.menuItemId == "bypassLink") {
-				browser.tabs.update({ url: url.href }, tab => {
-					tabIdRedirects[tab.id] = false
-				})
-			} else {
-				browser.tabs.create({ url: url.href }, tab => {
-					tabIdRedirects[tab.id] = false
-				})
-			}
-			return
-		}
+    browser.contextMenus.create({
+      id: "settingsTab",
+      title: browser.i18n.getMessage("settings"),
+      contexts: ["browser_action"],
+    })
+    browser.contextMenus.create({
+      id: "switchInstanceTab",
+      title: browser.i18n.getMessage("switchInstance"),
+      contexts: ["browser_action"],
+    })
+    browser.contextMenus.create({ id: "copyReverseTab", title: "Copy Original", contexts: ["browser_action"] })
+    browser.contextMenus.create({ id: "redirectTab", title: "Redirect", contexts: ["browser_action"] })
+    browser.contextMenus.create({ id: "reverseTab", title: "Redirect To Original", contexts: ["browser_action"] })
 
-		case 'copyReverseBookmark': {
-			browser.bookmarks.get(info.bookmarkId, bookmarks => {
-				const url = new URL(bookmarks[0].url)
-				servicesHelper.copyRaw(url)
-			});
-			return
-		}
+    browser.contextMenus.create({ id: "redirectLink", title: "Redirect", contexts: ["link"] })
+    browser.contextMenus.create({ id: "redirectLinkInNewTab", title: "Redirect In New Tab", contexts: ["link"] })
+    browser.contextMenus.create({ id: "reverseLink", title: "Redirect To Original", contexts: ["link"] })
+    browser.contextMenus.create({
+      id: "reverseLinkInNewTab",
+      title: "Redirect To Original In New Tab",
+      contexts: ["link"],
+    })
+    browser.contextMenus.create({ id: "copyReverseLink", title: "Copy Original", contexts: ["link"] })
+    browser.contextMenus.create({ id: "bypassLink", title: "Bypass", contexts: ["link"] })
+    browser.contextMenus.create({ id: "bypassLinkInNewTab", title: "Bypass In New Tab", contexts: ["link"] })
 
-		case 'redirectBookmark':
-		case 'redirectBookmarkInNewTab': {
-			browser.bookmarks.get(info.bookmarkId, bookmarks => {
-				const url = new URL(bookmarks[0].url)
-				const newUrl = servicesHelper.redirect(url, "main_frame", null, true)
-				if (newUrl) {
-					if (info.menuItemId == 'redirectBookmark') browser.tabs.update({ url: newUrl })
-					else browser.tabs.create({ url: newUrl })
-				}
-			})
-			return
-		}
-		case 'reverseBookmark':
-		case 'reverseBookmarkInNewTab': {
-			browser.bookmarks.get(info.bookmarkId, async bookmarks => {
-				const url = new URL(bookmarks[0].url)
-				const newUrl = await servicesHelper.reverse(url)
-				if (newUrl) {
-					if (info.menuItemId == "reverseBookmark") {
-						browser.tabs.update({ url: newUrl }, tab => {
-							tabIdRedirects[tab.id] = false
-						})
-					} else {
-						browser.tabs.create({ url: newUrl }, tab => {
-							tabIdRedirects[tab.id] = false
-						})
-					}
-				}
-			})
-			return
-		}
+    if (!isChrome) {
+      browser.contextMenus.create({ id: "redirectBookmark", title: "Redirect", contexts: ["bookmark"] })
+      browser.contextMenus.create({
+        id: "redirectBookmarkInNewTab",
+        title: "Redirect In New Tab",
+        contexts: ["bookmark"],
+      })
+      browser.contextMenus.create({ id: "reverseBookmark", title: "Redirect To Original", contexts: ["bookmark"] })
+      browser.contextMenus.create({
+        id: "reverseBookmarkInNewTab",
+        title: "Redirect To Original In New Tab",
+        contexts: ["bookmark"],
+      })
+      browser.contextMenus.create({ id: "copyReverseBookmark", title: "Copy Original", contexts: ["bookmark"] })
+      browser.contextMenus.create({ id: "bypassBookmark", title: "Bypass", contexts: ["bookmark"] })
+      browser.contextMenus.create({ id: "bypassBookmarkInNewTab", title: "Bypass In New Tab", contexts: ["bookmark"] })
+    }
 
-		case 'bypassBookmark':
-		case 'bypassBookmarkInNewTab': {
-			browser.bookmarks.get(info.bookmarkId, async bookmarks => {
-				const url = new URL(bookmarks[0].url)
-				if (info.menuItemId == "bypassBookmark") {
-					browser.tabs.update({ url: url.href }, tab => {
-						tabIdRedirects[tab.id] = false
-					})
-				} else {
-					browser.tabs.create({ url: url.href }, tab => {
-						tabIdRedirects[tab.id] = false
-					})
-				}
-				return
-			})
-		}
-	}
+    browser.contextMenus.onClicked.addListener(async info => {
+      switch (info.menuItemId) {
+        case "switchInstanceTab": {
+          const url = new URL(info.pageUrl)
+          const newUrl = await servicesHelper.switchInstance(url)
+          if (newUrl) browser.tabs.update({ url: newUrl })
+          return
+        }
+        case "settingsTab":
+          browser.runtime.openOptionsPage()
+          return
+        case "copyReverseTab":
+          browser.tabs.query({ active: true, currentWindow: true }, async tabs => {
+            if (tabs[0].url) {
+              const url = new URL(tabs[0].url)
+              servicesHelper.copyRaw(url)
+            }
+          })
+          return
+        case "reverseTab":
+          browser.tabs.query({ active: true, currentWindow: true }, async tabs => {
+            if (tabs[0].url) {
+              const url = new URL(tabs[0].url)
+              const newUrl = await servicesHelper.reverse(url)
+              if (newUrl) {
+                browser.tabs.update(tabs[0].id, { url: newUrl }, () => {
+                  tabIdRedirects[tabs[0].id] = false
+                })
+              }
+            }
+          })
+          return
+        case "redirectTab":
+          browser.tabs.query({ active: true, currentWindow: true }, async tabs => {
+            if (tabs[0].url) {
+              const url = new URL(tabs[0].url)
+              const newUrl = servicesHelper.redirect(url, "main_frame", null, null, false, true)
+              if (newUrl) {
+                browser.tabs.update(tabs[0].id, { url: newUrl }, () => {
+                  tabIdRedirects[tabs[0].id] = true
+                })
+              }
+            }
+          })
+          return
+        case "copyReverseLink": {
+          const url = new URL(info.linkUrl)
+          await servicesHelper.copyRaw(url)
+          return
+        }
+        case "redirectLink":
+        case "redirectLinkInNewTab": {
+          const url = new URL(info.linkUrl)
+          const newUrl = servicesHelper.redirect(url, "main_frame", null, null, false, true)
+          if (newUrl) {
+            if (info.menuItemId == "redirectLink") browser.tabs.update({ url: newUrl })
+            else browser.tabs.create({ url: newUrl })
+          }
+          return
+        }
+        case "reverseLink":
+        case "reverseLinkInNewTab": {
+          const url = new URL(info.linkUrl)
+          const newUrl = await servicesHelper.reverse(url)
+          if (newUrl) {
+            if (info.menuItemId == "reverseLink") {
+              browser.tabs.update({ url: newUrl }, tab => {
+                tabIdRedirects[tab.id] = false
+              })
+            } else {
+              browser.tabs.create({ url: newUrl }, tab => {
+                tabIdRedirects[tab.id] = false
+              })
+            }
+          }
+          return
+        }
+        case "bypassLink":
+        case "bypassLinkInNewTab": {
+          const url = new URL(info.linkUrl)
+          if (info.menuItemId == "bypassLink") {
+            browser.tabs.update({ url: url.href }, tab => {
+              tabIdRedirects[tab.id] = false
+            })
+          } else {
+            browser.tabs.create({ url: url.href }, tab => {
+              tabIdRedirects[tab.id] = false
+            })
+          }
+          return
+        }
+        case "copyReverseBookmark":
+          browser.bookmarks.get(info.bookmarkId, bookmarks => {
+            const url = new URL(bookmarks[0].url)
+            servicesHelper.copyRaw(url)
+          })
+          return
+        case "redirectBookmark":
+        case "redirectBookmarkInNewTab":
+          browser.bookmarks.get(info.bookmarkId, bookmarks => {
+            const url = new URL(bookmarks[0].url)
+            const newUrl = servicesHelper.redirect(url, "main_frame", null, null, false, true)
+            if (newUrl) {
+              if (info.menuItemId == "redirectBookmark") browser.tabs.update({ url: newUrl })
+              else browser.tabs.create({ url: newUrl })
+            }
+          })
+          return
+        case "reverseBookmark":
+        case "reverseBookmarkInNewTab":
+          browser.bookmarks.get(info.bookmarkId, async bookmarks => {
+            const url = new URL(bookmarks[0].url)
+            const newUrl = await servicesHelper.reverse(url)
+            if (newUrl) {
+              if (info.menuItemId == "reverseBookmark") {
+                browser.tabs.update({ url: newUrl }, tab => {
+                  tabIdRedirects[tab.id] = false
+                })
+              } else {
+                browser.tabs.create({ url: newUrl }, tab => {
+                  tabIdRedirects[tab.id] = false
+                })
+              }
+            }
+          })
+          return
+        case "bypassBookmark":
+        case "bypassBookmarkInNewTab":
+          browser.bookmarks.get(info.bookmarkId, async bookmarks => {
+            const url = new URL(bookmarks[0].url)
+            if (info.menuItemId == "bypassBookmark") {
+              browser.tabs.update({ url: url.href }, tab => (tabIdRedirects[tab.id] = false))
+            } else {
+              browser.tabs.create({ url: url.href }, tab => (tabIdRedirects[tab.id] = false))
+            }
+            return
+          })
+      }
+    })
+  }
 })
 
-browser.runtime.onMessage.addListener((request, sender, sendResponse) => {
-	if (request == "reverseTab") {
-		browser.tabs.query({ active: true, currentWindow: true }, async tabs => {
-			if (tabs[0].url) {
-				const url = new URL(tabs[0].url)
-				const newUrl = await servicesHelper.reverse(url)
-				if (newUrl) {
-					browser.tabs.update(tabs[0].id, { url: newUrl }, () => {
-						tabIdRedirects[tabs[0].id] = false
-					})
-				}
-			}
-		})
-	}
-	else if (request == "redirectTab") {
-		browser.tabs.query({ active: true, currentWindow: true }, async tabs => {
-			if (tabs[0].url) {
-				const url = new URL(tabs[0].url)
-				const newUrl = servicesHelper.redirect(url, "main_frame", null, true)
-				if (newUrl) {
-					browser.tabs.update(tabs[0].id, { url: newUrl }, () => {
-						tabIdRedirects[tabs[0].id] = true
-					})
-				}
-			}
-		})
-	}
-})
\ No newline at end of file
+browser.runtime.onMessage.addListener(r => {
+  if (r.message == "reverse") tabIdRedirects[r.tabId] = false
+  else if (r.message == "redirect") tabIdRedirects[r.tabId] = true
+})
diff --git a/src/pages/components/Button.svelte b/src/pages/components/Button.svelte
new file mode 100644
index 00000000..6ae2ba61
--- /dev/null
+++ b/src/pages/components/Button.svelte
@@ -0,0 +1,35 @@
+<button {...$$restProps} on:click {...$$props}>
+  <slot></slot>
+</button>
+
+<style>
+  button {
+    color: var(--text);
+    border: none;
+    text-decoration: none;
+    cursor: pointer;
+    font-size: 16px;
+    font-weight: bold;
+    transition-duration: 0.1s;
+    display: inline-flex;
+    align-items: center;
+    margin: 7.5px 0;
+    background-color: var(--bg-secondary);
+    border-radius: 5px;
+    padding: 10px;
+  }
+
+  button:hover:enabled {
+    color: var(--active);
+  }
+
+  button:active:enabled {
+    transform: translateY(1px);
+  }
+
+  button:disabled {
+    cursor: not-allowed;
+    opacity: 0.5;
+  }
+  
+</style>
diff --git a/src/pages/components/Checkbox.svelte b/src/pages/components/Checkbox.svelte
new file mode 100644
index 00000000..d42a4f10
--- /dev/null
+++ b/src/pages/components/Checkbox.svelte
@@ -0,0 +1,52 @@
+<script>
+  export let checked
+  export let onChange
+</script>
+
+<input class={document.body.dir} {...$$restProps} bind:checked on:change={onChange} type="checkbox" />
+
+<style>
+  input[type="checkbox"] {
+    appearance: none;
+    -moz-appearance: none;
+    -webkit-appearance: none;
+    width: 46px;
+    height: 24px;
+    background-color: var(--light-grey);
+    border-radius: 50px;
+    transition: 0.4s;
+    cursor: pointer;
+  }
+
+  input[type="checkbox"]:checked {
+    background-color: var(--active);
+  }
+
+  input[type="checkbox"]::before {
+    content: "";
+    display: inline-block;
+    width: 18px;
+    height: 18px;
+    box-sizing: border-box;
+    position: relative;
+    top: 3px;
+    left: 3.5px;
+    background-color: white;
+    border-radius: 50%;
+    transition: 0.3s;
+  }
+
+  input[type="checkbox"]:checked::before {
+    left: 24px;
+  }
+
+  input[type="checkbox"].rtl::before {
+    left: auto;
+    right: 3.5px;
+  }
+
+  input[type="checkbox"].rtl:checked::before {
+    left: auto;
+    right: 24px;
+  }
+</style>
diff --git a/src/pages/components/Input.svelte b/src/pages/components/Input.svelte
new file mode 100644
index 00000000..59e584db
--- /dev/null
+++ b/src/pages/components/Input.svelte
@@ -0,0 +1,46 @@
+<script>
+  export let value
+</script>
+
+<input
+  {...$$restProps}
+  bind:value
+  on:blur
+  on:change
+  on:click
+  on:contextmenu
+  on:focus
+  on:keydown
+  on:keypress
+  on:keyup
+  on:mouseover
+  on:mouseenter
+  on:mouseleave
+  on:paste
+  on:input
+/>
+
+<style>
+  input {
+    font-weight: bold;
+    box-sizing: border-box;
+    color: var(--text);
+    font-size: 16px;
+    padding: 8px;
+    background-color: var(--bg-secondary);
+    border: none;
+    margin: 0;
+    width: 400px;
+    border-radius: 3px;
+    outline-color: var(--active);
+  }
+
+  input:focus {
+    outline-color: var(--active);
+  }
+  @media (max-width: 715px) {
+    input {
+      width: 200px;
+    }
+  }
+</style>
diff --git a/src/pages/components/Label.svelte b/src/pages/components/Label.svelte
new file mode 100644
index 00000000..39930cd1
--- /dev/null
+++ b/src/pages/components/Label.svelte
@@ -0,0 +1,18 @@
+<span>
+  <slot></slot>
+</span>
+
+<style>
+  span {
+    font-size: 18px;
+  }
+
+  span :global(a) {
+    color: var(--text);
+    text-decoration: none;
+  }
+
+  span :global(a:hover) {
+    text-decoration: underline;
+  }
+</style>
diff --git a/src/pages/components/Row.svelte b/src/pages/components/Row.svelte
new file mode 100644
index 00000000..09246d98
--- /dev/null
+++ b/src/pages/components/Row.svelte
@@ -0,0 +1,12 @@
+<div {...$$restProps} on:click>
+  <slot></slot>
+</div>
+
+<style>
+  div {
+    justify-content: space-between;
+    display: flex;
+    align-items: center;
+    margin: 20px 0;
+  }
+</style>
diff --git a/src/pages/components/Select.svelte b/src/pages/components/Select.svelte
new file mode 100644
index 00000000..7829c53e
--- /dev/null
+++ b/src/pages/components/Select.svelte
@@ -0,0 +1,34 @@
+<script>
+  export let values
+  export let value
+  export let onChange
+  export let ariaLabel
+</script>
+
+<select bind:value on:change={onChange} aria-label={ariaLabel} on:change on:contextmenu on:input>
+  {#each values as option}
+    <option value={option.value}>{option.name}</option>
+  {/each}
+</select>
+
+<style>
+  select {
+    font-weight: bold;
+    box-sizing: border-box;
+    border-style: solid;
+    border-color: #767676;
+    color: var(--text);
+    font-size: 16px;
+    padding: 8px;
+    background-color: var(--bg-secondary);
+    border: none;
+    margin: 0;
+    max-width: 500px;
+    border-radius: 3px;
+  }
+
+  select:disabled {
+    opacity: 0.6;
+    cursor: not-allowed;
+  }
+</style>
diff --git a/src/pages/stylesheets/Inter-VariableFont_slnt,wght.ttf b/src/pages/fonts/Inter-VariableFont_slnt,wght.ttf
index 969a990f..969a990f 100644
--- a/src/pages/stylesheets/Inter-VariableFont_slnt,wght.ttf
+++ b/src/pages/fonts/Inter-VariableFont_slnt,wght.ttf
Binary files differdiff --git a/src/pages/stylesheets/Vazirmatn-VariableFont_wght.ttf b/src/pages/fonts/Vazirmatn-VariableFont_wght.ttf
index f4b97c01..f4b97c01 100644
--- a/src/pages/stylesheets/Vazirmatn-VariableFont_wght.ttf
+++ b/src/pages/fonts/Vazirmatn-VariableFont_wght.ttf
Binary files differdiff --git a/src/pages/fonts/styles.css b/src/pages/fonts/styles.css
new file mode 100644
index 00000000..754543b1
--- /dev/null
+++ b/src/pages/fonts/styles.css
@@ -0,0 +1,13 @@
+@font-face {
+  font-family: "Inter";
+  src: url("Inter-VariableFont_slnt,wght.ttf");
+  font-weight: normal;
+  font-style: normal;
+}
+
+@font-face {
+  font-family: "Vazirmatn";
+  src: url("Vazirmatn-VariableFont_wght.ttf");
+  font-weight: normal;
+  font-style: normal;
+}
\ No newline at end of file
diff --git a/src/pages/icons/AboutIcon.svelte b/src/pages/icons/AboutIcon.svelte
new file mode 100644
index 00000000..e113dd68
--- /dev/null
+++ b/src/pages/icons/AboutIcon.svelte
@@ -0,0 +1,11 @@
+<svg
+  {...$$props}
+  xmlns="http://www.w3.org/2000/svg"
+  height="24px"
+  viewBox="0 -960 960 960"
+  width="24px"
+  fill="currentColor"
+  ><path
+    d="M440-280h80v-240h-80v240Zm40-320q17 0 28.5-11.5T520-640q0-17-11.5-28.5T480-680q-17 0-28.5 11.5T440-640q0 17 11.5 28.5T480-600Zm0 520q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"
+  /></svg
+>
diff --git a/src/pages/icons/AddIcon.svelte b/src/pages/icons/AddIcon.svelte
new file mode 100644
index 00000000..ab26f078
--- /dev/null
+++ b/src/pages/icons/AddIcon.svelte
@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 0 24 24" width="20px" fill="currentColor">
+  <path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z" />
+</svg>
diff --git a/src/pages/icons/AutoPickIcon.svelte b/src/pages/icons/AutoPickIcon.svelte
new file mode 100644
index 00000000..86adfa28
--- /dev/null
+++ b/src/pages/icons/AutoPickIcon.svelte
@@ -0,0 +1,11 @@
+<svg
+  {...$$restProps}
+  xmlns="http://www.w3.org/2000/svg"
+  height="20px"
+  viewBox="0 -960 960 960"
+  width="20px"
+  fill="currentColor"
+  ><path
+    d="M144-144v-178l342-342-54-53 51-51 72 72 110.22-110.22q4.45-4.45 11.11-7.11Q683-816 691-816t15 2.5q7 2.5 12 7.5l87 88q4.55 5.83 7.27 12.64 2.73 6.8 2.73 14.58t-2.66 14.44q-2.67 6.66-7.11 11.1L696-556l72 73-51 51-54-54-341 342H144Zm72-72h76l320-320-75-76-321 320v76Zm424-385 90-91-38-39-91 90 39 40Zm0 0-39-40 39 40Z"
+  /></svg
+>
diff --git a/src/pages/icons/CloseIcon.svelte b/src/pages/icons/CloseIcon.svelte
new file mode 100644
index 00000000..ddfb29cb
--- /dev/null
+++ b/src/pages/icons/CloseIcon.svelte
@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 -960 960 960" width="20px" fill="currentColor"
+  ><path d="m291-240-51-51 189-189-189-189 51-51 189 189 189-189 51 51-189 189 189 189-51 51-189-189-189 189Z" /></svg
+>
diff --git a/src/pages/icons/CopyIcon.svelte b/src/pages/icons/CopyIcon.svelte
new file mode 100644
index 00000000..37c13f98
--- /dev/null
+++ b/src/pages/icons/CopyIcon.svelte
@@ -0,0 +1,12 @@
+<svg
+  xmlns="http://www.w3.org/2000/svg"
+  height="24px"
+  width="24px"
+  viewBox="0 0 24 24"
+  preserveAspectRatio="xMinYMin meet"
+  fill="currentColor"
+>
+  <path
+    d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"
+  />
+</svg>
diff --git a/src/pages/icons/ExportIcon.svelte b/src/pages/icons/ExportIcon.svelte
new file mode 100644
index 00000000..d155e5c5
--- /dev/null
+++ b/src/pages/icons/ExportIcon.svelte
@@ -0,0 +1,12 @@
+<svg
+  {...$$restProps}
+  xmlns="http://www.w3.org/2000/svg"
+  height="24px"
+  viewBox="0 0 24 24"
+  width="24px"
+  fill="currentColor"
+>
+  <path
+    d="M10.09 15.59L11.5 17l5-5-5-5-1.41 1.41L12.67 11H3v2h9.67l-2.58 2.59zM19 3H5c-1.11 0-2 .9-2 2v4h2V5h14v14H5v-4H3v4c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2z"
+  />
+</svg>
diff --git a/src/pages/icons/GeneralIcon.svelte b/src/pages/icons/GeneralIcon.svelte
new file mode 100644
index 00000000..b9429021
--- /dev/null
+++ b/src/pages/icons/GeneralIcon.svelte
@@ -0,0 +1,13 @@
+<svg
+  {...$$props}
+  xmlns="http://www.w3.org/2000/svg"
+  enable-background="new 0 0 24 24"
+  height="26px"
+  viewBox="0 0 24 24"
+  width="26px"
+  fill="currentColor"
+>
+  <path
+    d="M19.14,12.94c0.04-0.3,0.06-0.61,0.06-0.94c0-0.32-0.02-0.64-0.07-0.94l2.03-1.58c0.18-0.14,0.23-0.41,0.12-0.61 l-1.92-3.32c-0.12-0.22-0.37-0.29-0.59-0.22l-2.39,0.96c-0.5-0.38-1.03-0.7-1.62-0.94L14.4,2.81c-0.04-0.24-0.24-0.41-0.48-0.41 h-3.84c-0.24,0-0.43,0.17-0.47,0.41L9.25,5.35C8.66,5.59,8.12,5.92,7.63,6.29L5.24,5.33c-0.22-0.08-0.47,0-0.59,0.22L2.74,8.87 C2.62,9.08,2.66,9.34,2.86,9.48l2.03,1.58C4.84,11.36,4.8,11.69,4.8,12s0.02,0.64,0.07,0.94l-2.03,1.58 c-0.18,0.14-0.23,0.41-0.12,0.61l1.92,3.32c0.12,0.22,0.37,0.29,0.59,0.22l2.39-0.96c0.5,0.38,1.03,0.7,1.62,0.94l0.36,2.54 c0.05,0.24,0.24,0.41,0.48,0.41h3.84c0.24,0,0.44-0.17,0.47-0.41l0.36-2.54c0.59-0.24,1.13-0.56,1.62-0.94l2.39,0.96 c0.22,0.08,0.47,0,0.59-0.22l1.92-3.32c0.12-0.22,0.07-0.47-0.12-0.61L19.14,12.94z M12,15.6c-1.98,0-3.6-1.62-3.6-3.6 s1.62-3.6,3.6-3.6s3.6,1.62,3.6,3.6S13.98,15.6,12,15.6z"
+  ></path>
+</svg>
diff --git a/src/pages/icons/ImportIcon.svelte b/src/pages/icons/ImportIcon.svelte
new file mode 100644
index 00000000..f64d0ff6
--- /dev/null
+++ b/src/pages/icons/ImportIcon.svelte
@@ -0,0 +1,12 @@
+<svg
+  {...$$restProps}
+  xmlns="http://www.w3.org/2000/svg"
+  height="24px"
+  viewBox="0 0 24 24"
+  width="24px"
+  fill="currentColor"
+>
+  <path
+    d="M19 19H5V5h7V3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2v-7h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"
+  />
+</svg>
diff --git a/src/pages/icons/PingIcon.svelte b/src/pages/icons/PingIcon.svelte
new file mode 100644
index 00000000..34c4a37d
--- /dev/null
+++ b/src/pages/icons/PingIcon.svelte
@@ -0,0 +1,12 @@
+<svg
+  {...$$restProps}
+  xmlns="http://www.w3.org/2000/svg"
+  height="20px"
+  viewBox="0 0 24 24"
+  width="20px"
+  fill="currentColor"
+>
+  <path
+    d="M10.45 15.5q.6.6 1.55.587.95-.012 1.4-.687L19 7l-8.4 5.6q-.675.45-.712 1.375-.038.925.562 1.525ZM12 4q1.475 0 2.838.412Q16.2 4.825 17.4 5.65l-1.9 1.2q-.825-.425-1.712-.637Q12.9 6 12 6 8.675 6 6.338 8.337 4 10.675 4 14q0 1.05.287 2.075Q4.575 17.1 5.1 18h13.8q.575-.95.838-1.975Q20 15 20 13.9q0-.9-.212-1.75-.213-.85-.638-1.65l1.2-1.9q.75 1.175 1.188 2.5.437 1.325.462 2.75.025 1.425-.325 2.725-.35 1.3-1.025 2.475-.275.45-.75.7-.475.25-1 .25H5.1q-.525 0-1-.25t-.75-.7q-.65-1.125-1-2.387Q2 15.4 2 14q0-2.075.788-3.888.787-1.812 2.15-3.175Q6.3 5.575 8.125 4.787 9.95 4 12 4Zm.175 7.825Z"
+  /></svg
+>
diff --git a/src/pages/icons/RedirectIcon.svelte b/src/pages/icons/RedirectIcon.svelte
new file mode 100644
index 00000000..9392762a
--- /dev/null
+++ b/src/pages/icons/RedirectIcon.svelte
@@ -0,0 +1,10 @@
+<svg
+  xmlns="http://www.w3.org/2000/svg"
+  height="24px"
+  width="24px"
+  viewBox="0 0 24 24"
+  preserveAspectRatio="xMinYMin meet"
+  fill="currentColor"
+>
+  <path d="M7 20v-9q0-.825.588-1.413Q8.175 9 9 9h8.2l-1.6-1.6L17 6l4 4-4 4-1.4-1.4 1.6-1.6H9v9Z" />
+</svg>
diff --git a/src/pages/icons/RedirectToOriginalIcon.svelte b/src/pages/icons/RedirectToOriginalIcon.svelte
new file mode 100644
index 00000000..aad5c48e
--- /dev/null
+++ b/src/pages/icons/RedirectToOriginalIcon.svelte
@@ -0,0 +1,13 @@
+<svg
+  xmlns="http://www.w3.org/2000/svg"
+  height="24px"
+  width="24px"
+  viewBox="0 0 24 24"
+  preserveAspectRatio="xMinYMin meet"
+  fill="currentColor"
+>
+  <path
+    d="M 17,20 V 11 Q 17,10.175 16.412,9.587 15.825,9 15,9 H 6.8 L 8.4,7.4 7,6 3,10 7,14 8.4,12.6 6.8,11 H 15 v 9 z"
+    id="path2"
+  />
+</svg>
diff --git a/src/pages/icons/ResetIcon.svelte b/src/pages/icons/ResetIcon.svelte
new file mode 100644
index 00000000..6daf57e6
--- /dev/null
+++ b/src/pages/icons/ResetIcon.svelte
@@ -0,0 +1,16 @@
+<svg
+  xmlns="http://www.w3.org/2000/svg"
+  enable-background="new 0 0 24 24"
+  height="24px"
+  viewBox="0 0 24 24"
+  width="24px"
+  fill="currentColor"
+  {...$$restProps}
+>
+  <path
+    d="M12,5V2L8,6l4,4V7c3.31,0,6,2.69,6,6c0,2.97-2.17,5.43-5,5.91v2.02c3.95-0.49,7-3.85,7-7.93C20,8.58,16.42,5,12,5z"
+  />
+  <path
+    d="M6,13c0-1.65,0.67-3.15,1.76-4.24L6.34,7.34C4.9,8.79,4,10.79,4,13c0,4.08,3.05,7.44,7,7.93v-2.02 C8.17,18.43,6,15.97,6,13z"
+  />
+</svg>
diff --git a/src/pages/icons/ServicesIcon.svelte b/src/pages/icons/ServicesIcon.svelte
new file mode 100644
index 00000000..ec24259b
--- /dev/null
+++ b/src/pages/icons/ServicesIcon.svelte
@@ -0,0 +1,11 @@
+<svg
+  {...$$props}
+  xmlns="http://www.w3.org/2000/svg"
+  fill="currentColor"
+  height="24px"
+  viewBox="0 -960 960 960"
+  width="24px"
+  ><path
+    d="m240-120 240-240 240 240H240ZM80-280v-480q0-33 23.5-56.5T160-840h640q33 0 56.5 23.5T880-760v480q0 33-23.5 56.5T800-200H680v-80h120v-480H160v480h120v80H160q-33 0-56.5-23.5T80-280Zm400-200Z"
+  /></svg
+>
diff --git a/src/pages/icons/SettingsIcon.svelte b/src/pages/icons/SettingsIcon.svelte
new file mode 100644
index 00000000..00798289
--- /dev/null
+++ b/src/pages/icons/SettingsIcon.svelte
@@ -0,0 +1,15 @@
+<svg
+  {...$$restProps}
+  xmlns="http://www.w3.org/2000/svg"
+  height="24px"
+  width="24px"
+  viewBox="0 0 24 24"
+  preserveAspectRatio="xMinYMin meet"
+  fill="currentColor"
+  on:click
+  on:keydown={null}
+>
+  <path
+    d="m9.25 22-.4-3.2q-.325-.125-.612-.3-.288-.175-.563-.375L4.7 19.375l-2.75-4.75 2.575-1.95Q4.5 12.5 4.5 12.337v-.675q0-.162.025-.337L1.95 9.375l2.75-4.75 2.975 1.25q.275-.2.575-.375.3-.175.6-.3l.4-3.2h5.5l.4 3.2q.325.125.613.3.287.175.562.375l2.975-1.25 2.75 4.75-2.575 1.95q.025.175.025.337v.675q0 .163-.05.338l2.575 1.95-2.75 4.75-2.95-1.25q-.275.2-.575.375-.3.175-.6.3l-.4 3.2Zm2.8-6.5q1.45 0 2.475-1.025Q15.55 13.45 15.55 12q0-1.45-1.025-2.475Q13.5 8.5 12.05 8.5q-1.475 0-2.488 1.025Q8.55 10.55 8.55 12q0 1.45 1.012 2.475Q10.575 15.5 12.05 15.5Z"
+  />
+</svg>
diff --git a/src/pages/icons/SwitchInstanceIcon.svelte b/src/pages/icons/SwitchInstanceIcon.svelte
new file mode 100644
index 00000000..6a1f96ac
--- /dev/null
+++ b/src/pages/icons/SwitchInstanceIcon.svelte
@@ -0,0 +1,15 @@
+<svg
+  {...$$restProps}
+  xmlns="http://www.w3.org/2000/svg"
+  height="24px"
+  width="24px"
+  viewBox="0 0 24 24"
+  preserveAspectRatio="xMinYMin meet"
+  fill="currentColor"
+  on:click
+  on:keydown={null}
+>
+  <path
+    d="M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6 0 1.01-.25 1.97-.7 2.8l1.46 1.46C19.54 15.03 20 13.57 20 12c0-4.42-3.58-8-8-8zm0 14c-3.31 0-6-2.69-6-6 0-1.01.25-1.97.7-2.8L5.24 7.74C4.46 8.97 4 10.43 4 12c0 4.42 3.58 8 8 8v3l4-4-4-4v3z"
+  />
+</svg>
diff --git a/src/pages/messages/index.html b/src/pages/messages/index.html
new file mode 100644
index 00000000..8701c152
--- /dev/null
+++ b/src/pages/messages/index.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8" />
+    <meta name="viewport" content="width=device-width,initial-scale=1" />
+    <link rel="icon" type="image/x-icon" href="../../../assets/images/libredirect.svg" />
+    <title>Settings</title>
+    <link rel="stylesheet" href="build/bundle.css" />
+    <link rel="stylesheet" href="../fonts/styles.css" />
+    <script defer src="build/bundle.js"></script>
+  </head>
+
+  <body></body>
+</html>
diff --git a/src/pages/messages/no_instance.html b/src/pages/messages/no_instance.html
deleted file mode 100644
index 76ec19cf..00000000
--- a/src/pages/messages/no_instance.html
+++ /dev/null
@@ -1,26 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-
-<head>
-    <meta charset="UTF-8">
-    <meta http-equiv="X-UA-Compatible" content="IE=edge">
-    <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <link href="../stylesheets/styles.css" rel="stylesheet">
-    <title>No instances found</title>
-    <style>
-        #body {
-            display: flex;
-            justify-content: center;
-            align-items: center;
-            height: 100vh;
-        }
-    </style>
-</head>
-
-<body>
-    <div id="body">
-        <h1>You have no instance selected for this frontend</h1>
-    </div>
-</body>
-
-</html>
\ No newline at end of file
diff --git a/src/pages/messages_src/App.svelte b/src/pages/messages_src/App.svelte
new file mode 100644
index 00000000..1c5170dd
--- /dev/null
+++ b/src/pages/messages_src/App.svelte
@@ -0,0 +1,200 @@
+<script>
+  const browser = window.browser || window.chrome
+
+  import utils from "../../assets/javascripts/utils.js"
+  import { onDestroy } from "svelte"
+  import servicesHelper from "../../assets/javascripts/services.js"
+  import { onMount } from "svelte"
+
+  import { options, config, page } from "./stores"
+  import Button from "../components/Button.svelte"
+  import AutoPickIcon from "../icons/AutoPickIcon.svelte"
+  import SwitchInstanceIcon from "../icons/SwitchInstanceIcon.svelte"
+
+  let _options
+  const unsubscribeOptions = options.subscribe(val => {
+    if (val) {
+      _options = val
+      browser.storage.local.set({ options: val })
+    }
+  })
+
+  let _config
+  const unsubscribeConfig = config.subscribe(val => (_config = val))
+
+  onDestroy(() => {
+    unsubscribeOptions()
+    unsubscribeConfig()
+  })
+
+  onMount(async () => {
+    let opts = await utils.getOptions()
+    if (!opts) {
+      await servicesHelper.initDefaults()
+      opts = await utils.getOptions()
+    }
+    options.set(opts)
+    config.set(await utils.getConfig())
+  })
+
+  let _page
+  page.subscribe(val => (_page = val))
+
+  let style
+  $: if (_options) style = utils.style(_options, window)
+
+  let autoPicking = false
+
+  const params = new URLSearchParams(window.location.search)
+  const oldUrl = new URL(params.get("url"))
+
+  async function autoPick() {
+    const frontend = params.get("frontend")
+    autoPicking = true
+    const redirects = await utils.getList(_options)
+    const instances = utils.randomInstances(redirects[frontend]["clearnet"], 5)
+    const pings = await Promise.all([
+      ...instances.map(async instance => {
+        return [instance, await utils.ping(instance)]
+      }),
+    ])
+    pings.sort((a, b) => a[1] - b[1])
+    _options[frontend].push(pings[0][0])
+    options.set(_options)
+    autoPicking = false
+  }
+
+  async function autoPickInstance() {
+    await autoPick()
+    await redirectUrl()
+  }
+
+  async function enableService() {
+    const service = await servicesHelper.computeService(oldUrl)
+    _options[service].enabled = true
+    options.set(_options)
+    await redirectUrl()
+  }
+
+  async function redirectUrl() {
+    const newUrl = await servicesHelper.redirectAsync(oldUrl, "main_frame", null, null, false, true)
+    browser.tabs.update({ url: newUrl })
+  }
+
+  async function switchInstance() {
+    const newUrl = await servicesHelper.switchInstance(oldUrl)
+    browser.tabs.update({ url: newUrl })
+  }
+
+  async function removeInstance() {
+    const service = await servicesHelper.computeService(oldUrl)
+    const frontend = params.get("frontend")
+    const i = _options[frontend].findIndex(instance => oldUrl.href.startsWith(instance))
+    _options[frontend].splice(i, 1)
+    options.set(_options)
+    const newUrl = await servicesHelper.switchInstance(oldUrl, service)
+    browser.tabs.update({ url: newUrl })
+  }
+
+  async function removeAndAutoPickInstance() {
+    const service = await servicesHelper.computeService(oldUrl)
+
+    const frontend = params.get("frontend")
+    const i = _options[frontend].findIndex(instance => oldUrl.href.startsWith(instance))
+    _options[frontend].splice(i, 1)
+    options.set(_options)
+    await autoPick()
+    const newUrl = await servicesHelper.switchInstance(oldUrl, service)
+    browser.tabs.update({ url: newUrl })
+  }
+
+  async function addAutoPickInstance() {
+    await autoPick()
+    const newUrl = await servicesHelper.switchInstance(oldUrl)
+    browser.tabs.update({ url: newUrl })
+  }
+</script>
+
+{#if _options && _config}
+  <div class="main" dir="auto" {style}>
+    {#if params.get("message") == "disabled"}
+      <div>
+        <h1>You disabled redirections for this service</h1>
+        <Button on:click={enableService}>
+          {browser.i18n.getMessage("enable") || "Enable"}
+        </Button>
+      </div>
+    {:else if params.get("message") == "server_error"}
+      <!-- https://httpstat.us/403 for testing -->
+      <div>
+        <h1>Your selected instance gave out an error: {params.get("code")}</h1>
+        {#if _options[params.get("frontend")].length > 1}
+          <Button on:click={switchInstance}>
+            <SwitchInstanceIcon class="margin margin_{document.body.dir}" />
+            {browser.i18n.getMessage("switchInstance") || "Switch Instance"}
+          </Button>
+          <Button on:click={removeInstance}>
+            <SwitchInstanceIcon class="margin margin_{document.body.dir}" />
+            {browser.i18n.getMessage("removeInstance") || "Remove Instance"}
+            +
+            {browser.i18n.getMessage("switchInstance") || "Switch Instance"}
+          </Button>
+        {:else}
+          <Button on:click={addAutoPickInstance} disabled={autoPicking}>
+            <AutoPickIcon class="margin margin_{document.body.dir}" />
+            {browser.i18n.getMessage("autoPickInstance") || "Auto Pick Instance"}
+          </Button>
+          <Button on:click={removeAndAutoPickInstance} disabled={autoPicking}>
+            <AutoPickIcon class="margin margin_{document.body.dir}" />
+            {browser.i18n.getMessage("removeInstance") || "Remove Instance"}
+            +
+            {browser.i18n.getMessage("autoPickInstance") || "Auto Pick Instance"}
+          </Button>
+        {/if}
+      </div>
+    {:else if params.get("message") == "no_instance"}
+      <div>
+        <h1>You have no instance selected for this frontend</h1>
+        <Button on:click={autoPickInstance} disabled={autoPicking}>
+          <AutoPickIcon class="margin margin_{document.body.dir}" />
+          {browser.i18n.getMessage("autoPickInstance") || "Auto Pick Instance"}
+        </Button>
+      </div>
+    {/if}
+  </div>
+{:else}
+  <p>Loading...</p>
+{/if}
+
+<style>
+  :global(body) {
+    width: 100vw;
+    height: 100vh;
+    margin: 0;
+    padding: 0;
+  }
+
+  div.main {
+    height: 100%;
+    display: grid;
+    grid-template-columns: 800px;
+    margin: 0;
+    padding-top: 50px;
+    justify-content: center;
+    font-family: "Inter", sans-serif;
+    box-sizing: border-box;
+    font-size: 16px;
+    background-color: var(--bg-main);
+    color: var(--text);
+    overflow: scroll;
+  }
+
+  :global(.margin) {
+    margin-right: 10px;
+    margin-left: 0;
+  }
+  :global(.margin_rtl) {
+    margin-right: 0;
+    margin-left: 10px;
+  }
+</style>
diff --git a/src/pages/messages_src/main.js b/src/pages/messages_src/main.js
new file mode 100644
index 00000000..c4012f4a
--- /dev/null
+++ b/src/pages/messages_src/main.js
@@ -0,0 +1,7 @@
+import App from "./App.svelte"
+
+const app = new App({
+  target: document.body,
+})
+
+export default app
diff --git a/src/pages/messages_src/stores.js b/src/pages/messages_src/stores.js
new file mode 100644
index 00000000..782f6064
--- /dev/null
+++ b/src/pages/messages_src/stores.js
@@ -0,0 +1,5 @@
+import { writable } from "svelte/store"
+
+export const options = writable(null)
+export const config = writable(null)
+export const page = writable("general")
diff --git a/src/pages/options/index.html b/src/pages/options/index.html
new file mode 100644
index 00000000..b197d4a7
--- /dev/null
+++ b/src/pages/options/index.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+	<meta charset='utf-8'>
+	<meta name='viewport' content='width=device-width,initial-scale=1'>
+	<link rel="icon" type="image/x-icon" href="../../../assets/images/libredirect.svg">
+	<title>Settings</title>
+	<link rel='stylesheet' href='build/bundle.css'>
+	<link rel='stylesheet' href='../fonts/styles.css'>
+	<script defer src='build/bundle.js'></script>
+</head>
+
+<body>
+</body>
+
+</html>
\ No newline at end of file
diff --git a/src/pages/options/index.js b/src/pages/options/index.js
deleted file mode 100644
index fcc51298..00000000
--- a/src/pages/options/index.js
+++ /dev/null
@@ -1,399 +0,0 @@
-import utils from "../../assets/javascripts/utils.js"
-
-let config,
-	options,
-	divs = {}
-
-for (const a of document.getElementById("links").getElementsByTagName("a")) {
-	if (!a.href.includes("https://")) {
-		a.addEventListener("click", e => {
-			const path = a.getAttribute("href").replace("#", "")
-			loadPage(path)
-			e.preventDefault()
-		})
-	}
-}
-
-config = await utils.getConfig()
-options = await utils.getOptions()
-
-/**
- * @param {string} service
- */
-async function changeFrontendsSettings(service) {
-	options = await utils.getOptions()
-	const opacityDiv = document.getElementById(`${service}-opacity`)
-	if (document.getElementById(`${service}-enabled`).checked) {
-		opacityDiv.style.pointerEvents = 'auto'
-		opacityDiv.style.opacity = 1
-		opacityDiv.style.userSelect = 'auto'
-	} else {
-		opacityDiv.style.pointerEvents = 'none'
-		opacityDiv.style.opacity = 0.4
-		opacityDiv.style.userSelect = 'none'
-	}
-	for (const frontend in config.services[service].frontends) {
-		if (config.services[service].frontends[frontend].instanceList) {
-			const frontendDiv = document.getElementById(frontend)
-			if (typeof divs[service].frontend !== "undefined") {
-				if (
-					frontend == divs[service].frontend.value
-					||
-					(config.services[service].frontends[divs[service].frontend.value].desktopApp && divs[service].embedFrontend && frontend == divs[service].embedFrontend.value)
-				) {
-					frontendDiv.style.display = ""
-					if (config.services[service].frontends[frontend].localhost === true) {
-						document.getElementById(`${service}-instance-div`).style.display = ""
-
-						if (options[service].instance == "localhost") {
-							frontendDiv.style.display = "none"
-						}
-					} else {
-						document.getElementById(`${service}-instance-div`).style.display = "none"
-					}
-				} else {
-					frontendDiv.style.display = "none"
-				}
-			}
-		}
-	}
-	if (document.getElementById(`${service}-redirectType`)) {
-		const frontend = options[service].frontend
-		if (config.services[service].frontends[frontend].embeddable) {
-			document.getElementById(`${service}-redirectType`).innerHTML = `
-			<option value="both" data-localise="__MSG_both__">both</options>
-			<option value="sub_frame" data-localise="__MSG_onlyEmbedded__">Only Embedded</option>
-			<option value="main_frame" data-localise="__MSG_onlyNotEmbedded__">Only Not Embedded</option>
-			`
-		}
-		else if (config.services[service].frontends[frontend].desktopApp && Object.values(config.services[service].frontends).some(frontend => frontend.embeddable)) {
-			document.getElementById(`${service}-redirectType`).innerHTML = `
-			<option value="both" data-localise="__MSG_both__">both</options>
-			<option value="main_frame" data-localise="__MSG_onlyNotEmbedded__">Only Not Embedded</option>
-			`
-			if (options[service].redirectType == "sub_frame") {
-				options[service].redirectType = "main_frame"
-				browser.storage.local.set({ options })
-			}
-		} else {
-			document.getElementById(`${service}-redirectType`).innerHTML =
-				'<option value="main_frame" data-localise="__MSG_onlyNotEmbedded__">Only Not Embedded</option>'
-			options[service].redirectType = "main_frame"
-
-			browser.storage.local.set({ options })
-		}
-		document.getElementById(`${service}-redirectType`).value = options[service].redirectType
-		if (config.services[service].frontends[frontend].desktopApp && options[service].redirectType != "main_frame") {
-			document.getElementById(`${service}-embedFrontend-div`).style.display = ''
-			document.getElementById(divs[service].embedFrontend.value).style.display = ''
-		}
-		else if (config.services[service].frontends[frontend].desktopApp && options[service].redirectType == "main_frame") {
-			document.getElementById(`${service}-embedFrontend-div`).style.display = 'none'
-			document.getElementById(divs[service].embedFrontend.value).style.display = 'none'
-		} else {
-			document.getElementById(`${service}-embedFrontend-div`).style.display = 'none'
-		}
-	}
-	const frontend_name_element = document.getElementById(`${service}_page`).getElementsByClassName("frontend_name")[0]
-	frontend_name_element.href = config.services[service].frontends[divs[service].frontend.value].url
-}
-
-/**
- * @param {string} path
- */
-async function loadPage(path) {
-	options = await utils.getOptions()
-	for (const section of document.getElementById("pages").getElementsByTagName("section")) section.style.display = "none"
-	document.getElementById(`${path}_page`).style.display = "block"
-
-	for (const element of document.getElementsByClassName("title")) {
-		const a = element.getElementsByTagName('a')[0]
-		if (a.getAttribute("href") == `#${path}`) {
-			element.classList.add("selected")
-		} else {
-			element.classList.remove("selected")
-		}
-	}
-
-	for (const service in config.services) {
-		if (options[service].enabled) {
-			document.getElementById(`${service}-link`).style.opacity = 1
-		} else {
-			document.getElementById(`${service}-link`).style.opacity = 0.4
-		}
-	}
-
-	window.history.pushState({ id: "100" }, "Page 2", `/pages/options/index.html#${path}`)
-
-	if (path != 'general') {
-		const service = path;
-
-		divs[service] = {}
-
-		for (const option in config.services[service].options) {
-			divs[service][option] = document.getElementById(`${service}-${option}`)
-			if (typeof config.services[service].options[option] == "boolean") divs[service][option].checked = options[service][option]
-			else divs[service][option].value = options[service][option]
-			divs[service][option].addEventListener("change", async () => {
-				let options = await utils.getOptions()
-				if (typeof config.services[service].options[option] == "boolean")
-					options[service][option] = divs[service][option].checked
-				else
-					options[service][option] = divs[service][option].value
-				browser.storage.local.set({ options })
-				changeFrontendsSettings(service)
-			})
-		}
-
-		changeFrontendsSettings(service)
-
-
-		for (const frontend in config.services[service].frontends) {
-			if (config.services[service].frontends[frontend].instanceList) {
-				processCustomInstances(frontend, document)
-				document.getElementById(`ping-${frontend}`).addEventListener("click", async () => {
-					document.getElementById(`ping-${frontend}`).getElementsByTagName('x')[0].innerHTML = "Pinging..."
-					await ping(frontend)
-					document.getElementById(`ping-${frontend}`).getElementsByTagName('x')[0].innerHTML = "Ping instances"
-				})
-			}
-		}
-
-		!async function () {
-			const blacklist = await utils.getBlacklist(options)
-			const redirects = await utils.getList(options)
-
-			for (const frontend in config.services[service].frontends) {
-				if (config.services[service].frontends[frontend].instanceList) {
-					if (redirects == 'disabled' || blacklist == 'disabled') {
-						document.getElementById(frontend).getElementsByClassName('clearnet')[0].style.display = 'none'
-						document.getElementById(frontend).getElementsByClassName('ping')[0].style.display = 'none'
-					}
-					else if (!redirects || !blacklist) {
-						document.getElementById(frontend)
-							.getElementsByClassName('clearnet')[0]
-							.getElementsByClassName("checklist")[0]
-							.getElementsByClassName('loading')[0]
-							.innerHTML = 'Could not fetch instances.'
-					}
-					else {
-						createList(frontend, config.networks, document, redirects, blacklist)
-					}
-				}
-			}
-		}()
-	}
-}
-
-async function calcCustomInstances(frontend) {
-	let options = await utils.getOptions()
-	let customInstances = options[frontend]
-	const pingCache = await utils.getPingCache()
-
-	document.getElementById(frontend).getElementsByClassName("custom-checklist")[0].innerHTML = customInstances
-		.map(
-			x => {
-				let time = pingCache[x]
-				let timeText = ""
-				if (time) {
-					const { color, text } = processTime(time)
-					timeText = `<span class="ping" style="color:${color};">${text}</span>`
-				}
-				return `<div>
-							<x>
-								<a href="${x}" target="_blank">${x}</a>
-								${timeText}
-							</x>
-							<button class="add clear-${x}">
-								<svg xmlns="https://www.w3.org/2000/svg" height="20px" viewBox="0 0 24 24" width="20px" fill="currentColor">
-									<path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z" />
-								</svg>
-							</button>
-						</div>
-						<hr>`
-			})
-		.join("\n")
-	for (const item of customInstances) {
-		document.getElementById(frontend).getElementsByClassName(`clear-${item}`)[0].addEventListener("click", async () => {
-			const index = customInstances.indexOf(item)
-			if (index > -1) customInstances.splice(index, 1)
-			options = await utils.getOptions()
-			options[frontend] = customInstances
-			browser.storage.local.set({ options }, async () => {
-				calcCustomInstances(frontend)
-				const blacklist = await utils.getBlacklist(options)
-				const redirects = await utils.getList(options)
-				createList(frontend, config.networks, document, redirects, blacklist)
-			})
-		})
-	}
-}
-
-async function processCustomInstances(frontend, document) {
-	calcCustomInstances(frontend)
-	document.getElementById(frontend).getElementsByClassName("custom-instance-form")[0].addEventListener("submit", async event => {
-		event.preventDefault()
-		let options = await utils.getOptions()
-		let customInstances = options[frontend]
-		let frontendCustomInstanceInput = document.getElementById(frontend).getElementsByClassName("custom-instance")[0]
-		let url
-		try {
-			url = new URL(frontendCustomInstanceInput.value)
-		} catch (error) {
-			return
-		}
-		let protocolHostVar = utils.protocolHost(url)
-		if (frontendCustomInstanceInput.validity.valid) {
-			if (!customInstances.includes(protocolHostVar)) {
-				customInstances.push(protocolHostVar)
-				options = await utils.getOptions()
-				options[frontend] = customInstances
-				browser.storage.local.set({ options }, () => {
-					frontendCustomInstanceInput.value = ""
-					calcCustomInstances(frontend)
-				})
-			}
-		}
-	})
-}
-
-/**
- * @param {string} frontend
- * @param {*} networks
- * @param {*} document
- * @param {*} redirects
- * @param {*} blacklist
- */
-async function createList(frontend, networks, document, redirects, blacklist) {
-	const pingCache = await utils.getPingCache()
-	const options = await utils.getOptions()
-	for (const network in networks) {
-		const checklist = document.getElementById(frontend)
-			.getElementsByClassName(network)[0]
-			.getElementsByClassName("checklist")[0]
-
-		if (!redirects[frontend]) {
-			checklist.innerHTML = '<div class="block block-option">No instances found.</div>'
-			break
-		}
-
-		const instances = redirects[frontend][network]
-		if (!instances || instances.length === 0) continue
-
-		document.getElementById(frontend)
-			.getElementsByClassName("custom-instance")[0]
-			.placeholder = redirects[frontend].clearnet[0]
-
-		const sortedInstances = instances.sort((a, b) => blacklist.cloudflare.includes(a) && !blacklist.cloudflare.includes(b))
-
-		const content = sortedInstances
-			.map(x => {
-				const cloudflare = blacklist.cloudflare.includes(x) ?
-					`<a target="_blank" href="https://libredirect.github.io/docs.html#instances">
-                        <span style="color:red;">cloudflare</span>
-                    </a>` : ""
-
-				let time = pingCache[x]
-				let timeText = ""
-				if (time) {
-					const { color, text } = processTime(time)
-					timeText = `<span class="ping" style="color:${color};">${text}</span>`
-				}
-
-				const chosen = options[frontend].includes(x) ? `<span style="color:grey;">chosen</span>` : ""
-
-				const warnings = [cloudflare, timeText, chosen].join(" ")
-				return `<div class="frontend">
-                            <x>
-                                <a href="${x}" target="_blank">${x}</a>
-								${warnings}
-                            </x>
-                            <button class="add add-${x}">
-                                <svg xmlns="https://www.w3.org/2000/svg" height="20px" viewBox="0 0 24 24" width="20px" fill="currentColor">
-                                    <path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z" />
-                                </svg>
-                            </button>
-                        </div>`
-			})
-
-		checklist.innerHTML = [
-			`<div class="block block-option">
-                <label>${utils.camelCase(network)}</label>
-            </div>`,
-			...content,
-			"<br>"
-		].join("\n<hr>\n")
-
-		for (const instance of instances) {
-			checklist.getElementsByClassName(`add-${instance}`)[0]
-				.addEventListener("click", async () => {
-					let options = await utils.getOptions()
-					if (!options[frontend].includes(instance)) {
-						options[frontend].push(instance)
-						browser.storage.local.set({ options }, () => {
-							calcCustomInstances(frontend)
-							createList(frontend, config.networks, document, redirects, blacklist)
-						})
-					}
-				})
-		}
-	}
-}
-
-const r = window.location.href.match(/#(.*)/)
-if (r) loadPage(r[1])
-else loadPage("general")
-
-/**
- * @param {string} frontend
- */
-async function ping(frontend) {
-	const instanceElements = [
-		...document.getElementById(frontend).getElementsByClassName("custom-checklist")[0].getElementsByTagName('x'),
-		...document.getElementById(frontend).getElementsByClassName('clearnet')[0].getElementsByTagName('x')
-	]
-
-	let pingCache = await utils.getPingCache()
-	let redundancyList = {}
-	for (const element of instanceElements) {
-		let span = element.getElementsByClassName('ping')[0]
-		if (!span) span = document.createElement('span')
-		span.classList = ['ping']
-		span.innerHTML = '<span style="color:lightblue">pinging...</span>'
-		element.appendChild(span)
-		const href = element.getElementsByTagName('a')[0].href
-		const innerHTML = element.getElementsByTagName('a')[0].innerHTML
-		const time = redundancyList[innerHTML] ?? await utils.ping(href)
-		const { color, text } = processTime(time)
-		span.innerHTML = `<span style="color:${color};">${text}</span>`
-		pingCache[innerHTML] = time
-		redundancyList[innerHTML] = time
-
-		browser.storage.local.set({ pingCache })
-	}
-}
-
-/**
- * @param {number} time
- */
-function processTime(time) {
-	let text
-	let color
-	if (time < 5000) {
-		text = `${time}ms`
-		if (time <= 1000) color = "green"
-		else if (time <= 2000) color = "orange"
-	}
-	else if (time >= 5000) {
-		color = "red"
-		if (time == 5000) text = "5000ms+"
-		if (time > 5000) text = `Error: ${time - 5000}`
-	}
-	else {
-		color = "red"
-		text = 'Server not found'
-	}
-	return {
-		color, text
-	}
-}
diff --git a/src/pages/options/index.pug b/src/pages/options/index.pug
deleted file mode 100644
index 4e19b087..00000000
--- a/src/pages/options/index.pug
+++ /dev/null
@@ -1,10 +0,0 @@
-doctype html
-html(id="elementToShowWithJavaScript" lang="en")
-    include /src/pages/widgets/head
-    body(class="option" dir="auto")
-        include /src/pages/widgets/links
-        div#pages
-            include /src/pages/options/widgets/general
-            include /src/pages/options/widgets/services
-    script(type="module" src="./index.js")
-    
\ No newline at end of file
diff --git a/src/pages/options/init.js b/src/pages/options/init.js
deleted file mode 100644
index f88c9ef9..00000000
--- a/src/pages/options/init.js
+++ /dev/null
@@ -1,54 +0,0 @@
-window.browser = window.browser || window.chrome
-
-import localise from "../../assets/javascripts/localise.js"
-import utils from "../../assets/javascripts/utils.js"
-import servicesHelper from "../../assets/javascripts/services.js"
-
-if (!(await utils.getOptions())) {
-	await servicesHelper.initDefaults()
-}
-
-function changeTheme() {
-	return new Promise(async resolve => {
-		switch ((await utils.getOptions()).theme) {
-			case "dark":
-				document.body.classList.add("dark-theme")
-				document.body.classList.remove("light-theme")
-				for (const element of document.body.getElementsByClassName('dark')) {
-					element.style.display = 'none';
-				}
-				break
-			case "light":
-				document.body.classList.add("light-theme")
-				document.body.classList.remove("dark-theme")
-				for (const element of document.body.getElementsByClassName('light')) {
-					element.style.display = 'none';
-				}
-				break
-			default:
-				if (matchMedia("(prefers-color-scheme: light)").matches) {
-					document.body.classList.add("light-theme")
-					document.body.classList.remove("dark-theme")
-					for (const element of document.body.getElementsByClassName('light')) {
-						element.style.display = 'none';
-					}
-				} else {
-					document.body.classList.add("dark-theme")
-					document.body.classList.remove("light-theme")
-					for (const element of document.body.getElementsByClassName('dark')) {
-						element.style.display = 'none';
-					}
-				}
-		}
-		resolve()
-	})
-}
-
-changeTheme()
-if (["ar", "iw", "ku", "fa", "ur"].includes(browser.i18n.getUILanguage())) {
-	document.getElementsByTagName("body")[0].classList.add("rtl")
-	document.getElementsByTagName("body")[0].dir = "rtl"
-}
-localise.localisePage()
-
-window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", changeTheme)
diff --git a/src/pages/options/widgets/general.js b/src/pages/options/widgets/general.js
deleted file mode 100644
index 6f2852a9..00000000
--- a/src/pages/options/widgets/general.js
+++ /dev/null
@@ -1,219 +0,0 @@
-"use strict"
-window.browser = window.browser || window.chrome
-
-import utils from "../../../assets/javascripts/utils.js"
-import servicesHelper from "../../../assets/javascripts/services.js"
-
-const isChrome = browser.runtime.getBrowserInfo === undefined
-
-async function setOption(option, type, event) {
-	let options = await utils.getOptions()
-	if (type == "select") {
-		options[option] = event.target.options[event.target.options.selectedIndex].value
-	} else if (type == "checkbox") {
-		options[option] = event.target.checked
-	} else if (type == "range") {
-		options[option] = event.target.value
-	}
-	browser.storage.local.set({ options })
-}
-
-const exportSettingsElement = document.getElementById("export-settings")
-async function exportSettings() {
-	const options = await utils.getOptions()
-	options.version = browser.runtime.getManifest().version
-	let resultString = JSON.stringify(options, null, "  ")
-	exportSettingsElement.href = "data:application/json;base64," + btoa(resultString)
-	exportSettingsElement.download = `libredirect-settings-v${options.version}.json`
-	return
-}
-exportSettings()
-document.getElementById("general_page").onclick = exportSettings
-
-const importSettingsElement = document.getElementById("import-settings")
-const importSettingsElementText = document.getElementById("import_settings_text")
-importSettingsElement.addEventListener("change", () => {
-	function importError() {
-		const oldHTML = importSettingsElementText.innerHTML
-		importSettingsElementText.innerHTML = '<span style="color:red;">Error!</span>'
-		setTimeout(() => (importSettingsElementText.innerHTML = oldHTML), 1000)
-	}
-	importSettingsElementText.innerHTML = "..."
-	let file = importSettingsElement.files[0]
-	const reader = new FileReader()
-	reader.readAsText(file)
-	reader.onload = async () => {
-		const data = JSON.parse(reader.result)
-		if (
-			"theme" in data
-			&& data.version == browser.runtime.getManifest().version
-		) {
-			browser.storage.local.clear(async () => {
-				browser.storage.local.set({ options: data }, () => {
-					location.reload()
-				})
-			})
-		} else {
-			console.log("incompatible settings")
-			importError()
-		}
-	}
-	reader.onerror = error => {
-		console.log("error", error)
-		importError()
-	}
-})
-
-const exportSettingsSync = document.getElementById("export-settings-sync")
-const importSettingsSync = document.getElementById("import-settings-sync")
-const importSettingsSyncText = document.getElementById("import_settings_sync_text")
-
-exportSettingsSync.addEventListener("click", async () => {
-	let options = await utils.getOptions()
-	options.version = browser.runtime.getManifest().version
-	browser.storage.sync.set({ options }, () => location.reload())
-})
-
-importSettingsSync.addEventListener("click", () => {
-	function importError() {
-		importSettingsSyncText.innerHTML = '<span style="color:red;">Error!</span>'
-		setTimeout(() => (importSettingsSyncText.innerHTML = oldHTML), 1000)
-	}
-	const oldHTML = importSettingsSyncText.innerHTML
-	importSettingsSyncText.innerHTML = "..."
-	browser.storage.sync.get({ options }, r => {
-		const options = r.options
-		if (options.version == browser.runtime.getManifest().version) {
-			browser.storage.local.set({ options }, () => location.reload())
-		} else {
-			importError()
-		}
-	})
-})
-
-const resetSettings = document.getElementById("reset-settings")
-resetSettings.addEventListener("click", async () => {
-	resetSettings.innerHTML = "..."
-	await servicesHelper.initDefaults()
-	location.reload()
-})
-
-const fetchInstancesElement = document.getElementById('fetch-instances')
-fetchInstancesElement.addEventListener('change', event => {
-	setOption('fetchInstances', 'select', event)
-	location.reload()
-})
-
-const redirectOnlyInIncognitoElement = document.getElementById('redirectOnlyInIncognito')
-redirectOnlyInIncognitoElement.addEventListener('change', event => {
-	setOption('redirectOnlyInIncognito', 'checkbox', event)
-})
-
-const bookmarksMenuElement = document.getElementById('bookmarksMenu')
-bookmarksMenuElement.addEventListener('change', async event => {
-	if (event.target.checked)
-		bookmarksMenuElement.checked = await browser.permissions.request({
-			permissions: ["bookmarks"]
-		})
-	else
-		bookmarksMenuElement.checked = !await browser.permissions.remove({
-			permissions: ["bookmarks"]
-		})
-})
-
-let themeElement = document.getElementById("theme")
-themeElement.addEventListener("change", event => {
-	setOption("theme", "select", event)
-	location.reload()
-})
-
-let nameCustomInstanceInput = document.getElementById("exceptions-custom-instance")
-let instanceTypeElement = document.getElementById("exceptions-custom-instance-type")
-let instanceType = "url"
-
-let config = await utils.getConfig()
-
-for (const service in config.services) {
-	document.getElementById(service).addEventListener("change", async event => {
-		let options = await utils.getOptions()
-		if (event.target.checked && !options.popupServices.includes(service)) options.popupServices.push(service)
-		else if (options.popupServices.includes(service)) {
-			var index = options.popupServices.indexOf(service)
-			if (index !== -1) options.popupServices.splice(index, 1)
-		}
-		browser.storage.local.set({ options })
-	})
-}
-
-let options = await utils.getOptions()
-themeElement.value = options.theme
-fetchInstancesElement.value = options.fetchInstances
-redirectOnlyInIncognitoElement.checked = options.redirectOnlyInIncognito
-bookmarksMenuElement.checked = await browser.permissions.contains({ permissions: ["bookmarks"] })
-for (const service in config.services) document.getElementById(service).checked = options.popupServices.includes(service)
-
-instanceTypeElement.addEventListener("change", event => {
-	instanceType = event.target.options[instanceTypeElement.selectedIndex].value
-	if (instanceType == "url") {
-		nameCustomInstanceInput.setAttribute("type", "url")
-		nameCustomInstanceInput.setAttribute("placeholder", "https://www.google.com")
-	} else if (instanceType == "regex") {
-		nameCustomInstanceInput.setAttribute("type", "text")
-		nameCustomInstanceInput.setAttribute("placeholder", "https?://(www.|)youtube.com/")
-	}
-})
-
-let exceptionsCustomInstances = options.exceptions
-function calcExceptionsCustomInstances() {
-	document.getElementById("exceptions-custom-checklist").innerHTML = [...exceptionsCustomInstances.url, ...exceptionsCustomInstances.regex]
-		.map(
-			x => `<div>
-                      ${x}
-                      <button class="add" id="clear-${x}">
-                        <svg xmlns="https://www.w3.org/2000/svg" height="20px" viewBox="0 0 24 24" width="20px"
-                        fill="currentColor">
-                          <path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z" />
-                        </svg>
-                      </button>
-                    </div>
-                    <hr>`
-		)
-		.join("\n")
-
-	for (const x of [...exceptionsCustomInstances.url, ...exceptionsCustomInstances.regex]) {
-		document.getElementById(`clear-${x}`).addEventListener("click", async () => {
-			let index
-			index = exceptionsCustomInstances.url.indexOf(x)
-			if (index > -1) exceptionsCustomInstances.url.splice(index, 1)
-			else {
-				index = exceptionsCustomInstances.regex.indexOf(x)
-				if (index > -1) exceptionsCustomInstances.regex.splice(index, 1)
-			}
-			options = await utils.getOptions()
-			options.exceptions = exceptionsCustomInstances
-			browser.storage.local.set({ options })
-			calcExceptionsCustomInstances()
-		})
-	}
-}
-calcExceptionsCustomInstances()
-document.getElementById("custom-exceptions-instance-form").addEventListener("submit", async event => {
-	event.preventDefault()
-	let val
-	if (instanceType == "url" && nameCustomInstanceInput.validity.valid) {
-		val = nameCustomInstanceInput.value
-		if (!exceptionsCustomInstances.url.includes(val)) exceptionsCustomInstances.url.push(val)
-	} else if (instanceType == "regex") {
-		val = nameCustomInstanceInput.value
-		if (val.trim() != "" && !exceptionsCustomInstances.regex.includes(val)) exceptionsCustomInstances.regex.push(val)
-	}
-	if (val) {
-		options = await utils.getOptions()
-		options.exceptions = exceptionsCustomInstances
-		browser.storage.local.set({ options }, () =>
-			nameCustomInstanceInput.value = ""
-		)
-
-	}
-	calcExceptionsCustomInstances()
-})
diff --git a/src/pages/options/widgets/general.pug b/src/pages/options/widgets/general.pug
deleted file mode 100644
index 70316473..00000000
--- a/src/pages/options/widgets/general.pug
+++ /dev/null
@@ -1,88 +0,0 @@
-section(class="block-option" id="general_page")
-    div(class="block block-option")
-        h1(data-localise="__MSG_general__") General
-    hr
-
-    div(class="block block-option")
-        label(data-localise="__MSG_theme__") Theme
-        select(id="theme" aria-label="select theme")
-            option(value="detect" data-localise="__MSG_auto__") Auto
-            option(value="light" data-localise="__MSG_light__") Light
-            option(value="dark" data-localise="__MSG_dark__") Dark
-
-    div(class="block block-option")
-        label(data-localise="__MSG_fetchPublicInstances__") Fetch public instances
-        select(id="fetch-instances" aria-label="Select fetch public instances")
-            option(value="github") GitHub
-            option(value="codeberg") Codeberg
-            option(value="disable" data-localise="__MSG_disable__") Disable
-
-    div(class="block block-option")
-        label(for='redirectOnlyInIncognito' data-localise="__MSG_redirectOnlyInIncognito__") Redirect Only in Incognito
-        input(id='redirectOnlyInIncognito' type="checkbox")
-
-    div(class="block block-option")
-        label(for='bookmarksMenu' data-localise="__MSG_bookmarksMenu__") Bookmarks menu
-        input(id='bookmarksMenu' type="checkbox")
-
-    div(class="block block-option")
-        label(data-localise="__MSG_excludeFromRedirecting__") Excluded from redirecting
-
-    form(id="custom-exceptions-instance-form")
-        div(class="block block-option")
-            div(class="block" style="padding: 0")
-                input(id="exceptions-custom-instance" placeholder="https://www.google.com" type="url" aria-label="Add url exception input")
-                |&nbsp;
-                select(id="exceptions-custom-instance-type")
-                    option(value="url") URL
-                    option(value="regex") Regex
-                |&nbsp;
-            button(class="add" id="exceptions-add-instance" type="submit" aria-label="Add the url exception")
-                svg(xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 0 24 24" width="20px" fill="currentColor")
-                    path(d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z")
-
-    hr
-
-    div(class="checklist" id="exceptions-custom-checklist")
-
-    div(class="buttons")
-        label(class="button button-inline" id="import_settings_text" for="import-settings")
-            svg(xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="currentColor")
-                path(d="M19 19H5V5h7V3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2v-7h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z")
-            |&nbsp;
-            x(data-localise="__MSG_importSettings__") Import Settings
-        input(id="import-settings" type="file" style="display: none")
-
-        |&nbsp;&nbsp;
-
-        a(class="button button-inline" id="export-settings")
-            svg(xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="currentColor")
-                path(d="M10.09 15.59L11.5 17l5-5-5-5-1.41 1.41L12.67 11H3v2h9.67l-2.58 2.59zM19 3H5c-1.11 0-2 .9-2 2v4h2V5h14v14H5v-4H3v4c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2z")
-            |&nbsp;
-            x(data-localise="__MSG_exportSettings__") Export Settings
-
-        |&nbsp;&nbsp;
-
-        button(class="button button-inline" id="export-settings-sync")
-            svg(xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="currentColor")
-                path(d="M10.09 15.59L11.5 17l5-5-5-5-1.41 1.41L12.67 11H3v2h9.67l-2.58 2.59zM19 3H5c-1.11 0-2 .9-2 2v4h2V5h14v14H5v-4H3v4c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2z")
-            |&nbsp;
-            x() Export Settings to Sync
-
-        |&nbsp;&nbsp;
-
-        button(class="button button-inline" id="import-settings-sync")
-            svg(xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="currentColor")
-                path(d="M19 19H5V5h7V3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2v-7h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z")
-            |&nbsp;
-            x(id="import_settings_sync_text") Import Settings from Sync
-
-        |&nbsp;&nbsp;
-
-        button(class="button button-inline" id="reset-settings")
-            svg(xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="currentColor")
-                path(d="M12,5V2L8,6l4,4V7c3.31,0,6,2.69,6,6c0,2.97-2.17,5.43-5,5.91v2.02c3.95-0.49,7-3.85,7-7.93C20,8.58,16.42,5,12,5z")
-                path(d="M6,13c0-1.65,0.67-3.15,1.76-4.24L6.34,7.34C4.9,8.79,4,10.79,4,13c0,4.08,3.05,7.44,7,7.93v-2.02 C8.17,18.43,6,15.97,6,13z")
-            x(data-localise="__MSG_resetSettings__") Reset Settings
-
-    script(type="module" src="./widgets/general.js")
\ No newline at end of file
diff --git a/src/pages/options/widgets/services.pug b/src/pages/options/widgets/services.pug
deleted file mode 100644
index e08bb001..00000000
--- a/src/pages/options/widgets/services.pug
+++ /dev/null
@@ -1,83 +0,0 @@
-each val, service in services
-    section(class="block-option" id=service+"_page")
-        div(class="block block-option")
-            h1
-                a(target="_blank" href=services[service].url)=services[service].name
-
-        hr
-
-        div(class="block block-option")
-            label(for=`${service}-enabled` data-localise="__MSG_enable__") Enable
-            input(id=`${service}-enabled` type="checkbox")
-
-        div(class="block block-option")
-            label(for=service data-localise="__MSG_showInPopup__") Show in popup
-            input(id=service type="checkbox")
-
-        div(id=service+"-opacity")
-
-            div(class="block block-option")
-                label(for=`${service}-frontend`)
-                    a(class="frontend_name" target="_blank" data-localise="__MSG_frontend__") Frontend
-                select(id=`${service}-frontend`)
-                    each val, frontend in services[service].frontends
-                        option(value=frontend)=services[service].frontends[frontend].name
-
-            div(class="block block-option" id=service+"-instance-div")
-                label(for=`${service}-instance`) Instance Type
-                select(id=`${service}-instance`)
-                    option(value="localhost") localhost
-                    option(value="public") public instances
-
-            div(class="block block-option")
-                label(for=`${service}-redirectType` data-localise="__MSG_redirectType__") Redirect Type
-                select(id=`${service}-redirectType`)
-
-
-            div(id=`${service}-embedFrontend-div` class="block block-option")
-                label(for=`${service}-embedFrontend` data-localise="__MSG_embedFrontend__") Embed Frontend
-                select(id=`${service}-embedFrontend`)
-                    each val, frontend in services[service].frontends
-                        if services[service].frontends[frontend].embeddable && services[service].frontends[frontend].instanceList
-                            option(value=frontend)=services[service].frontends[frontend].name
-
-
-            div(class="block block-option")
-                label(for=`${service}-unsupportedUrls` data-localise="__MSG_unsupportedIframesHandling__") Unsupported iframes handling
-                select(id=`${service}-unsupportedUrls`)
-                    option(value="bypass") bypass
-                    option(value="block") block
-
-            if (service == 'search')
-                div(class="block block-option")
-                    label Set LibRedirect as Default Search Engine. For how to do in chromium browsers, click <a href="https://libredirect.github.io/docs.html#search_engine_chromium">here</a>.
-
-
-            each val, frontend in services[service].frontends
-                if services[service].frontends[frontend].instanceList
-                    div(id=frontend dir="ltr")
-                        hr        
-                        div(dir="auto" class="block block-option")
-                            label(data-localise="__MSG_addYourFavoriteInstances__") Add your favorite instances
-
-                        form(class="custom-instance-form")
-                            div(class="block block-option")
-                                input(class="custom-instance" type="url" placeholder="https://instance.com" aria-label="Add instance input")
-                                button(class="add add-instance" type="submit" aria-label="Add the instance")
-                                    svg(xmlns="https://www.w3.org/2000/svg" height="20px" viewBox="0 0 24 24" width="20px" fill="currentColor")
-                                        path(d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z")
-
-                        div(class="checklist custom-checklist")  
-
-                        div(class="ping block")
-                            button(class="button button-inline" id=`ping-${frontend}`)
-                                svg(xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 0 24 24" width="20px" fill="currentColor")
-                                    path(d="M10.45 15.5q.6.6 1.55.587.95-.012 1.4-.687L19 7l-8.4 5.6q-.675.45-.712 1.375-.038.925.562 1.525ZM12 4q1.475 0 2.838.412Q16.2 4.825 17.4 5.65l-1.9 1.2q-.825-.425-1.712-.637Q12.9 6 12 6 8.675 6 6.338 8.337 4 10.675 4 14q0 1.05.287 2.075Q4.575 17.1 5.1 18h13.8q.575-.95.838-1.975Q20 15 20 13.9q0-.9-.212-1.75-.213-.85-.638-1.65l1.2-1.9q.75 1.175 1.188 2.5.437 1.325.462 2.75.025 1.425-.325 2.725-.35 1.3-1.025 2.475-.275.45-.75.7-.475.25-1 .25H5.1q-.525 0-1-.25t-.75-.7q-.65-1.125-1-2.387Q2 15.4 2 14q0-2.075.788-3.888.787-1.812 2.15-3.175Q6.3 5.575 8.125 4.787 9.95 4 12 4Zm.175 7.825Z")
-                                |&nbsp;
-                                x() Ping instances
-
-                        each val, network in networks
-                            div(class=network)
-                                div(class="checklist")
-                                    if (network == 'clearnet')
-                                        div(class="block block-option loading") Loading...
\ No newline at end of file
diff --git a/src/pages/options_src/App.svelte b/src/pages/options_src/App.svelte
new file mode 100644
index 00000000..1c4830bf
--- /dev/null
+++ b/src/pages/options_src/App.svelte
@@ -0,0 +1,101 @@
+<script>
+  const browser = window.browser || window.chrome
+
+  import General from "./General/General.svelte"
+  import url from "./url"
+  import utils from "../../assets/javascripts/utils.js"
+  import { onDestroy } from "svelte"
+  import servicesHelper from "../../assets/javascripts/services.js"
+  import { onMount } from "svelte"
+  import Sidebar from "./Sidebar.svelte"
+  import { options, config } from "./stores"
+  import Services from "./Services/Services.svelte"
+
+  let _options
+  const unsubscribeOptions = options.subscribe(val => {
+    if (val) {
+      _options = val
+      browser.storage.local.set({ options: val })
+    }
+  })
+
+  let _config
+  const unsubscribeConfig = config.subscribe(val => (_config = val))
+
+  onDestroy(() => {
+    unsubscribeOptions()
+    unsubscribeConfig()
+  })
+
+  onMount(async () => {
+    let opts = await utils.getOptions()
+    if (!opts) {
+      await servicesHelper.initDefaults()
+      opts = await utils.getOptions()
+    }
+    options.set(opts)
+    config.set(await utils.getConfig())
+  })
+
+  let style
+  $: if (_options) style = utils.style(_options, window)
+
+  const dir = ["ar", "iw", "ku", "fa", "ur"].includes(browser.i18n.getUILanguage()) ? "rtl" : "ltr"
+  document.body.dir = dir
+</script>
+
+{#if _options && _config}
+  <div class={dir} {dir} {style}>
+    <Sidebar />
+    {#if !$url.hash || $url.hash == "#general"}
+      <General />
+    {:else if $url.hash.startsWith("#services")}
+      <Services />
+    {/if}
+  </div>
+{:else}
+  <p>Loading...</p>
+{/if}
+
+<style>
+  :global(body) {
+    width: 100vw;
+    height: 100vh;
+    margin: 0;
+    padding: 0;
+  }
+
+  div {
+    height: 100%;
+    display: grid;
+    grid-template-columns: min-content 800px;
+    margin: 0;
+    padding-top: 50px;
+    justify-content: center;
+    font-family: "Inter", sans-serif;
+    box-sizing: border-box;
+    font-size: 16px;
+    background-color: var(--bg-main);
+    color: var(--text);
+    overflow: scroll;
+  }
+
+  @media (max-width: 1250px) {
+    div {
+      grid-template-columns: auto;
+      grid-template-rows: min-content auto;
+      padding-left: 5vw;
+      padding-right: 5vw;
+    }
+  }
+
+  @media (max-width: 715px) {
+    div {
+      font-size: 14px;
+      grid-template-columns: auto;
+      grid-template-rows: min-content auto;
+      padding-left: 5vw;
+      padding-right: 5vw;
+    }
+  }
+</style>
diff --git a/src/pages/options_src/General/Exceptions.svelte b/src/pages/options_src/General/Exceptions.svelte
new file mode 100644
index 00000000..7315877d
--- /dev/null
+++ b/src/pages/options_src/General/Exceptions.svelte
@@ -0,0 +1,110 @@
+<script>
+  const browser = window.browser || window.chrome
+
+  import Row from "../../components/Row.svelte"
+  import Select from "../../components/Select.svelte"
+  import AddIcon from "../../icons/AddIcon.svelte"
+  import CloseIcon from "../../icons/CloseIcon.svelte"
+  import Input from "../../components/Input.svelte"
+  import Label from "../../components/Label.svelte"
+  import { options, config } from "../stores"
+  import { onDestroy } from "svelte"
+
+  let _options
+  let _config
+
+  const unsubscribeOptions = options.subscribe(val => (_options = val))
+  const unsubscribeConfig = config.subscribe(val => (_config = val))
+  onDestroy(() => {
+    unsubscribeOptions()
+    unsubscribeConfig()
+  })
+  let inputType = "url"
+  let inputValue = ""
+
+  $: inputPlaceholder = inputType == "url" ? "https://www.google.com" : "https?://(www.|)youtube.com/"
+
+  function removeException(exception) {
+    let index
+    index = _options.exceptions.url.indexOf(exception)
+    if (index > -1) {
+      _options.exceptions.url.splice(index, 1)
+    } else {
+      index = _options.exceptions.regex.indexOf(exception)
+      if (index > -1) _options.exceptions.regex.splice(index, 1)
+    }
+    options.set(_options)
+  }
+
+  function addException() {
+    let valid = false
+    if (inputType == "url" && /^(ftp|http|https):\/\/[^ "]+$/.test(inputValue)) {
+      valid = true
+      if (!_options.exceptions.url.includes(inputValue)) {
+        _options.exceptions.url.push(inputValue)
+      }
+    } else if (inputType == "regex") {
+      valid = true
+      if (!_options.exceptions.regex.includes(inputValue)) {
+        _options.exceptions.regex.push(inputValue)
+      }
+    }
+    if (valid) {
+      options.set(_options)
+      inputValue = ""
+    }
+  }
+</script>
+
+<Row>
+  <Label>{browser.i18n.getMessage("excludeFromRedirecting") || "Excluded from redirecting"}</Label>
+</Row>
+<div dir="ltr">
+  <Row>
+    <div>
+      <Input
+        placeholder={inputPlaceholder}
+        aria-label="Add url exception input"
+        bind:value={inputValue}
+        on:keydown={e => {
+          if (e.key === "Enter") addException()
+        }}
+      />
+      <Select
+        bind:value={inputType}
+        values={[
+          { value: "url", name: "URL" },
+          { value: "regex", name: "Regex" },
+        ]}
+      />
+    </div>
+    <button class="add" on:click={addException} aria-label="Add the url exception">
+      <AddIcon />
+    </button>
+  </Row>
+  <hr />
+  <div class="checklist">
+    {#each [..._options.exceptions.url, ..._options.exceptions.regex] as exception}
+      <Row>
+        {exception}
+        <button class="add" on:click={() => removeException(exception)}>
+          <CloseIcon />
+        </button>
+      </Row>
+      <hr />
+    {/each}
+  </div>
+</div>
+
+<style>
+  .add {
+    background-color: transparent;
+    border: none;
+    color: var(--text);
+    padding: 0;
+    margin: 0;
+    text-decoration: none;
+    display: inline-block;
+    cursor: pointer;
+  }
+</style>
diff --git a/src/pages/options_src/General/General.svelte b/src/pages/options_src/General/General.svelte
new file mode 100644
index 00000000..b6ed1b46
--- /dev/null
+++ b/src/pages/options_src/General/General.svelte
@@ -0,0 +1,98 @@
+<script>
+  const browser = window.browser || window.chrome
+
+  import Exceptions from "./Exceptions.svelte"
+  import SettingsButtons from "./SettingsButtons.svelte"
+  import { options } from "../stores"
+  import { onDestroy } from "svelte"
+  import Row from "../../components/Row.svelte"
+  import Label from "../../components/Label.svelte"
+  import Select from "../../components/Select.svelte"
+  import Checkbox from "../../components/Checkbox.svelte"
+
+  let _options
+  const unsubscribe = options.subscribe(val => (_options = val))
+  onDestroy(unsubscribe)
+
+  let disableBookmarks = null
+  browser.runtime.getPlatformInfo(r => {
+    switch (r.os) {
+      case "fuchsia":
+      case "ios":
+      case "android":
+        disableBookmarks = true
+        break
+      default:
+        disableBookmarks = false
+    }
+    if (!disableBookmarks) {
+      browser.permissions.contains({ permissions: ["bookmarks"] }, r => (bookmarksPermission = r))
+    }
+  })
+
+  let bookmarksPermission
+  $: if (disableBookmarks !== null && disableBookmarks === false) {
+    if (bookmarksPermission) {
+      browser.permissions.request({ permissions: ["bookmarks"] }, r => (bookmarksPermission = r))
+    } else {
+      browser.permissions.remove({ permissions: ["bookmarks"] })
+      bookmarksPermission = false
+    }
+  }
+</script>
+
+<div>
+  <Row>
+    <Label>{browser.i18n.getMessage("theme") || "Theme"}</Label>
+    <Select
+      values={[
+        { value: "detect", name: browser.i18n.getMessage("auto") || "Auto" },
+        { value: "light", name: browser.i18n.getMessage("light") || "Light" },
+        { value: "dark", name: browser.i18n.getMessage("dark") || "Dark" },
+      ]}
+      value={_options.theme}
+      onChange={e => {
+        _options.theme = e.target.options[e.target.options.selectedIndex].value
+        options.set(_options)
+      }}
+    />
+  </Row>
+
+  <Row>
+    <Label>{browser.i18n.getMessage("fetchPublicInstances") || "Fetch public instances"}</Label>
+    <Select
+      value={_options.fetchInstances}
+      values={[
+        { value: "github", name: "GitHub" },
+        { value: "codeberg", name: "Codeberg" },
+        { value: "disable", name: browser.i18n.getMessage("disable") || "Disable" },
+      ]}
+      onChange={e => {
+        _options.fetchInstances = e.target.options[e.target.options.selectedIndex].value
+        options.set(_options)
+      }}
+    />
+  </Row>
+
+  <Row>
+    <Label>{browser.i18n.getMessage("redirectOnlyInIncognito") || "Redirect Only in Incognito"}</Label>
+    <Checkbox
+      checked={_options.redirectOnlyInIncognito}
+      onChange={e => {
+        _options.redirectOnlyInIncognito = e.target.checked
+        options.set(_options)
+      }}
+    />
+  </Row>
+
+  {#if disableBookmarks === false}
+    <Row>
+      <Label>{browser.i18n.getMessage("bookmarksMenu") || "Bookmarks menu"}</Label>
+      <Checkbox bind:checked={bookmarksPermission} />
+    </Row>
+  {/if}
+
+  <Exceptions />
+
+  <SettingsButtons />
+</div>
diff --git a/src/pages/options_src/General/SettingsButtons.svelte b/src/pages/options_src/General/SettingsButtons.svelte
new file mode 100644
index 00000000..4be747fe
--- /dev/null
+++ b/src/pages/options_src/General/SettingsButtons.svelte
@@ -0,0 +1,112 @@
+<script>
+  const browser = window.browser || window.chrome
+
+  import { onDestroy } from "svelte"
+  import Button from "../../components/Button.svelte"
+  import ExportIcon from "../../icons/ExportIcon.svelte"
+  import ImportIcon from "../../icons/ImportIcon.svelte"
+  import ResetIcon from "../../icons/ResetIcon.svelte"
+  import { options } from "../stores"
+  import servicesHelper from "../../../assets/javascripts/services.js"
+  import utils from "../../../assets/javascripts/utils.js"
+
+  let _options
+  const unsubscribe = options.subscribe(val => (_options = val))
+  onDestroy(unsubscribe)
+
+  let importSettingsInput
+  let importSettingsFiles
+  $: if (importSettingsFiles) {
+    const reader = new FileReader()
+    reader.readAsText(importSettingsFiles[0])
+    reader.onload = async () => {
+      let data = JSON.parse(reader.result)
+      if (data.version != browser.runtime.getManifest().version) {
+        alert("Importing from a previous version. Be careful")
+      }
+      data = await servicesHelper.processUpdate(data)
+      options.set(data)
+    }
+    reader.onerror = error => {
+      console.log("error", error)
+      alert("Error!")
+    }
+  }
+
+  async function exportSettings() {
+    _options.version = browser.runtime.getManifest().version
+    const resultString = JSON.stringify(_options, null, "  ")
+    const anchor = document.createElement("a")
+    anchor.href = "data:application/json;base64," + btoa(resultString)
+    anchor.download = `libredirect-settings-v${_options.version}.json`
+    anchor.click()
+  }
+
+  async function exportSettingsSync() {
+    _options.version = browser.runtime.getManifest().version
+    browser.storage.sync.set({ options: _options })
+  }
+
+  async function importSettingsSync() {
+    browser.storage.sync.get({ options }, async r => {
+      let data = r.options
+      if (data.version != browser.runtime.getManifest().version) {
+        alert("Importing from a previous version. Be careful")
+      }
+      data = await servicesHelper.processUpdate(data)
+      options.set(data)
+    })
+  }
+
+  async function resetSettings() {
+    browser.storage.local.clear(async () => {
+      const data = await servicesHelper.initDefaults()
+      options.set(data)
+    })
+  }
+</script>
+
+<div class="buttons">
+  <Button on:click={() => importSettingsInput.click()}>
+    <ImportIcon class="margin margin_{document.body.dir}" />
+    {browser.i18n.getMessage("importSettings") || "Import Settings"}
+  </Button>
+  <input
+    type="file"
+    accept=".json"
+    style="display: none"
+    bind:this={importSettingsInput}
+    bind:files={importSettingsFiles}
+  />
+
+  <Button on:click={exportSettings}>
+    <ExportIcon class="margin margin_{document.body.dir}" />
+    {browser.i18n.getMessage("exportSettings") || "Export Settings"}
+  </Button>
+
+  <Button on:click={exportSettingsSync}>
+    <ExportIcon class="margin margin_{document.body.dir}" />
+    {browser.i18n.getMessage("exportSettingsToSync") || "Export Settings to Sync"}
+  </Button>
+
+  <Button on:click={importSettingsSync}>
+    <ImportIcon class="margin margin_{document.body.dir}" />
+    {browser.i18n.getMessage("importSettingsFromSync") || "Import Settings from Sync"}
+  </Button>
+
+  <Button on:click={resetSettings}>
+    <ResetIcon class="margin margin_{document.body.dir}" />
+    {browser.i18n.getMessage("resetSettings") || "Reset Settings"}
+  </Button>
+</div>
+
+<style>
+  :global(.margin) {
+    margin-right: 10px;
+    margin-left: 0;
+  }
+  :global(.margin_rtl) {
+    margin-right: 0;
+    margin-left: 10px;
+  }
+</style>
diff --git a/src/pages/options_src/Services/FrontendIcon.svelte b/src/pages/options_src/Services/FrontendIcon.svelte
new file mode 100644
index 00000000..4b392676
--- /dev/null
+++ b/src/pages/options_src/Services/FrontendIcon.svelte
@@ -0,0 +1,41 @@
+<script>
+  import { onDestroy } from "svelte"
+  export let details
+  export let selectedService
+  import { config, options } from "../stores"
+  let _options
+  let _config
+
+  const unsubscribeOptions = options.subscribe(val => (_options = val))
+  const unsubscribeConfig = config.subscribe(val => (_config = val))
+  onDestroy(() => {
+    unsubscribeOptions()
+    unsubscribeConfig()
+  })
+
+  let theme
+  $: if (_options) {
+    if (_options.theme == "dark") {
+      theme = "dark"
+    } else if (_options.theme == "light") {
+      theme = "light"
+    } else if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
+      theme = "dark"
+    } else {
+      theme = "light"
+    }
+  }
+  $: imageType = _config.services[selectedService].frontends[details.value].imageType
+</script>
+
+{#if imageType}
+  {#if imageType == "svgMono"}
+    {#if theme == "dark"}
+      <img src={`/assets/images/${details.value}-icon-light.svg`} alt={details.label} />
+    {:else}
+      <img src={`/assets/images/${details.value}-icon.svg`} alt={details.label} />
+    {/if}
+  {:else}
+    <img src={`/assets/images/${details.value}-icon.${imageType}`} alt={details.label} />
+  {/if}
+{/if}
diff --git a/src/pages/options_src/Services/Instances.svelte b/src/pages/options_src/Services/Instances.svelte
new file mode 100644
index 00000000..4e5d1e7d
--- /dev/null
+++ b/src/pages/options_src/Services/Instances.svelte
@@ -0,0 +1,261 @@
+<script>
+  const browser = window.browser || window.chrome
+
+  import Button from "../../components/Button.svelte"
+  import AddIcon from "../../icons/AddIcon.svelte"
+  import { options, config } from "../stores"
+  import PingIcon from "../../icons/PingIcon.svelte"
+  import AutoPickIcon from "../../icons/AutoPickIcon.svelte"
+  import Row from "../../components/Row.svelte"
+  import Input from "../../components/Input.svelte"
+  import Label from "../../components/Label.svelte"
+  import CloseIcon from "../../icons/CloseIcon.svelte"
+  import { onDestroy, onMount } from "svelte"
+  import utils from "../../../assets/javascripts/utils"
+
+  export let selectedService
+  export let selectedFrontend
+
+  let _options
+  let _config
+
+  const unsubscribeOptions = options.subscribe(val => (_options = val))
+  const unsubscribeConfig = config.subscribe(val => (_config = val))
+  onDestroy(() => {
+    unsubscribeOptions()
+    unsubscribeConfig()
+  })
+
+  let blacklist
+  let redirects
+
+  $: serviceConf = _config.services[selectedService]
+
+  let allInstances = []
+
+  $: {
+    allInstances = []
+    if (_options[selectedFrontend]) allInstances.push(..._options[selectedFrontend])
+    if (redirects && redirects[selectedFrontend]) {
+      allInstances.push(...redirects[selectedFrontend]["clearnet"])
+    }
+    allInstances = [...new Set(allInstances)]
+  }
+
+  let pingCache
+  $: {
+    if (pingCache) browser.storage.local.set({ pingCache })
+  }
+
+  function isCustomInstance(instance) {
+    if (redirects[selectedFrontend]) {
+      for (const network in redirects[selectedFrontend]) {
+        if (redirects[selectedFrontend][network].includes(instance)) return false
+      }
+    }
+    return true
+  }
+
+  async function pingInstances() {
+    pingCache = {}
+    for (const instance of allInstances) {
+      pingCache[instance] = { color: "lightblue", value: "pinging..." }
+      const time = await utils.ping(instance)
+      pingCache[instance] = colorTime(time)
+    }
+  }
+
+  async function autoPickInstance() {
+    const instances = utils.randomInstances(redirects[selectedFrontend]["clearnet"], 5)
+    const myInstancesCache = []
+    for (const instance of instances) {
+      pingCache[instance] = { color: "lightblue", value: "pinging..." }
+      const time = await utils.ping(instance)
+      pingCache[instance] = colorTime(time)
+      myInstancesCache.push([instance, time])
+    }
+    myInstancesCache.sort((a, b) => a[1] - b[1])
+
+    _options[selectedFrontend].push(myInstancesCache[0][0])
+    options.set(_options)
+  }
+
+  function colorTime(time) {
+    let value
+    let color
+    if (time < 5000) {
+      value = `${time}ms`
+      if (time <= 1000) color = "green"
+      else if (time <= 2000) color = "orange"
+    } else if (time >= 5000) {
+      color = "red"
+      if (time == 5000) value = "5000ms+"
+      if (time > 5000) value = `Error: ${time - 5000}`
+    } else {
+      color = "red"
+      value = "Server not found"
+    }
+    return { color, value }
+  }
+
+  onMount(async () => {
+    blacklist = await utils.getBlacklist(_options)
+    redirects = await utils.getList(_options)
+    pingCache = await utils.getPingCache()
+  })
+
+  let addInstanceValue
+  function addInstance() {
+    const instance = utils.protocolHost(new URL(addInstanceValue))
+    if (!_options[selectedFrontend].includes(instance)) {
+      _options[selectedFrontend].push(instance)
+      addInstanceValue = ""
+      options.set(_options)
+    }
+  }
+</script>
+
+{#if serviceConf.frontends[selectedFrontend].instanceList && redirects && blacklist}
+  <hr />
+
+  <div>
+    <Button on:click={pingInstances}>
+      <PingIcon class="margin margin_{document.body.dir}" />
+      {browser.i18n.getMessage("pingInstances") || "Ping Instances"}
+    </Button>
+    <Button on:click={autoPickInstance}>
+      <AutoPickIcon class="margin margin_{document.body.dir}" />
+      {browser.i18n.getMessage("autoPickInstance") || "Auto Pick Instance"}
+    </Button>
+  </div>
+
+  <Row>
+    <Label>{browser.i18n.getMessage("addYourFavoriteInstances") || "Add your favorite instances"}</Label>
+  </Row>
+  <div dir="ltr">
+    <Row>
+      <Input
+        bind:value={addInstanceValue}
+        type="url"
+        placeholder="https://instance.com"
+        aria-label="Add instance input"
+        on:keydown={e => e.key === "Enter" && addInstance()}
+      />
+      <button on:click={addInstance} class="add" aria-label="Add the instance">
+        <AddIcon />
+      </button>
+    </Row>
+
+    {#each _options[selectedFrontend] as instance}
+      <Row>
+        <span>
+          <a href={instance} target="_blank" rel="noopener noreferrer">{instance}</a>
+          {#if isCustomInstance(instance)}
+            <span style="color:grey">custom</span>
+          {/if}
+          {#if pingCache && pingCache[instance]}
+            <span style="color:{pingCache[instance].color}">{pingCache[instance].value}</span>
+          {/if}
+        </span>
+        <button
+          class="add"
+          aria-label="Remove Instance"
+          on:click={() => {
+            const index = _options[selectedFrontend].indexOf(instance)
+            if (index > -1) {
+              _options[selectedFrontend].splice(index, 1)
+              options.set(_options)
+            }
+          }}
+        >
+          <CloseIcon />
+        </button>
+      </Row>
+      <hr />
+    {/each}
+
+    {#if redirects !== "disabled" && blacklist !== "disabled"}
+      {#if redirects[selectedFrontend] && redirects[selectedFrontend]["clearnet"]}
+        {#each Object.entries(_config.networks) as [networkName, network]}
+          {#if redirects[selectedFrontend] && redirects[selectedFrontend][networkName] && redirects[selectedFrontend][networkName].length > 0}
+            <Row></Row>
+            <Row><Label>{network.name}</Label></Row>
+            <hr />
+            {#each redirects[selectedFrontend][networkName] as instance}
+              <Row>
+                <span>
+                  <a href={instance} target="_blank" rel="noopener noreferrer">{instance}</a>
+                  {#if blacklist.cloudflare.includes(instance)}
+                    <a
+                      href="https://libredirect.github.io/docs.html#instances"
+                      target="_blank"
+                      rel="noopener noreferrer"
+                      style="color:red;"
+                    >
+                      cloudflare
+                    </a>
+                  {/if}
+                  {#if _options[selectedFrontend].includes(instance)}
+                    <span style="color:grey">chosen</span>
+                  {/if}
+                  {#if pingCache && pingCache[instance]}
+                    <span style="color:{pingCache[instance].color}">{pingCache[instance].value}</span>
+                  {/if}
+                </span>
+                <button
+                  class="add"
+                  aria-label="Add instance"
+                  on:click={() => {
+                    if (_options[selectedFrontend]) {
+                      if (!_options[selectedFrontend].includes(instance)) {
+                        _options[selectedFrontend].push(instance)
+                        options.set(_options)
+                      }
+                    }
+                  }}
+                >
+                  <AddIcon />
+                </button>
+              </Row>
+              <hr />
+            {/each}
+          {/if}
+        {/each}
+      {:else}
+        <Row><Label>No instances found.</Label></Row>
+      {/if}
+    {/if}
+  </div>
+{/if}
+
+<style>
+  .add {
+    background-color: transparent;
+    border: none;
+    color: var(--text);
+    padding: 0;
+    margin: 0;
+    text-decoration: none;
+    display: inline-block;
+    cursor: pointer;
+  }
+
+  a {
+    color: var(--text);
+    text-decoration: none;
+    word-wrap: anywhere;
+  }
+
+  a:hover {
+    text-decoration: underline;
+  }
+
+  :global(.margin) {
+    margin-right: 10px;
+    margin-left: 0;
+  }
+  :global(.margin_rtl) {
+    margin-right: 0;
+    margin-left: 10px;
+  }
+</style>
diff --git a/src/pages/options_src/Services/RedirectType.svelte b/src/pages/options_src/Services/RedirectType.svelte
new file mode 100644
index 00000000..69ea2b73
--- /dev/null
+++ b/src/pages/options_src/Services/RedirectType.svelte
@@ -0,0 +1,102 @@
+<script>
+  const browser = window.browser || window.chrome
+
+  import { onDestroy } from "svelte"
+  import SvelteSelect from "svelte-select"
+  import { options, config } from "../stores"
+  import Row from "../../components/Row.svelte"
+  import Label from "../../components/Label.svelte"
+  import FrontendIcon from "./FrontendIcon.svelte"
+  import Select from "../../components/Select.svelte"
+
+  let _options
+  let _config
+
+  const unsubscribeOptions = options.subscribe(val => (_options = val))
+  const unsubscribeConfig = config.subscribe(val => (_config = val))
+  onDestroy(() => {
+    unsubscribeOptions()
+    unsubscribeConfig()
+  })
+
+  export let selectedService
+
+  $: serviceConf = _config.services[selectedService]
+  $: serviceOptions = _options[selectedService]
+  $: frontendName = _options[selectedService].frontend
+
+  let values
+  $: if (serviceConf.frontends[frontendName].embeddable) {
+    values = [
+      { value: "both", name: browser.i18n.getMessage("both") || "Both" },
+      { value: "sub_frame", name: browser.i18n.getMessage("onlyEmbedded") || "Only Embedded" },
+      { value: "main_frame", name: browser.i18n.getMessage("onlyNotEmbedded") || "Only Not Embedded" },
+    ]
+  } else if (
+    serviceConf.frontends[frontendName].desktopApp &&
+    Object.values(serviceConf.frontends).some(frontend => frontend.embeddable)
+  ) {
+    values = [
+      { value: "both", name: browser.i18n.getMessage("both") || "Both" },
+      { value: "main_frame", name: browser.i18n.getMessage("onlyNotEmbedded") || "Only Not Embedded" },
+    ]
+    if (serviceOptions.redirectType == "sub_frame") {
+      serviceOptions.redirectType = "main_frame"
+      options.set(_options)
+    }
+  } else {
+    values = [{ value: "main_frame", name: browser.i18n.getMessage("onlyNotEmbedded") || "Only Not Embedded" }]
+    serviceOptions.redirectType = "main_frame"
+    options.set(_options)
+  }
+
+  let embeddableFrontends = []
+  $: if (serviceConf) {
+    embeddableFrontends = []
+    for (const [frontendId, frontendConf] of Object.entries(serviceConf.frontends)) {
+      if (frontendConf.embeddable && frontendConf.instanceList) {
+        embeddableFrontends.push({
+          value: frontendId,
+          label: frontendConf.name,
+        })
+      }
+    }
+  }
+</script>
+
+<Row>
+  <Label>{browser.i18n.getMessage("redirectType") || "Redirect Type"}</Label>
+  <Select
+    value={serviceOptions.redirectType}
+    onChange={e => {
+      serviceOptions.redirectType = e.target.options[e.target.options.selectedIndex].value
+      options.set(_options)
+    }}
+    {values}
+  />
+</Row>
+
+{#if serviceConf.frontends[frontendName].desktopApp && serviceOptions.redirectType != "main_frame"}
+  <Row>
+    <Label>{browser.i18n.getMessage("embedFrontend") || "Embed Frontend"}</Label>
+    <SvelteSelect
+      clearable={false}
+      class="svelte_select"
+      value={serviceOptions.embedFrontend}
+      on:change={e => {
+        serviceOptions.embedFrontend = e.detail.value
+        options.set(_options)
+      }}
+      items={embeddableFrontends}
+    >
+      <div class="slot" slot="item" let:item>
+        <FrontendIcon details={item} {selectedService} />
+        {item.label}
+      </div>
+      <div class="slot" slot="selection" let:selection>
+        <FrontendIcon details={selection} {selectedService} />
+        {selection.label}
+      </div>
+    </SvelteSelect>
+  </Row>
+{/if}
diff --git a/src/pages/options_src/Services/ServiceIcon.svelte b/src/pages/options_src/Services/ServiceIcon.svelte
new file mode 100644
index 00000000..89393cf6
--- /dev/null
+++ b/src/pages/options_src/Services/ServiceIcon.svelte
@@ -0,0 +1,40 @@
+<script>
+  import { onDestroy } from "svelte"
+  export let details
+  import { config, options } from "../stores"
+  let _options
+  let _config
+
+  const unsubscribeOptions = options.subscribe(val => (_options = val))
+  const unsubscribeConfig = config.subscribe(val => (_config = val))
+  onDestroy(() => {
+    unsubscribeOptions()
+    unsubscribeConfig()
+  })
+
+  let theme
+  $: if (_options) {
+    if (_options.theme == "dark") {
+      theme = "dark"
+    } else if (_options.theme == "light") {
+      theme = "light"
+    } else if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
+      theme = "dark"
+    } else {
+      theme = "light"
+    }
+  }
+  $: imageType = _config.services[details.value].imageType
+</script>
+
+{#if imageType}
+  {#if imageType == "svgMono"}
+    {#if theme == "dark"}
+      <img src={`/assets/images/${details.value}-icon-light.svg`} alt={details.label} />
+    {:else}
+      <img src={`/assets/images/${details.value}-icon.svg`} alt={details.label} />
+    {/if}
+  {:else}
+    <img src={`/assets/images/${details.value}-icon.${imageType}`} alt={details.label} />
+  {/if}
+{/if}
diff --git a/src/pages/options_src/Services/Services.svelte b/src/pages/options_src/Services/Services.svelte
new file mode 100644
index 00000000..db2977f9
--- /dev/null
+++ b/src/pages/options_src/Services/Services.svelte
@@ -0,0 +1,260 @@
+<script>
+  const browser = window.browser || window.chrome
+
+  import url from "../url"
+  import Row from "../../components/Row.svelte"
+  import Label from "../../components/Label.svelte"
+  import Select from "../../components/Select.svelte"
+  import { options, config } from "../stores"
+  import RedirectType from "./RedirectType.svelte"
+  import { onDestroy } from "svelte"
+  import Instances from "./Instances.svelte"
+  import SvelteSelect from "svelte-select"
+  import ServiceIcon from "./ServiceIcon.svelte"
+  import FrontendIcon from "./FrontendIcon.svelte"
+  import Checkbox from "../../components/Checkbox.svelte"
+
+  let _options
+  let _config
+
+  const unsubscribeOptions = options.subscribe(val => (_options = val))
+  const unsubscribeConfig = config.subscribe(val => (_config = val))
+  onDestroy(() => {
+    unsubscribeOptions()
+    unsubscribeConfig()
+  })
+  let selectedService = $url.hash.startsWith("#services:") ? $url.hash.split(":")[1] : "youtube"
+  let hideServiceSelection = false
+  let hideFrontendSelection = false
+  $: serviceConf = _config.services[selectedService]
+  $: serviceOptions = _options[selectedService]
+  $: frontendWebsite = serviceConf.frontends[serviceOptions.frontend].url
+  $: servicesEntries = Object.entries(_config.services)
+  $: frontendEntries = Object.entries(serviceConf.frontends)
+</script>
+
+<div>
+  <Row>
+    <Label>
+      <a href={serviceConf.url} style="text-decoration: underline;" target="_blank" rel="noopener noreferrer">
+        {browser.i18n.getMessage("service") || "Service"}
+      </a>
+    </Label>
+    <div dir="ltr" on:click={() => (hideServiceSelection = true)} on:keydown={null}>
+      <SvelteSelect
+        clearable={false}
+        class="svelte_select"
+        value={selectedService}
+        showChevron
+        on:change={e => {
+          selectedService = e.detail.value
+          window.location.hash = `services:${e.detail.value}`
+          hideServiceSelection = false
+        }}
+        on:pointerup={() => (hideServiceSelection = false)}
+        on:focus={() => (hideServiceSelection = true)}
+        on:blur={() => (hideServiceSelection = false)}
+        items={[
+          ...servicesEntries.map(([serviceKey, service]) => {
+            return { value: serviceKey, label: service.name }
+          }),
+        ]}
+      >
+        <div class={"slot " + (!_options[item.value].enabled && "disabled")} slot="item" let:item>
+          <ServiceIcon details={item} />
+          {item.label}
+        </div>
+        <div
+          class={"slot " + (!_options[selection.value].enabled && !hideServiceSelection && "disabled")}
+          slot="selection"
+          let:selection
+        >
+          {#if !hideServiceSelection}
+            <ServiceIcon details={selection} />
+            {selection.label}
+          {:else}
+            {browser.i18n.getMessage("searchService") || "Search Service"}
+          {/if}
+        </div>
+        <div style="font-size: 10px;" slot="chevron-icon">🮦</div>
+      </SvelteSelect>
+    </div>
+  </Row>
+
+  <hr />
+
+  <Row>
+    <Label>{browser.i18n.getMessage("enable") || "Enable"}</Label>
+    <Checkbox
+      checked={serviceOptions.enabled}
+      onChange={e => {
+        serviceOptions.enabled = e.target.checked
+        options.set(_options)
+      }}
+    />
+  </Row>
+
+  <div style={!serviceOptions.enabled && "pointer-events: none;opacity: 0.4;user-select: none;"}>
+    <Row>
+      <Label>{browser.i18n.getMessage("showInPopup") || "Show in popup"}</Label>
+      <Checkbox
+        checked={_options.popupServices.includes(selectedService)}
+        onChange={e => {
+          if (e.target.checked && !_options.popupServices.includes(selectedService)) {
+            _options.popupServices.push(selectedService)
+          } else if (_options.popupServices.includes(selectedService)) {
+            const index = _options.popupServices.indexOf(selectedService)
+            if (index !== -1) _options.popupServices.splice(index, 1)
+          }
+          options.set(_options)
+        }}
+      />
+    </Row>
+
+    <Row>
+      <Label>
+        <a href={frontendWebsite} style="text-decoration: underline;" target="_blank" rel="noopener noreferrer">
+          {browser.i18n.getMessage("frontend") || "Frontend"}
+        </a>
+      </Label>
+      <div dir="ltr" on:click={() => (hideFrontendSelection = true)} on:keydown={null}>
+        <SvelteSelect
+          clearable={false}
+          dir="ltr"
+          class="svelte_select"
+          value={serviceOptions.frontend}
+          showChevron
+          on:change={e => {
+            serviceOptions.frontend = e.detail.value
+            options.set(_options)
+            hideFrontendSelection = false
+          }}
+          on:pointerup={() => (hideServiceSelection = false)}
+          on:focus={() => (hideFrontendSelection = true)}
+          on:blur={() => (hideFrontendSelection = false)}
+          items={[
+            ...frontendEntries.map(([frontendId, frontend]) => ({
+              value: frontendId,
+              label: frontend.name,
+            })),
+          ]}
+        >
+          <div class="slot" slot="item" let:item>
+            <FrontendIcon details={item} {selectedService} />
+            {item.label}
+          </div>
+          <div class="slot" slot="selection" let:selection>
+            {#if !hideFrontendSelection}
+              <FrontendIcon details={selection} {selectedService} />
+              {selection.label}
+            {:else}
+              {browser.i18n.getMessage("search_frontend") || "Search Frontend"}
+            {/if}
+          </div>
+          <div style="font-size: 10px;" slot="chevron-icon">🮦</div>
+        </SvelteSelect>
+      </div>
+    </Row>
+
+    <RedirectType {selectedService} />
+
+    <Row>
+      <Label>{browser.i18n.getMessage("unsupportedIframesHandling") || "Unsupported embeds handling"}</Label>
+      <Select
+        value={serviceOptions.unsupportedUrls}
+        onChange={e => {
+          serviceOptions.unsupportedUrls = e.target.options[e.target.options.selectedIndex].value
+          options.set(_options)
+        }}
+        values={[
+          { value: "bypass", name: browser.i18n.getMessage("bypass") || "Bypass" },
+          { value: "block", name: browser.i18n.getMessage("block") || "Block" },
+        ]}
+      />
+    </Row>
+
+    <div style={_options.redirectOnlyInIncognito && "pointer-events: none;opacity: 0.4;user-select: none;"}>
+      <Row>
+        <Label>{browser.i18n.getMessage("redirectOnlyInIncognito") || "Redirect Only in Incognito"}</Label>
+        <Checkbox
+          checked={serviceOptions.redirectOnlyInIncognito}
+          onChange={e => {
+            serviceOptions.redirectOnlyInIncognito = e.target.checked
+            options.set(_options)
+          }}
+        />
+      </Row>
+    </div>
+
+    {#if selectedService == "search"}
+      <Row>
+        <Label>{browser.i18n.getMessage("redirectGoogle") || "Redirect Google"}</Label>
+        <Checkbox
+          checked={serviceOptions.redirectGoogle}
+          onChange={e => {
+            serviceOptions.redirectGoogle = e.target.checked
+            options.set(_options)
+          }}
+        />
+      </Row>
+      <Row>
+        <Label>
+          {@html browser.i18n.getMessage("searchHint") ||
+            `Set LibRedirect as Default Search Engine. For how to do in chromium browsers, click
+          <a
+            href="https://libredirect.github.io/docs.html#search_engine_chromium"
+            target="_blank"
+            rel="noopener noreferrer"
+            >here
+          </a>.`}
+        </Label>
+      </Row>
+    {/if}
+
+    <Instances
+      {selectedService}
+      selectedFrontend={!serviceConf.frontends[serviceOptions.frontend].desktopApp ||
+      serviceOptions.redirectType == "main_frame"
+        ? serviceOptions.frontend
+        : serviceOptions.embedFrontend}
+    />
+
+    <Row></Row>
+  </div>
+</div>
+
+<style>
+  :global(.svelte_select) {
+    font-weight: bold;
+    --item-padding: 0 10px;
+    --border: none;
+    --border-hover: none;
+    --border-focused: none;
+    --width: 210px;
+    --background: var(--bg-secondary);
+    --list-background: var(--bg-secondary);
+    --item-is-active-bg: grey;
+    --item-hover-bg: grey;
+    --item-is-active-color: var(--text);
+    --list-max-height: 400px;
+    --padding: 0 0 0 10px;
+    --item-color: var(--text);
+  }
+  :global(.svelte_select .slot) {
+    display: flex;
+    justify-content: start;
+    align-items: center;
+  }
+
+  :global(.svelte_select img, .svelte_select svg) {
+    margin-right: 10px;
+    margin-left: 0;
+    height: 26px;
+    width: 26px;
+    color: var(--text);
+  }
+
+  :global(.svelte_select .disabled) {
+    opacity: 0.4;
+  }
+</style>
diff --git a/src/pages/options_src/Sidebar.svelte b/src/pages/options_src/Sidebar.svelte
new file mode 100644
index 00000000..6b67581a
--- /dev/null
+++ b/src/pages/options_src/Sidebar.svelte
@@ -0,0 +1,69 @@
+<script>
+  const browser = window.browser || window.chrome
+
+  import url from "./url"
+  import GeneralIcon from "../icons/GeneralIcon.svelte"
+  import ServicesIcon from "../icons/ServicesIcon.svelte"
+  import AboutIcon from "../icons/AboutIcon.svelte"
+</script>
+
+<div>
+  <a href="#general" style={$url.hash == "#general" && "color: var(--active);"}>
+    <GeneralIcon class="margin margin_{document.body.dir}" />
+    {browser.i18n.getMessage("general") || "General"}
+  </a>
+  <a href="#services" style={$url.hash == "#services" && "color: var(--active);"}>
+    <ServicesIcon class="margin margin_{document.body.dir}" />
+    {browser.i18n.getMessage("services") || "Services"}
+  </a>
+  <a href="https://libredirect.github.io" target="_blank" rel="noopener noreferrer">
+    <AboutIcon class="margin margin_{document.body.dir}" />
+    {browser.i18n.getMessage("about") || "About"}
+  </a>
+</div>
+
+<style>
+  div {
+    display: flex;
+    flex-direction: column;
+    margin: 0 20px;
+  }
+
+  a {
+    display: flex;
+    align-items: center;
+    font-size: 18px;
+    text-decoration: none;
+    color: var(--text);
+    transition: 0.1s;
+    margin: 10px;
+    min-width: max-content;
+  }
+
+  a:hover {
+    color: var(--active);
+  }
+
+  @media (max-width: 1250px) {
+    div {
+      flex-direction: row;
+      justify-content: center;
+      margin: 0;
+    }
+  }
+
+  @media (max-width: 715px) {
+    a {
+      margin: 5px;
+    }
+  }
+
+  :global(.margin) {
+    margin-right: 5px;
+    margin-left: 0;
+  }
+  :global(.margin_rtl) {
+    margin-right: 0;
+    margin-left: 5px;
+  }
+</style>
diff --git a/src/pages/options_src/main.js b/src/pages/options_src/main.js
new file mode 100644
index 00000000..c4012f4a
--- /dev/null
+++ b/src/pages/options_src/main.js
@@ -0,0 +1,7 @@
+import App from "./App.svelte"
+
+const app = new App({
+  target: document.body,
+})
+
+export default app
diff --git a/src/pages/options_src/stores.js b/src/pages/options_src/stores.js
new file mode 100644
index 00000000..7ae0f8c7
--- /dev/null
+++ b/src/pages/options_src/stores.js
@@ -0,0 +1,4 @@
+import { writable } from "svelte/store"
+
+export const options = writable(null)
+export const config = writable(null)
diff --git a/src/pages/options_src/url.js b/src/pages/options_src/url.js
new file mode 100644
index 00000000..010e5b21
--- /dev/null
+++ b/src/pages/options_src/url.js
@@ -0,0 +1,38 @@
+// https://svelte.dev/repl/5abaac000b164aa1aacc6051d5c4f584?version=3.59.2
+
+import { derived, writable } from 'svelte/store'
+
+export function createUrlStore(ssrUrl) {
+  // Ideally a bundler constant so that it's tree-shakable
+  if (typeof window === 'undefined') {
+    const { subscribe } = writable(ssrUrl)
+    return { subscribe }
+  }
+
+  const href = writable(window.location.href)
+
+  const originalPushState = history.pushState
+  const originalReplaceState = history.replaceState
+
+  const updateHref = () => href.set(window.location.href)
+
+  history.pushState = () => {
+    originalPushState.apply(this, arguments)
+    updateHref()
+  }
+
+  history.replaceState = () => {
+    originalReplaceState.apply(this, arguments)
+    updateHref()
+  }
+
+  window.addEventListener('popstate', updateHref)
+  window.addEventListener('hashchange', updateHref)
+
+  return {
+    subscribe: derived(href, ($href) => new URL($href)).subscribe
+  }
+}
+
+// If you're using in a pure SPA, you can return a store directly and share it everywhere
+export default createUrlStore()
diff --git a/src/pages/popup/index.html b/src/pages/popup/index.html
new file mode 100644
index 00000000..b197d4a7
--- /dev/null
+++ b/src/pages/popup/index.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+	<meta charset='utf-8'>
+	<meta name='viewport' content='width=device-width,initial-scale=1'>
+	<link rel="icon" type="image/x-icon" href="../../../assets/images/libredirect.svg">
+	<title>Settings</title>
+	<link rel='stylesheet' href='build/bundle.css'>
+	<link rel='stylesheet' href='../fonts/styles.css'>
+	<script defer src='build/bundle.js'></script>
+</head>
+
+<body>
+</body>
+
+</html>
\ No newline at end of file
diff --git a/src/pages/popup/popup.js b/src/pages/popup/popup.js
deleted file mode 100644
index 5eeb18c2..00000000
--- a/src/pages/popup/popup.js
+++ /dev/null
@@ -1,113 +0,0 @@
-"use strict"
-window.browser = window.browser || window.chrome
-
-import servicesHelper from "../../assets/javascripts/services.js"
-import utils from "../../assets/javascripts/utils.js"
-
-document.getElementById("more-options").addEventListener("click", () => browser.runtime.openOptionsPage())
-
-const allSites = document.getElementById("all_sites")
-const currSite = document.getElementById("current_site")
-const currentSiteDivider = document.getElementById("current_site_divider")
-
-const config = await utils.getConfig()
-const divs = {}
-
-for (const service in config.services) {
-	divs[service] = {}
-
-	divs[service].all = allSites.getElementsByClassName(service)[0]
-	divs[service].current = currSite.getElementsByClassName(service)[0]
-
-	divs[service].all_toggle = allSites.getElementsByClassName(`${service}-enabled`)[0]
-	divs[service].all_toggle.addEventListener("change", async () => {
-		const options = await utils.getOptions()
-		options[service].enabled = divs[service].all_toggle.checked
-		browser.storage.local.set({ options })
-	})
-
-	allSites.getElementsByClassName(`${service}-change_instance`)[0].addEventListener("click", () => {
-		browser.tabs.query({ active: true, currentWindow: true }, async tabs => {
-			if (tabs[0].url) {
-				const url = new URL(tabs[0].url)
-				browser.tabs.update({ url: await servicesHelper.switchInstance(url, service) })
-			}
-		})
-	})
-
-	divs[service].current_toggle = currSite.getElementsByClassName(`${service}-enabled`)[0]
-	divs[service].current_toggle.addEventListener("change", async () => {
-		const options = await utils.getOptions()
-		options[service].enabled = divs[service].current_toggle.checked
-		browser.storage.local.set({ options })
-	})
-
-	currSite.getElementsByClassName(`${service}-change_instance`)[0].addEventListener("click", () => {
-		browser.tabs.query({ active: true, currentWindow: true }, async tabs => {
-			if (tabs[0].url) {
-				const url = new URL(tabs[0].url)
-				browser.tabs.update({ url: await servicesHelper.switchInstance(url, service) })
-			}
-		})
-	})
-}
-
-browser.tabs.query({ active: true, currentWindow: true }, async tabs => {
-	let url;
-
-	// Set visibility of control buttons
-	if (tabs[0].url) {
-		url = new URL(tabs[0].url)
-		servicesHelper.switchInstance(url).then(r => {
-			if (r) {
-				document.getElementById("change_instance_div").style.display = ""
-				document.getElementById("change_instance").addEventListener("click", async () =>
-					browser.tabs.update({ url: await servicesHelper.switchInstance(url) })
-				)
-			}
-		})
-		servicesHelper.copyRaw(url, true).then(r => {
-			if (r) {
-				document.getElementById("copy_original_div").style.display = ""
-				document.getElementById("copy_original").addEventListener("click", () =>
-					servicesHelper.copyRaw(url)
-				)
-			}
-		})
-		servicesHelper.reverse(url).then(r => {
-			if (r) {
-				document.getElementById("redirect_to_original_div").style.display = ""
-				document.getElementById("redirect_to_original").addEventListener("click", () =>
-					browser.runtime.sendMessage("reverseTab")
-				)
-			}
-		})
-		servicesHelper.redirectAsync(url, "main_frame", null, true).then(r => {
-			if (r) {
-				document.getElementById("redirect_div").style.display = ""
-				document.getElementById("redirect").addEventListener("click", () =>
-					browser.runtime.sendMessage("redirectTab")
-				)
-			}
-		})
-	}
-
-	const options = await utils.getOptions()
-
-	// Set visibility of all service buttons
-	for (const service of options.popupServices) {
-		divs[service].all.classList.remove("hide")
-		divs[service].all_toggle.checked = options[service].enabled
-	}
-
-	// Set visibility of current page service button
-	if (url) {
-		const service = await servicesHelper.computeService(url)
-		if (service) {
-			divs[service].all.classList.add("hide")
-			divs[service].current.classList.remove("hide")
-			divs[service].current_toggle.checked = options[service].enabled
-			currentSiteDivider.style.display = ""
-		}
-	}
-})
\ No newline at end of file
diff --git a/src/pages/popup/popup.pug b/src/pages/popup/popup.pug
deleted file mode 100644
index f145fe5d..00000000
--- a/src/pages/popup/popup.pug
+++ /dev/null
@@ -1,51 +0,0 @@
-doctype html
-html(lang="en")
-    head
-        meta(charset="UTF-8")
-        meta(name="viewport" content="width=device-width, initial-scale=1.0")
-        link(href="../stylesheets/styles.css" rel="stylesheet")
-        link(href="./style.css" rel="stylesheet")
-    body(dir="auto")
-        div(id="current_site")
-            include /src/pages/popup/switches
-            div(id="current_site_divider" style="display: none")
-                hr
-
-        div(id="all_sites")
-            include /src/pages/popup/switches
-
-        hr
-
-        div(class="block" id="change_instance_div" style="display: none")
-            button(class="title button bottom-button" id="change_instance")
-                label(data-localise="__MSG_switchInstance__") Switch Instance
-                svg(xmlns="http://www.w3.org/2000/svg" height="26px" width="26px" fill="currentColor")
-                    path(d="M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6 0 1.01-.25 1.97-.7 2.8l1.46 1.46C19.54 15.03 20 13.57 20 12c0-4.42-3.58-8-8-8zm0 14c-3.31 0-6-2.69-6-6 0-1.01.25-1.97.7-2.8L5.24 7.74C4.46 8.97 4 10.43 4 12c0 4.42 3.58 8 8 8v3l4-4-4-4v3z")
-
-        div(class="block" id="copy_original_div" title="Copy the original redirected link" style="display: none")
-            button(class="title button bottom-button" id="copy_original")
-                label() Copy Original
-                svg(xmlns="http://www.w3.org/2000/svg" height="24px" width="24px" fill="currentColor")
-                    path(d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z")
-
-        div(class="block" id="redirect_div" style="display: none")
-            button(class="title button bottom-button" id="redirect")
-                label Redirect
-                svg(xmlns="http://www.w3.org/2000/svg" height="24" width="24" fill="currentColor")
-                    path(d="M7 20v-9q0-.825.588-1.413Q8.175 9 9 9h8.2l-1.6-1.6L17 6l4 4-4 4-1.4-1.4 1.6-1.6H9v9Z")
-
-        div(class="block" id="redirect_to_original_div" style="display: none")
-            button(class="title button bottom-button" id="redirect_to_original")
-                label Redirect To Original
-                svg(xmlns="http://www.w3.org/2000/svg" height="24px" width="24px" fill="currentColor")
-                    path(d="M 17,20 V 11 Q 17,10.175 16.412,9.587 15.825,9 15,9 H 6.8 L 8.4,7.4 7,6 3,10 7,14 8.4,12.6 6.8,11 H 15 v 9 z" id="path2")
-
-        div(class="block")
-            button(class="title button bottom-button" id="more-options")
-                label(data-localise="__MSG_settings__") Settings
-                svg(xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="26px" width="26px" fill="currentColor")
-                    path(d="m9.25 22-.4-3.2q-.325-.125-.612-.3-.288-.175-.563-.375L4.7 19.375l-2.75-4.75 2.575-1.95Q4.5 12.5 4.5 12.337v-.675q0-.162.025-.337L1.95 9.375l2.75-4.75 2.975 1.25q.275-.2.575-.375.3-.175.6-.3l.4-3.2h5.5l.4 3.2q.325.125.613.3.287.175.562.375l2.975-1.25 2.75 4.75-2.575 1.95q.025.175.025.337v.675q0 .163-.05.338l2.575 1.95-2.75 4.75-2.95-1.25q-.275.2-.575.375-.3.175-.6.3l-.4 3.2Zm2.8-6.5q1.45 0 2.475-1.025Q15.55 13.45 15.55 12q0-1.45-1.025-2.475Q13.5 8.5 12.05 8.5q-1.475 0-2.488 1.025Q8.55 10.55 8.55 12q0 1.45 1.012 2.475Q10.575 15.5 12.05 15.5Z")
-
-        div(class="space")
-        script(type="module" src="../options/init.js")
-        script(type="module" src="./popup.js")
\ No newline at end of file
diff --git a/src/pages/popup/style.css b/src/pages/popup/style.css
deleted file mode 100644
index 6c258d75..00000000
--- a/src/pages/popup/style.css
+++ /dev/null
@@ -1,53 +0,0 @@
-body {
-	width: 270px;
-	min-height: auto;
-}
-
-html,
-body {
-	margin: 0;
-}
-
-.hide {
-	display: none !important;
-}
-
-.button {
-	display: flex;
-	margin: 0 auto;
-	justify-content: space-between;
-}
-
-.button svg {
-	width: 26px;
-	height: 26px;
-}
-
-.bottom-button {
-	width: 100%;
-}
-
-.space {
-	height: 10px;
-}
-
-input {
-	height: 23px;
-	width: 46px;
-}
-
-
-div.block label {
-	margin: 0;
-	font-size: 18px;
-	font-weight: bold;
-	max-width: 180px;
-}
-
-div.block label:hover {
-	cursor: pointer;
-}
-
-div.block div {
-	display: flex;
-}
\ No newline at end of file
diff --git a/src/pages/popup/switches.pug b/src/pages/popup/switches.pug
deleted file mode 100644
index bea107d2..00000000
--- a/src/pages/popup/switches.pug
+++ /dev/null
@@ -1,14 +0,0 @@
-each _, service in services    
-    div(class=`${service} block hide`)
-        a(class="title" href=services[service].url)
-            if services[service].imageType == 'svgMono'
-                img(class='dark' src=`/assets/images/${service}-icon.svg`)
-                img(class='light' src=`/assets/images/${service}-icon-light.svg`)
-            else
-                img(src=`/assets/images/${service}-icon.${services[service].imageType}`)
-            label=services[service].name
-        div 
-            input(class=`${service}-enabled` type="checkbox" aria-label=`toggle ${services[service].name}`)
-            button(class=`${service}-change_instance title button` aria-label=`change instance for ${services[service].name}`)
-                svg(xmlns="http://www.w3.org/2000/svg" height="26px" width="26px" fill="currentColor")
-                    path(d="M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6 0 1.01-.25 1.97-.7 2.8l1.46 1.46C19.54 15.03 20 13.57 20 12c0-4.42-3.58-8-8-8zm0 14c-3.31 0-6-2.69-6-6 0-1.01.25-1.97.7-2.8L5.24 7.74C4.46 8.97 4 10.43 4 12c0 4.42 3.58 8 8 8v3l4-4-4-4v3z")
\ No newline at end of file
diff --git a/src/pages/popup_src/App.svelte b/src/pages/popup_src/App.svelte
new file mode 100644
index 00000000..f6699312
--- /dev/null
+++ b/src/pages/popup_src/App.svelte
@@ -0,0 +1,78 @@
+<script>
+  const browser = window.browser || window.chrome
+
+  import utils from "../../assets/javascripts/utils.js"
+  import { onDestroy } from "svelte"
+  import servicesHelper from "../../assets/javascripts/services.js"
+  import { onMount } from "svelte"
+  import Buttons from "./Buttons.svelte"
+
+  import { options, config, page } from "./stores"
+
+  let _options
+  const unsubscribeOptions = options.subscribe(val => {
+    if (val) {
+      _options = val
+      browser.storage.local.set({ options: val })
+    }
+  })
+
+  let _config
+  const unsubscribeConfig = config.subscribe(val => (_config = val))
+
+  onDestroy(() => {
+    unsubscribeOptions()
+    unsubscribeConfig()
+  })
+
+  onMount(async () => {
+    let opts = await utils.getOptions()
+    if (!opts) {
+      await servicesHelper.initDefaults()
+      opts = await utils.getOptions()
+    }
+    options.set(opts)
+    config.set(await utils.getConfig())
+  })
+
+  let _page
+  page.subscribe(val => (_page = val))
+
+  let style
+  $: if (_options) style = utils.style(_options, window)
+</script>
+
+{#if _options && _config}
+  <div class="main" dir="auto" {style}>
+    <Buttons />
+  </div>
+{:else}
+  <p>Loading...</p>
+{/if}
+
+<style>
+  :global(html, body) {
+    min-width: 260px;
+    height: min-content;
+    min-height: auto;
+    margin: 0;
+    padding: 0;
+    box-sizing: border-box;
+  }
+
+  :global(body) {
+    margin-top: -20px;
+  }
+
+  div {
+    font-weight: bold;
+    height: 100%;
+    margin: 0;
+    padding: 10px;
+    padding-top: 20px;
+    font-family: "Inter", sans-serif;
+    font-size: 16px;
+    background-color: var(--bg-main);
+    color: var(--text);
+  }
+</style>
diff --git a/src/pages/popup_src/Buttons.svelte b/src/pages/popup_src/Buttons.svelte
new file mode 100644
index 00000000..ab5682dc
--- /dev/null
+++ b/src/pages/popup_src/Buttons.svelte
@@ -0,0 +1,145 @@
+<script>
+  const browser = window.browser || window.chrome
+
+  import Row from "./components/Row.svelte"
+  import Label from "../components/Label.svelte"
+  import CopyIcon from "../icons/CopyIcon.svelte"
+  import RedirectToOriginalIcon from "../icons/RedirectToOriginalIcon.svelte"
+  import RedirectIcon from "../icons/RedirectIcon.svelte"
+  import SwitchInstanceIcon from "../icons/SwitchInstanceIcon.svelte"
+  import SettingsIcon from "../icons/SettingsIcon.svelte"
+  import { options, config } from "./stores"
+  import { onDestroy } from "svelte"
+  import servicesHelper from "../../assets/javascripts/services"
+  import Switch from "./components/Switch.svelte"
+
+  let _options
+  let _config
+
+  const unsubscribeOptions = options.subscribe(val => (_options = val))
+  const unsubscribeConfig = config.subscribe(val => (_config = val))
+  onDestroy(() => {
+    unsubscribeOptions()
+    unsubscribeConfig()
+  })
+
+  let url
+  let switchInstance
+  let redirectToOriginal
+  let redirect
+  let currentService
+  browser.tabs.query({ active: true, currentWindow: true }, async tabs => {
+    if (tabs[0].url) {
+      url = new URL(tabs[0].url)
+      servicesHelper.switchInstance(url).then(r => (switchInstance = r))
+      servicesHelper.reverse(url).then(r => (redirectToOriginal = r))
+      servicesHelper.redirectAsync(url, "main_frame", null, null, false, true).then(r => (redirect = r))
+      servicesHelper.computeService(url).then(r => (currentService = r))
+    }
+  })
+</script>
+
+<div class={document.body.dir}>
+  {#if redirect}
+    <Row
+      class="interactive"
+      on:click={() => {
+        browser.tabs.query({ active: true, currentWindow: true }, tabs => {
+          browser.runtime.sendMessage({ message: "redirect", tabId: tabs[0].id }, () => {
+            browser.tabs.update({ url: redirect })
+          })
+        })
+      }}
+    >
+      <Label>{browser.i18n.getMessage("redirect") || "Redirect"}</Label>
+      <RedirectIcon />
+    </Row>
+  {/if}
+
+  {#if switchInstance}
+    <Row
+      class="interactive"
+      on:click={async () =>
+        browser.tabs.update({ url: switchInstance }, () => {
+          window.close()
+        })}
+    >
+      <Label>{browser.i18n.getMessage("switchInstance") || "Switch Instance"}</Label>
+      <SwitchInstanceIcon />
+    </Row>
+  {/if}
+
+  {#if redirectToOriginal}
+    <Row class="interactive" on:click={() => servicesHelper.copyRaw(url)}>
+      <Label>{browser.i18n.getMessage("copyOriginal") || "Copy Original"}</Label>
+      <CopyIcon />
+    </Row>
+    <Row
+      class="interactive"
+      on:click={() => {
+        browser.tabs.query({ active: true, currentWindow: true }, tabs => {
+          browser.runtime.sendMessage({ message: "reverse", tabId: tabs[0].id }, () => {
+            browser.tabs.update({ url: redirectToOriginal })
+          })
+        })
+      }}
+    >
+      <Label>{browser.i18n.getMessage("redirectToOriginal" || "Redirect to Original")}</Label>
+      <RedirectToOriginalIcon />
+    </Row>
+  {/if}
+
+  {#if redirect || switchInstance || redirectToOriginal}
+    <hr />
+  {/if}
+
+  {#if currentService}
+    <Switch serviceKey={currentService} {url} />
+    <hr />
+  {/if}
+
+  {#each _options.popupServices as serviceKey}
+    {#if currentService !== serviceKey}
+      <Switch {serviceKey} {url} />
+    {/if}
+  {/each}
+
+  <hr />
+
+  <Row
+    class="interactive"
+    on:click={() =>
+      browser.tabs.create({ url: browser.runtime.getURL("pages/options/index.html") }, () => {
+        window.close()
+      })}
+  >
+    <Label>{browser.i18n.getMessage("settings")}</Label>
+    <SettingsIcon />
+  </Row>
+</div>
+
+<style>
+  :global(.interactive) {
+    transition: 0.1s;
+  }
+  :global(.interactive:hover) {
+    color: var(--active);
+    cursor: pointer;
+  }
+  :global(.interactive:active) {
+    transform: translateY(1px);
+  }
+
+  :global(img, svg) {
+    margin-right: 5px;
+    margin-left: 0;
+    height: 26px;
+    width: 26px;
+    color: var(--text);
+  }
+
+  :global(.rtl img, .rtl svg) {
+    margin-right: 0;
+    margin-left: 5px;
+  }
+</style>
diff --git a/src/pages/popup_src/components/Row.svelte b/src/pages/popup_src/components/Row.svelte
new file mode 100644
index 00000000..a4d78f07
--- /dev/null
+++ b/src/pages/popup_src/components/Row.svelte
@@ -0,0 +1,13 @@
+<div {...$$props} on:click>
+    <slot></slot>
+  </div>
+  
+  <style>
+    div {
+      justify-content: space-between;
+      display: flex;
+      align-items: center;
+      margin: 10px 0;
+    }
+  </style>
+  
\ No newline at end of file
diff --git a/src/pages/popup_src/components/ServiceIcon.svelte b/src/pages/popup_src/components/ServiceIcon.svelte
new file mode 100644
index 00000000..89393cf6
--- /dev/null
+++ b/src/pages/popup_src/components/ServiceIcon.svelte
@@ -0,0 +1,40 @@
+<script>
+  import { onDestroy } from "svelte"
+  export let details
+  import { config, options } from "../stores"
+  let _options
+  let _config
+
+  const unsubscribeOptions = options.subscribe(val => (_options = val))
+  const unsubscribeConfig = config.subscribe(val => (_config = val))
+  onDestroy(() => {
+    unsubscribeOptions()
+    unsubscribeConfig()
+  })
+
+  let theme
+  $: if (_options) {
+    if (_options.theme == "dark") {
+      theme = "dark"
+    } else if (_options.theme == "light") {
+      theme = "light"
+    } else if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
+      theme = "dark"
+    } else {
+      theme = "light"
+    }
+  }
+  $: imageType = _config.services[details.value].imageType
+</script>
+
+{#if imageType}
+  {#if imageType == "svgMono"}
+    {#if theme == "dark"}
+      <img src={`/assets/images/${details.value}-icon-light.svg`} alt={details.label} />
+    {:else}
+      <img src={`/assets/images/${details.value}-icon.svg`} alt={details.label} />
+    {/if}
+  {:else}
+    <img src={`/assets/images/${details.value}-icon.${imageType}`} alt={details.label} />
+  {/if}
+{/if}
diff --git a/src/pages/popup_src/components/Switch.svelte b/src/pages/popup_src/components/Switch.svelte
new file mode 100644
index 00000000..98f765b7
--- /dev/null
+++ b/src/pages/popup_src/components/Switch.svelte
@@ -0,0 +1,81 @@
+<script>
+  const browser = window.browser || window.chrome
+
+  import Checkbox from "../../components/Checkbox.svelte"
+  import Label from "../../components/Label.svelte"
+  import SwitchInstanceIcon from "../../icons/SwitchInstanceIcon.svelte"
+  import Row from "./Row.svelte"
+  import ServiceIcon from "./ServiceIcon.svelte"
+  import { onDestroy } from "svelte"
+  import servicesHelper from "../../../assets/javascripts/services"
+  import { options, config } from "../stores"
+  import SettingsIcon from "../../icons/SettingsIcon.svelte"
+
+  let _options
+  let _config
+
+  const unsubscribeOptions = options.subscribe(val => (_options = val))
+  const unsubscribeConfig = config.subscribe(val => (_config = val))
+  onDestroy(() => {
+    unsubscribeOptions()
+    unsubscribeConfig()
+  })
+
+  export let serviceKey
+  export let url
+</script>
+
+<Row>
+  <div
+    class="interactive margin margin_{document.body.dir}"
+    on:keydown={null}
+    on:click={() =>
+      browser.tabs.create({ url: _config.services[serviceKey].url }, () => {
+        window.close()
+      })}
+  >
+    <ServiceIcon details={{ value: serviceKey, label: _config.services[serviceKey].name }} />
+    <Label>{_config.services[serviceKey].name}</Label>
+  </div>
+  <div>
+    <Checkbox
+      class="margin margin_{document.body.dir}"
+      label="Enable"
+      checked={_options[serviceKey].enabled}
+      onChange={e => {
+        _options[serviceKey].enabled = e.target.checked
+        options.set(_options)
+      }}
+    />
+    <SwitchInstanceIcon
+      class="interactive"
+      on:click={async () =>
+        browser.tabs.update({ url: await servicesHelper.switchInstance(url, serviceKey) }, () => {
+          window.close()
+        })}
+    />
+    <SettingsIcon
+      class="interactive"
+      on:click={() =>
+        browser.tabs.create({ url: browser.runtime.getURL(`pages/options/index.html#services:${serviceKey}`) }, () => {
+          window.close()
+        })}
+    />
+  </div>
+</Row>
+
+<style>
+  div {
+    display: flex;
+    align-items: center;
+  }
+
+  :global(.margin) {
+    margin-right: 5px;
+    margin-left: 0;
+  }
+  :global(.margin_rtl) {
+    margin-right: 0;
+    margin-left: 5px;
+  }
+</style>
diff --git a/src/pages/popup_src/main.js b/src/pages/popup_src/main.js
new file mode 100644
index 00000000..c4012f4a
--- /dev/null
+++ b/src/pages/popup_src/main.js
@@ -0,0 +1,7 @@
+import App from "./App.svelte"
+
+const app = new App({
+  target: document.body,
+})
+
+export default app
diff --git a/src/pages/popup_src/stores.js b/src/pages/popup_src/stores.js
new file mode 100644
index 00000000..782f6064
--- /dev/null
+++ b/src/pages/popup_src/stores.js
@@ -0,0 +1,5 @@
+import { writable } from "svelte/store"
+
+export const options = writable(null)
+export const config = writable(null)
+export const page = writable("general")
diff --git a/src/pages/stylesheets/styles.css b/src/pages/stylesheets/styles.css
deleted file mode 100644
index 2519a05f..00000000
--- a/src/pages/stylesheets/styles.css
+++ /dev/null
@@ -1,391 +0,0 @@
-body {
-	--text: #fff;
-	--bg-main: #121212;
-	--bg-secondary: #202020;
-	--active: #fbc117;
-	--danger: #f04141;
-	--light-grey: #c3c3c3;
-}
-
-@font-face {
-	font-family: "Inter";
-	src: url("Inter-VariableFont_slnt,wght.ttf");
-	font-weight: normal;
-	font-style: normal;
-}
-
-@font-face {
-	font-family: "Vazirmatn";
-	src: url("Vazirmatn-VariableFont_wght.ttf");
-	font-weight: normal;
-	font-style: normal;
-}
-
-body {
-	margin: auto;
-	padding: 0;
-	font-family: "Inter";
-	font-size: 16px;
-	background-color: var(--bg-main);
-	color: var(--text);
-}
-
-body * {
-	font-family: "Inter";
-}
-
-body.rtl {
-	font-family: "Vazirmatn";
-}
-
-body.rtl * {
-	font-family: "Vazirmatn";
-}
-
-div.block input[type="checkbox"] {
-	appearance: none;
-	-moz-appearance: none;
-	-webkit-appearance: none;
-}
-
-.title {
-	display: flex;
-	align-items: center;
-	text-decoration: none;
-	width: min-content;
-	color: var(--text);
-	transition: .1s;
-}
-
-.title:hover {
-	opacity: 1 !important;
-}
-
-.title:hover a {
-	color: var(--active);
-}
-
-img,
-svg {
-	margin-right: 10px;
-	height: 26px;
-	width: 26px;
-	color: var(--text);
-}
-
-body.rtl img,
-body.rtl svg {
-	margin-right: 0px;
-	margin-left: 10px;
-}
-
-input[type="url"],
-input[type="text"],
-select {
-	font-weight: bold;
-	box-sizing: border-box;
-	border-style: solid;
-	border-color: #767676;
-	color: var(--text);
-	font-size: 16px;
-	padding: 8px;
-	background-color: var(--bg-secondary);
-	border: none;
-	margin: 0;
-	max-width: 500px;
-	border-radius: 3px;
-}
-
-input[type="url"],
-input[type="text"] {
-	width: 400px;
-	cursor: text;
-}
-
-input:invalid {
-	color: var(--danger);
-}
-
-.button svg {
-	height: 18px;
-	width: 18px;
-}
-
-section.block-option {
-	width: 750px;
-	margin: 0 50px;
-}
-
-section.block-option h2 {
-	margin: 0;
-}
-
-body.option {
-	display: flex;
-	padding: 40px;
-	width: 1160px;
-}
-
-section.links {
-	display: flex;
-	flex-wrap: wrap;
-	flex-direction: column;
-	width: 350px;
-	max-height: 930px;
-}
-
-section.links div {
-	margin: 10px;
-	width: max-content;
-}
-
-a {
-	text-decoration: none;
-	color: var(--text);
-	transition: 0.1s;
-}
-
-a:hover {
-	color: var(--active);
-}
-
-section.links a {
-	display: flex;
-	align-items: center;
-	font-size: 18px;
-	text-decoration: none;
-	color: white;
-	transition: 0.1s;
-}
-
-section.links a:hover,
-section.links .selected {
-	opacity: 1 !important;
-}
-
-section.links .selected a {
-	color: var(--active);
-}
-
-::placeholder {
-	color: var(--text);
-	opacity: 0.7;
-}
-
-hr {
-	height: 2px;
-	margin: 0 15px;
-	background-color: rgb(77, 77, 77);
-	border: none;
-}
-
-div.block {
-	padding: 0 15px;
-	justify-content: space-between;
-	display: flex;
-	align-items: center;
-	margin-top: 10px;
-	margin-bottom: 10px;
-}
-
-div.block-option {
-	margin: 30px 0;
-}
-
-div.block-option label {
-	margin-right: 5px;
-	width: 80%;
-	min-width: 150px;
-	font-size: 18px;
-}
-
-div.block-option h1 {
-	margin: 0;
-	font-size: 28px;
-	color: var(--text);
-}
-
-div.block-option div {
-	text-align: center;
-}
-
-div.block input[type="checkbox"] {
-	width: 46px;
-	height: 24px;
-	background-color: var(--light-grey);
-	border-radius: 50px;
-	transition: 0.4s;
-	cursor: pointer;
-}
-
-div.block input[type="checkbox"]:checked {
-	background-color: var(--active);
-}
-
-div.block input[type="checkbox"]::before {
-	content: "";
-	display: inline-block;
-	width: 18px;
-	height: 18px;
-	box-sizing: border-box;
-	position: relative;
-	top: 2.5px;
-	left: 3.5px;
-	background-color: white;
-	border-radius: 50%;
-	transition: 0.3s;
-}
-
-body.rtl div.block input[type="checkbox"]::before {
-	left: auto;
-	right: 4px;
-}
-
-div.block input[type="checkbox"]:checked::before {
-	left: 24px;
-}
-
-body.rtl div.block input[type="checkbox"]:checked::before {
-	left: auto;
-	right: 24px;
-}
-
-div.buttons {
-	display: flex;
-	margin: 0 15px;
-	margin-bottom: 15px;
-	margin-top: 15px;
-	flex-wrap: wrap;
-	align-items: center;
-	justify-content: start;
-}
-
-.button {
-	color: var(--text);
-	font-size: 16px;
-	font-weight: bold;
-	text-decoration: none;
-	cursor: pointer;
-	transition-duration: 0.1s;
-}
-
-.button:hover {
-	color: var(--active);
-}
-
-.button svg {
-	width: auto;
-	height: auto;
-	padding: 0;
-	margin-right: 5px;
-}
-
-.button:hover svg {
-	color: var(--active);
-}
-
-.button-inline {
-	display: inline-flex;
-	align-items: center;
-	margin: 7.5px 0;
-	background-color: var(--bg-secondary);
-	border-radius: 5px;
-	padding: 10px;
-}
-
-.button:active {
-	transform: translateY(1px);
-}
-
-button svg {
-	color: var(--text);
-}
-
-div.checklist div {
-	justify-content: space-between;
-	margin: 5px 15px;
-	padding: 10px 0;
-	word-wrap: break-word;
-	display: flex;
-}
-
-div.checklist a {
-	text-decoration: none;
-	color: var(--text);
-}
-
-div.checklist a:hover {
-	text-decoration: underline;
-}
-
-div.custom-checklist x a {
-	color: var(--active);
-}
-
-button.add {
-	background-color: transparent;
-	border: none;
-	padding: 0;
-	margin: 0;
-	text-decoration: none;
-	display: inline-block;
-	cursor: pointer;
-}
-
-body.light-theme {
-	--text: black;
-	--bg-main: white;
-	--bg-secondary: #e4e4e4;
-	--active: #fb9817;
-}
-
-body.light-theme select {
-	border: 1px solid black;
-}
-
-body.light-theme a {
-	color: black;
-}
-
-body.light-theme a:hover {
-	color: var(--active)
-}
-
-button {
-	background-color: transparent;
-	color: var(--text);
-	border: none;
-	text-decoration: none;
-	display: inline-block;
-	cursor: pointer;
-	border-radius: 5px;
-}
-
-body div section {
-	display: none;
-}
-
-select:disabled {
-	opacity: 0.6;
-	cursor: not-allowed;
-}
-
-input:disabled {
-	opacity: 0.6;
-	cursor: not-allowed;
-}
-
-
-@media (max-width: 1250px) {
-	body.option {
-		flex-direction: column;
-		width: 750px;
-		align-items: center;
-	}
-
-	section.links {
-		flex-direction: row;
-		width: 750px;
-		padding: 0 55px;
-	}
-}
\ No newline at end of file
diff --git a/src/pages/widgets/head.pug b/src/pages/widgets/head.pug
deleted file mode 100644
index 0c6e9fdb..00000000
--- a/src/pages/widgets/head.pug
+++ /dev/null
@@ -1,7 +0,0 @@
-head
-    meta(charset="utf-8")
-    meta(name="viewport" content="width=device-width, initial-scale=1")
-    link(rel="icon" type="image/x-icon" href="../../../assets/images/libredirect.svg")
-    link(href="../stylesheets/styles.css" rel="stylesheet")
-    title Settings
-    script(type="module" src="./init.js")
\ No newline at end of file
diff --git a/src/pages/widgets/links.pug b/src/pages/widgets/links.pug
deleted file mode 100644
index 318c72f9..00000000
--- a/src/pages/widgets/links.pug
+++ /dev/null
@@ -1,22 +0,0 @@
-section(class="links" id="links")
-    div(class="title")
-        a(href="#general") 
-            include /src/assets/images/general-icon.svg
-            span(data-localise="__MSG_general__") General
-
-    each val, key in services
-        div(class="title" id=`${key}-link`)
-            a(href="#"+key)
-                if services[key].imageType == 'svgMono'
-                    img(class='dark' src=`/assets/images/${key}-icon.svg`)
-                    img(class='light' src=`/assets/images/${key}-icon-light.svg`)
-                else
-                    img(src=`/assets/images/${key}-icon.${services[key].imageType}`)
-                span=services[key].name
-
-    div(class="title")
-        a(target="_blank" href="https://libredirect.github.io")
-            img(class='dark' src="/assets/images/about-icon.svg")
-            img(class='light' src="/assets/images/about-icon-light.svg")
-
-            span(data-localise="__MSG_about__") About