diff --git a/.gitignore b/.gitignore
index 5e068e1..4933660 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,5 +4,6 @@
/server/ui/dist
/Makefile
/mailpit*
+/.idea
*.old
*.db
diff --git a/server/ui-src/App.vue b/server/ui-src/App.vue
index 8b3fd67..68a270a 100644
--- a/server/ui-src/App.vue
+++ b/server/ui-src/App.vue
@@ -933,7 +933,7 @@ export default {
-
+
+
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
diff --git a/server/ui-src/app.js b/server/ui-src/app.js
index 4b0d6c8..dddf71b 100644
--- a/server/ui-src/app.js
+++ b/server/ui-src/app.js
@@ -3,5 +3,6 @@ import App from './App.vue';
import "./assets/styles.scss";
import "../../node_modules/bootstrap-icons/font/bootstrap-icons.scss";
import "bootstrap";
+import "./color-modes";
createApp(App).mount('#app');
diff --git a/server/ui-src/color-modes.js b/server/ui-src/color-modes.js
new file mode 100644
index 0000000..f9c437e
--- /dev/null
+++ b/server/ui-src/color-modes.js
@@ -0,0 +1,94 @@
+/*!
+ * Color mode toggler for Bootstrap's docs (https://getbootstrap.com/)
+ * Copyright 2011-2023 The Bootstrap Authors
+ * Licensed under the Creative Commons Attribution 3.0 Unported License.
+ */
+
+(() => {
+ 'use strict';
+
+ const getStoredTheme = () => localStorage.getItem('theme');
+ const setStoredTheme = (theme) => localStorage.setItem('theme', theme);
+
+ const getPreferredTheme = () => {
+ const storedTheme = getStoredTheme();
+ if (storedTheme) {
+ return storedTheme;
+ }
+
+ return window.matchMedia('(prefers-color-scheme: dark)').matches
+ ? 'dark'
+ : 'light';
+ };
+
+ const setTheme = (theme) => {
+ if (
+ theme === 'auto' &&
+ window.matchMedia('(prefers-color-scheme: dark)').matches
+ ) {
+ document.documentElement.setAttribute('data-bs-theme', 'dark');
+ } else {
+ document.documentElement.setAttribute('data-bs-theme', theme);
+ }
+ };
+
+ setTheme(getPreferredTheme());
+
+ const showActiveTheme = (theme, focus = false) => {
+ const themeSwitcher = document.querySelector('#bd-theme');
+
+ if (!themeSwitcher) {
+ return;
+ }
+
+ const themeSwitcherText = document.querySelector('#bd-theme-text');
+ const activeThemeIcon = document.querySelector('.theme-icon-active use');
+ const btnToActive = document.querySelector(
+ `[data-bs-theme-value="${theme}"]`
+ );
+ const svgOfActiveBtn = btnToActive
+ .querySelector('svg use')
+ .getAttribute('href');
+
+ document.querySelectorAll('[data-bs-theme-value]').forEach((element) => {
+ element.classList.remove('active');
+ element.setAttribute('aria-pressed', 'false');
+ });
+
+ btnToActive.classList.add('active');
+ btnToActive.setAttribute('aria-pressed', 'true');
+ activeThemeIcon.setAttribute('href', svgOfActiveBtn);
+ const themeSwitcherLabel = `${themeSwitcherText.textContent} (${btnToActive.dataset.bsThemeValue})`;
+ themeSwitcher.setAttribute('aria-label', themeSwitcherLabel);
+
+ if (focus) {
+ themeSwitcher.focus();
+ }
+ };
+
+ window
+ .matchMedia('(prefers-color-scheme: dark)')
+ .addEventListener('change', () => {
+ const storedTheme = getStoredTheme();
+ if (storedTheme !== 'light' && storedTheme !== 'dark') {
+ setTheme(getPreferredTheme());
+ }
+ });
+
+ window.addEventListener('DOMContentLoaded', () => {
+ showActiveTheme(getPreferredTheme());
+
+ document.querySelectorAll('[data-bs-theme-value]').forEach((toggle) => {
+ toggle.addEventListener('click', () => {
+ const theme = toggle.getAttribute('data-bs-theme-value');
+ setStoredTheme(theme);
+ setTheme(theme);
+ showActiveTheme(theme, true);
+ });
+ });
+ });
+})();
+
+document.querySelectorAll('[data-bs-toggle="popover"]').forEach((popover) => {
+ new bootstrap.Popover(popover);
+});