From 63add0376e7771ac53939995812aea9f0db8ff93 Mon Sep 17 00:00:00 2001
From: Nelson Chan <chakflying@hotmail.com>
Date: Mon, 12 Feb 2024 06:22:41 +0800
Subject: [PATCH 1/2] Feat: Impl. server default appearance by injected JSON

---
 server/server.js                              |   3 +
 server/utils/inject-default-appearance.js     |  50 ++++
 src/components/HorizontalTabHeader.vue        |  66 +++++
 src/components/settings/Appearance.vue        | 251 ++++++------------
 .../settings/AppearanceSettings.vue           | 164 ++++++++++++
 src/mixins/theme.js                           |  18 ++
 src/pages/Settings.vue                        |   9 +
 7 files changed, 391 insertions(+), 170 deletions(-)
 create mode 100644 server/utils/inject-default-appearance.js
 create mode 100644 src/components/HorizontalTabHeader.vue
 create mode 100644 src/components/settings/AppearanceSettings.vue

diff --git a/server/server.js b/server/server.js
index eeffb0635..9728c3b15 100644
--- a/server/server.js
+++ b/server/server.js
@@ -282,6 +282,9 @@ let needSetup = false;
     const statusPageRouter = require("./routers/status-page-router");
     app.use(statusPageRouter);
 
+    const { injectDefaultAppearance } = require("./utils/inject-default-appearance");
+    app.use(injectDefaultAppearance);
+
     // Universal Route Handler, must be at the end of all express routes.
     app.get("*", async (_request, response) => {
         if (_request.originalUrl.startsWith("/upload/")) {
diff --git a/server/utils/inject-default-appearance.js b/server/utils/inject-default-appearance.js
new file mode 100644
index 000000000..fcf0847ce
--- /dev/null
+++ b/server/utils/inject-default-appearance.js
@@ -0,0 +1,50 @@
+const { Settings } = require("../settings");
+const cheerio = require("cheerio");
+const jsesc = require("jsesc");
+const { log } = require("../../src/util");
+
+const injectDefaultAppearance = (req, res, next) => {
+
+    try {
+        // Intercept send() calls and inject Default Appearance
+        // https://stackoverflow.com/a/60817116
+        const oldSend = res.send;
+        res.send = async (data) => {
+
+            if (typeof data === "string") {
+                log.debug("inject-default-appearance", req.method + " " + req.url);
+                const $ = cheerio.load(data);
+
+                const defaultAppearance = await Settings.get("defaultAppearance");
+                if (defaultAppearance) {
+                    const head = $("head");
+
+                    const escapedJSONObject = jsesc(defaultAppearance, { isScriptContext: true });
+
+                    const script = $(`
+                        <script id="default-appearance" data-json="{}">
+                            window.defaultAppearance = ${escapedJSONObject};
+                        </script>
+                    `);
+
+                    head.append(script);
+
+                    data = $.root().html();
+                }
+            }
+
+            res.send = oldSend; // set function back to avoid 'double-send'
+            return res.send(data);
+        };
+
+        next();
+    } catch (e) {
+
+        next(e);
+    }
+
+};
+
+module.exports = {
+    injectDefaultAppearance
+};
diff --git a/src/components/HorizontalTabHeader.vue b/src/components/HorizontalTabHeader.vue
new file mode 100644
index 000000000..3857394dc
--- /dev/null
+++ b/src/components/HorizontalTabHeader.vue
@@ -0,0 +1,66 @@
+<template>
+    <div>
+        <ul class="nav nav-tabs">
+            <li v-for="(tab, index) in tabs" :key="index" class="nav-item">
+                <a class="nav-link" :class="{ active: index == selected }" href="#" @click="$emit('update:selected', index)">
+                    {{ tab }}
+                </a>
+            </li>
+        </ul>
+    </div>
+</template>
+
+<script lang="js">
+export default {
+    props: {
+        tabs: {
+            type: Array[String],
+            required: true,
+        },
+        selected: {
+            type: Number,
+            required: true,
+        },
+    },
+    emits: [ "update:selected" ],
+    data() {
+        return {};
+    },
+};
+</script>
+
+<style lang="scss" scoped>
+@import "../assets/vars.scss";
+
+.nav-tabs {
+    border-bottom: 1px solid $primary;
+
+    .nav-item {
+        flex-grow: 1;
+
+        .nav-link {
+            text-align: center;
+            border-top-left-radius: 10px;
+            border-top-right-radius: 10px;
+
+            &.active {
+                background-color: $highlight-white;
+                .dark & {
+                    color: $dark-font-color;
+                    background-color: $dark-header-bg;
+                }
+                border-color: transparent transparent $primary transparent;
+                border-width: 1px 1px 6px 1px;
+            }
+
+            &:hover {
+                .dark & {
+                    background-color: $dark-header-bg;
+                }
+                border-color: $primary;
+                border-width: 1px 1px 6px 1px;
+            }
+        }
+    }
+}
+</style>
diff --git a/src/components/settings/Appearance.vue b/src/components/settings/Appearance.vue
index a1391d614..04411960f 100644
--- a/src/components/settings/Appearance.vue
+++ b/src/components/settings/Appearance.vue
@@ -1,186 +1,97 @@
 <template>
     <div>
-        <div class="my-4">
-            <label for="language" class="form-label">
-                {{ $t("Language") }}
-            </label>
-            <select id="language" v-model="$root.language" class="form-select">
-                <option
-                    v-for="(lang, i) in $i18n.availableLocales"
-                    :key="`Lang${i}`"
-                    :value="lang"
-                >
-                    {{ $i18n.messages[lang].languageName }}
-                </option>
-            </select>
-        </div>
-        <div class="my-4">
-            <label for="timezone" class="form-label">{{ $t("Theme") }}</label>
-            <div>
-                <div
-                    class="btn-group"
-                    role="group"
-                    aria-label="Basic checkbox toggle button group"
-                >
-                    <input
-                        id="btncheck1"
-                        v-model="$root.userTheme"
-                        type="radio"
-                        class="btn-check"
-                        name="theme"
-                        autocomplete="off"
-                        value="light"
-                    />
-                    <label class="btn btn-outline-primary" for="btncheck1">
-                        {{ $t("Light") }}
-                    </label>
-
-                    <input
-                        id="btncheck2"
-                        v-model="$root.userTheme"
-                        type="radio"
-                        class="btn-check"
-                        name="theme"
-                        autocomplete="off"
-                        value="dark"
-                    />
-                    <label class="btn btn-outline-primary" for="btncheck2">
-                        {{ $t("Dark") }}
-                    </label>
-
-                    <input
-                        id="btncheck3"
-                        v-model="$root.userTheme"
-                        type="radio"
-                        class="btn-check"
-                        name="theme"
-                        autocomplete="off"
-                        value="auto"
-                    />
-                    <label class="btn btn-outline-primary" for="btncheck3">
-                        {{ $t("Auto") }}
-                    </label>
-                </div>
-            </div>
-        </div>
-        <div class="my-4">
-            <label class="form-label">{{ $t("Theme - Heartbeat Bar") }}</label>
-            <div>
-                <div
-                    class="btn-group"
-                    role="group"
-                    aria-label="Basic checkbox toggle button group"
-                >
-                    <input
-                        id="btncheck4"
-                        v-model="$root.userHeartbeatBar"
-                        type="radio"
-                        class="btn-check"
-                        name="heartbeatBarTheme"
-                        autocomplete="off"
-                        value="normal"
-                    />
-                    <label class="btn btn-outline-primary" for="btncheck4">
-                        {{ $t("Normal") }}
-                    </label>
-
-                    <input
-                        id="btncheck5"
-                        v-model="$root.userHeartbeatBar"
-                        type="radio"
-                        class="btn-check"
-                        name="heartbeatBarTheme"
-                        autocomplete="off"
-                        value="bottom"
-                    />
-                    <label class="btn btn-outline-primary" for="btncheck5">
-                        {{ $t("Bottom") }}
-                    </label>
-
-                    <input
-                        id="btncheck6"
-                        v-model="$root.userHeartbeatBar"
-                        type="radio"
-                        class="btn-check"
-                        name="heartbeatBarTheme"
-                        autocomplete="off"
-                        value="none"
-                    />
-                    <label class="btn btn-outline-primary" for="btncheck6">
-                        {{ $t("None") }}
-                    </label>
-                </div>
-            </div>
-        </div>
-
-        <!-- Timeline -->
-        <div class="my-4">
-            <label class="form-label">{{ $t("styleElapsedTime") }}</label>
-            <div>
-                <div class="btn-group" role="group">
-                    <input
-                        id="styleElapsedTimeShowNoLine"
-                        v-model="$root.styleElapsedTime"
-                        type="radio"
-                        class="btn-check"
-                        name="styleElapsedTime"
-                        autocomplete="off"
-                        value="no-line"
-                    />
-                    <label class="btn btn-outline-primary" for="styleElapsedTimeShowNoLine">
-                        {{ $t("styleElapsedTimeShowNoLine") }}
-                    </label>
-
-                    <input
-                        id="styleElapsedTimeShowWithLine"
-                        v-model="$root.styleElapsedTime"
-                        type="radio"
-                        class="btn-check"
-                        name="styleElapsedTime"
-                        autocomplete="off"
-                        value="with-line"
-                    />
-                    <label class="btn btn-outline-primary" for="styleElapsedTimeShowWithLine">
-                        {{ $t("styleElapsedTimeShowWithLine") }}
-                    </label>
-
-                    <input
-                        id="styleElapsedTimeNone"
-                        v-model="$root.styleElapsedTime"
-                        type="radio"
-                        class="btn-check"
-                        name="styleElapsedTime"
-                        autocomplete="off"
-                        value="none"
-                    />
-                    <label class="btn btn-outline-primary" for="styleElapsedTimeNone">
-                        {{ $t("None") }}
-                    </label>
-                </div>
-            </div>
-        </div>
+        <HorizontalTabHeader
+            v-model:selected="storageLoc"
+            class="mt-4"
+            :tabs="['Browser', 'Server']"
+        />
+        <AppearanceSettings
+            v-if="storageLoc == 0"
+            v-model:language="$root.language"
+            v-model:userTheme="$root.userTheme"
+            v-model:userHeartbeatBar="$root.userHeartbeatBar"
+            v-model:styleElapsedTime="$root.styleElapsedTime"
+            :languages="languagesList"
+        />
+        <AppearanceSettings
+            v-if="storageLoc == 1"
+            :language="settings.defaultAppearance.language || ''"
+            :userTheme="settings.defaultAppearance.theme || ''"
+            :userHeartbeatBar="settings.defaultAppearance.heartbeatBarTheme || ''"
+            :styleElapsedTime="settings.defaultAppearance.styleElapsedTime || ''"
+            :languages="languagesList"
+            @update:language="updateDefaultLanguage"
+            @update:user-theme="updateDefaultTheme"
+            @update:user-heartbeat-bar="updateDefaultHeartbeatBarTheme"
+            @update:style-elapsed-time="updateDefaultStyleElapsedTime"
+        />
     </div>
 </template>
 
 <script>
-export default {
+import HorizontalTabHeader from "../HorizontalTabHeader.vue";
+import AppearanceSettings from "./AppearanceSettings.vue";
 
+export default {
+    components: {
+        HorizontalTabHeader,
+        AppearanceSettings
+    },
+    data() {
+        return {
+            storageLoc: 0,
+            languagesList: this.$i18n.availableLocales.map(lang => {
+                return {
+                    value: lang,
+                    label: this.$i18n.messages[lang].languageName,
+                };
+            })
+        };
+    },
+    computed: {
+        settings() {
+            return this.$parent.$parent.$parent.settings;
+        },
+        saveSettings() {
+            return this.$parent.$parent.$parent.saveSettings;
+        }
+    },
+    methods: {
+        updateDefaultLanguage(language) {
+            this.updateDefaultAppearance({
+                ...this.settings.defaultAppearance,
+                language
+            });
+        },
+        updateDefaultTheme(theme) {
+            this.updateDefaultAppearance({
+                ...this.settings.defaultAppearance,
+                theme: theme
+            });
+        },
+        updateDefaultHeartbeatBarTheme(theme) {
+            this.updateDefaultAppearance({
+                ...this.settings.defaultAppearance,
+                heartbeatBarTheme: theme
+            });
+        },
+        updateDefaultStyleElapsedTime(styleElapsedTime) {
+            this.updateDefaultAppearance({
+                ...this.settings.defaultAppearance,
+                styleElapsedTime
+            });
+        },
+        updateDefaultAppearance(appearance) {
+            this.$parent.$parent.$parent.settings.defaultAppearance = appearance;
+            this.saveSettings();
+        }
+    }
 };
 </script>
 
 <style lang="scss" scoped>
 @import "../../assets/vars.scss";
 
-.btn-check:active + .btn-outline-primary,
-.btn-check:checked + .btn-outline-primary,
-.btn-check:hover + .btn-outline-primary {
-    color: #fff;
-
-    .dark & {
-        color: #000;
-    }
-}
-
 .dark {
     .list-group-item {
         background-color: $dark-bg2;
diff --git a/src/components/settings/AppearanceSettings.vue b/src/components/settings/AppearanceSettings.vue
new file mode 100644
index 000000000..54c3470b9
--- /dev/null
+++ b/src/components/settings/AppearanceSettings.vue
@@ -0,0 +1,164 @@
+<template>
+    <div>
+        <div class="my-4">
+            <label for="language" class="form-label">
+                {{ $t("Language") }}
+            </label>
+            <select
+                id="language" :value="language" class="form-select"
+                @input="$emit('update:language', $event.target.value)"
+            >
+                <option v-for="(lang, i) in languages" :key="`Lang${i}`" :value="lang.value">
+                    {{ lang.label }}
+                </option>
+            </select>
+        </div>
+        <div class="my-4">
+            <label for="timezone" class="form-label">{{ $t("Theme") }}</label>
+            <div>
+                <div class="btn-group" role="group" aria-label="Basic checkbox toggle button group">
+                    <input
+                        id="btncheck1" :checked="userTheme == 'light'" type="radio" class="btn-check" name="theme"
+                        autocomplete="off" value="light" @input="$emit('update:userTheme', 'light')"
+                    />
+                    <label class="btn btn-outline-primary" for="btncheck1">
+                        {{ $t("Light") }}
+                    </label>
+
+                    <input
+                        id="btncheck2" :checked="userTheme == 'dark'" type="radio" class="btn-check" name="theme"
+                        autocomplete="off" value="dark" @input="$emit('update:userTheme', 'dark')"
+                    />
+                    <label class="btn btn-outline-primary" for="btncheck2">
+                        {{ $t("Dark") }}
+                    </label>
+
+                    <input
+                        id="btncheck3" :checked="userTheme == 'auto'" type="radio" class="btn-check" name="theme"
+                        autocomplete="off" value="auto" @input="$emit('update:userTheme', 'auto')"
+                    />
+                    <label class="btn btn-outline-primary" for="btncheck3">
+                        {{ $t("Auto") }}
+                    </label>
+                </div>
+            </div>
+        </div>
+        <div class="my-4">
+            <label class="form-label">{{ $t("Theme - Heartbeat Bar") }}</label>
+            <div>
+                <div class="btn-group" role="group" aria-label="Basic checkbox toggle button group">
+                    <input
+                        id="btncheck4" :checked="userHeartbeatBar == 'normal'" type="radio" class="btn-check"
+                        name="heartbeatBarTheme" autocomplete="off" value="normal"
+                        @input="$emit('update:userHeartbeatBar', 'normal')"
+                    />
+                    <label class="btn btn-outline-primary" for="btncheck4">
+                        {{ $t("Normal") }}
+                    </label>
+
+                    <input
+                        id="btncheck5" :checked="userHeartbeatBar == 'bottom'" type="radio" class="btn-check"
+                        name="heartbeatBarTheme" autocomplete="off" value="bottom"
+                        @input="$emit('update:userHeartbeatBar', 'bottom')"
+                    />
+                    <label class="btn btn-outline-primary" for="btncheck5">
+                        {{ $t("Bottom") }}
+                    </label>
+
+                    <input
+                        id="btncheck6" :checked="userHeartbeatBar == 'none'" type="radio" class="btn-check"
+                        name="heartbeatBarTheme" autocomplete="off" value="none"
+                        @input="$emit('update:userHeartbeatBar', 'none')"
+                    />
+                    <label class="btn btn-outline-primary" for="btncheck6">
+                        {{ $t("None") }}
+                    </label>
+                </div>
+            </div>
+        </div>
+
+        <!-- Timeline -->
+        <div class="my-4">
+            <label class="form-label">{{ $t("styleElapsedTime") }}</label>
+            <div>
+                <div class="btn-group" role="group">
+                    <input
+                        id="styleElapsedTimeShowNoLine" :checked="styleElapsedTime == 'no-line'" type="radio"
+                        class="btn-check" name="styleElapsedTime" autocomplete="off" value="no-line"
+                        @input="$emit('update:styleElapsedTime', 'no-line')"
+                    />
+                    <label class="btn btn-outline-primary" for="styleElapsedTimeShowNoLine">
+                        {{ $t("styleElapsedTimeShowNoLine") }}
+                    </label>
+
+                    <input
+                        id="styleElapsedTimeShowWithLine" :checked="styleElapsedTime == 'with-line'" type="radio"
+                        class="btn-check" name="styleElapsedTime" autocomplete="off" value="with-line"
+                        @input="$emit('update:styleElapsedTime', 'with-line')"
+                    />
+                    <label class="btn btn-outline-primary" for="styleElapsedTimeShowWithLine">
+                        {{ $t("styleElapsedTimeShowWithLine") }}
+                    </label>
+
+                    <input
+                        id="styleElapsedTimeNone" :checked="styleElapsedTime == 'none'" type="radio" class="btn-check"
+                        name="styleElapsedTime" autocomplete="off" value="none"
+                        @input="$emit('update:styleElapsedTime', 'none')"
+                    />
+                    <label class="btn btn-outline-primary" for="styleElapsedTimeNone">
+                        {{ $t("None") }}
+                    </label>
+                </div>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script lang="js">
+export default {
+    props: {
+        languages: {
+            type: Array,
+            required: true,
+        },
+        language: {
+            type: String,
+            required: true,
+        },
+        userTheme: {
+            type: String,
+            required: true,
+        },
+        userHeartbeatBar: {
+            type: String,
+            required: true,
+        },
+        styleElapsedTime: {
+            type: String,
+            required: true,
+        },
+    },
+    emits: [
+        "update:language",
+        "update:userTheme",
+        "update:userHeartbeatBar",
+        "update:styleElapsedTime",
+    ],
+    data() {
+        return {};
+    },
+};
+</script>
+
+<style lang="scss" scoped>
+.btn-check:active+.btn-outline-primary,
+.btn-check:checked+.btn-outline-primary,
+.btn-check:hover+.btn-outline-primary {
+    color: #fff;
+
+    .dark & {
+        color: #000;
+    }
+}
+
+</style>
diff --git a/src/mixins/theme.js b/src/mixins/theme.js
index e1486d5af..a8479cfb7 100644
--- a/src/mixins/theme.js
+++ b/src/mixins/theme.js
@@ -13,6 +13,24 @@ export default {
     },
 
     mounted() {
+        if (window.defaultAppearance) {
+            if (window.defaultAppearance.language) {
+                localStorage.locale = window.defaultAppearance.language;
+            }
+
+            if (window.defaultAppearance.theme) {
+                this.userTheme = window.defaultAppearance.theme;
+            }
+
+            if (window.defaultAppearance.heartbeatBarTheme) {
+                this.userHeartbeatBar = window.defaultAppearance.heartbeatBarTheme;
+            }
+
+            if (window.defaultAppearance.styleElapsedTime) {
+                this.styleElapsedTime = window.defaultAppearance.styleElapsedTime;
+            }
+        }
+
         // Default Light
         if (! this.userTheme) {
             this.userTheme = "auto";
diff --git a/src/pages/Settings.vue b/src/pages/Settings.vue
index 96bb1fee1..40f5304ee 100644
--- a/src/pages/Settings.vue
+++ b/src/pages/Settings.vue
@@ -183,6 +183,15 @@ export default {
                     this.settings.trustProxy = false;
                 }
 
+                if (this.settings.defaultAppearance === undefined) {
+                    this.settings.defaultAppearance = {
+                        theme: null,
+                        language: null,
+                        heartbeatBarTheme: null,
+                        styleElapsedTime: null,
+                    };
+                }
+
                 this.settingsLoaded = true;
             });
         },

From 9e2c66657d3a7a39bfa172c49d74205cdf706b12 Mon Sep 17 00:00:00 2001
From: Nelson Chan <chakflying@hotmail.com>
Date: Mon, 12 Feb 2024 06:33:22 +0800
Subject: [PATCH 2/2] Chore: Fix lint

---
 src/components/HorizontalTabHeader.vue        |   9 +-
 .../settings/AppearanceSettings.vue           | 128 ++++++++++++++----
 2 files changed, 107 insertions(+), 30 deletions(-)

diff --git a/src/components/HorizontalTabHeader.vue b/src/components/HorizontalTabHeader.vue
index 3857394dc..00712ad0f 100644
--- a/src/components/HorizontalTabHeader.vue
+++ b/src/components/HorizontalTabHeader.vue
@@ -2,7 +2,12 @@
     <div>
         <ul class="nav nav-tabs">
             <li v-for="(tab, index) in tabs" :key="index" class="nav-item">
-                <a class="nav-link" :class="{ active: index == selected }" href="#" @click="$emit('update:selected', index)">
+                <a
+                    class="nav-link"
+                    :class="{ active: index == selected }"
+                    href="#"
+                    @click="$emit('update:selected', index)"
+                >
                     {{ tab }}
                 </a>
             </li>
@@ -45,10 +50,12 @@ export default {
 
             &.active {
                 background-color: $highlight-white;
+
                 .dark & {
                     color: $dark-font-color;
                     background-color: $dark-header-bg;
                 }
+
                 border-color: transparent transparent $primary transparent;
                 border-width: 1px 1px 6px 1px;
             }
diff --git a/src/components/settings/AppearanceSettings.vue b/src/components/settings/AppearanceSettings.vue
index 54c3470b9..670fd7164 100644
--- a/src/components/settings/AppearanceSettings.vue
+++ b/src/components/settings/AppearanceSettings.vue
@@ -5,10 +5,16 @@
                 {{ $t("Language") }}
             </label>
             <select
-                id="language" :value="language" class="form-select"
+                id="language"
+                :value="language"
+                class="form-select"
                 @input="$emit('update:language', $event.target.value)"
             >
-                <option v-for="(lang, i) in languages" :key="`Lang${i}`" :value="lang.value">
+                <option
+                    v-for="(lang, i) in languages"
+                    :key="`Lang${i}`"
+                    :value="lang.value"
+                >
                     {{ lang.label }}
                 </option>
             </select>
@@ -16,26 +22,48 @@
         <div class="my-4">
             <label for="timezone" class="form-label">{{ $t("Theme") }}</label>
             <div>
-                <div class="btn-group" role="group" aria-label="Basic checkbox toggle button group">
+                <div
+                    class="btn-group"
+                    role="group"
+                    aria-label="Basic checkbox toggle button group"
+                >
                     <input
-                        id="btncheck1" :checked="userTheme == 'light'" type="radio" class="btn-check" name="theme"
-                        autocomplete="off" value="light" @input="$emit('update:userTheme', 'light')"
+                        id="btncheck1"
+                        :checked="userTheme == 'light'"
+                        type="radio"
+                        class="btn-check"
+                        name="theme"
+                        autocomplete="off"
+                        value="light"
+                        @input="$emit('update:userTheme', 'light')"
                     />
                     <label class="btn btn-outline-primary" for="btncheck1">
                         {{ $t("Light") }}
                     </label>
 
                     <input
-                        id="btncheck2" :checked="userTheme == 'dark'" type="radio" class="btn-check" name="theme"
-                        autocomplete="off" value="dark" @input="$emit('update:userTheme', 'dark')"
+                        id="btncheck2"
+                        :checked="userTheme == 'dark'"
+                        type="radio"
+                        class="btn-check"
+                        name="theme"
+                        autocomplete="off"
+                        value="dark"
+                        @input="$emit('update:userTheme', 'dark')"
                     />
                     <label class="btn btn-outline-primary" for="btncheck2">
                         {{ $t("Dark") }}
                     </label>
 
                     <input
-                        id="btncheck3" :checked="userTheme == 'auto'" type="radio" class="btn-check" name="theme"
-                        autocomplete="off" value="auto" @input="$emit('update:userTheme', 'auto')"
+                        id="btncheck3"
+                        :checked="userTheme == 'auto'"
+                        type="radio"
+                        class="btn-check"
+                        name="theme"
+                        autocomplete="off"
+                        value="auto"
+                        @input="$emit('update:userTheme', 'auto')"
                     />
                     <label class="btn btn-outline-primary" for="btncheck3">
                         {{ $t("Auto") }}
@@ -46,10 +74,19 @@
         <div class="my-4">
             <label class="form-label">{{ $t("Theme - Heartbeat Bar") }}</label>
             <div>
-                <div class="btn-group" role="group" aria-label="Basic checkbox toggle button group">
+                <div
+                    class="btn-group"
+                    role="group"
+                    aria-label="Basic checkbox toggle button group"
+                >
                     <input
-                        id="btncheck4" :checked="userHeartbeatBar == 'normal'" type="radio" class="btn-check"
-                        name="heartbeatBarTheme" autocomplete="off" value="normal"
+                        id="btncheck4"
+                        :checked="userHeartbeatBar == 'normal'"
+                        type="radio"
+                        class="btn-check"
+                        name="heartbeatBarTheme"
+                        autocomplete="off"
+                        value="normal"
                         @input="$emit('update:userHeartbeatBar', 'normal')"
                     />
                     <label class="btn btn-outline-primary" for="btncheck4">
@@ -57,8 +94,13 @@
                     </label>
 
                     <input
-                        id="btncheck5" :checked="userHeartbeatBar == 'bottom'" type="radio" class="btn-check"
-                        name="heartbeatBarTheme" autocomplete="off" value="bottom"
+                        id="btncheck5"
+                        :checked="userHeartbeatBar == 'bottom'"
+                        type="radio"
+                        class="btn-check"
+                        name="heartbeatBarTheme"
+                        autocomplete="off"
+                        value="bottom"
                         @input="$emit('update:userHeartbeatBar', 'bottom')"
                     />
                     <label class="btn btn-outline-primary" for="btncheck5">
@@ -66,8 +108,13 @@
                     </label>
 
                     <input
-                        id="btncheck6" :checked="userHeartbeatBar == 'none'" type="radio" class="btn-check"
-                        name="heartbeatBarTheme" autocomplete="off" value="none"
+                        id="btncheck6"
+                        :checked="userHeartbeatBar == 'none'"
+                        type="radio"
+                        class="btn-check"
+                        name="heartbeatBarTheme"
+                        autocomplete="off"
+                        value="none"
                         @input="$emit('update:userHeartbeatBar', 'none')"
                     />
                     <label class="btn btn-outline-primary" for="btncheck6">
@@ -83,29 +130,53 @@
             <div>
                 <div class="btn-group" role="group">
                     <input
-                        id="styleElapsedTimeShowNoLine" :checked="styleElapsedTime == 'no-line'" type="radio"
-                        class="btn-check" name="styleElapsedTime" autocomplete="off" value="no-line"
+                        id="styleElapsedTimeShowNoLine"
+                        :checked="styleElapsedTime == 'no-line'"
+                        type="radio"
+                        class="btn-check"
+                        name="styleElapsedTime"
+                        autocomplete="off"
+                        value="no-line"
                         @input="$emit('update:styleElapsedTime', 'no-line')"
                     />
-                    <label class="btn btn-outline-primary" for="styleElapsedTimeShowNoLine">
+                    <label
+                        class="btn btn-outline-primary"
+                        for="styleElapsedTimeShowNoLine"
+                    >
                         {{ $t("styleElapsedTimeShowNoLine") }}
                     </label>
 
                     <input
-                        id="styleElapsedTimeShowWithLine" :checked="styleElapsedTime == 'with-line'" type="radio"
-                        class="btn-check" name="styleElapsedTime" autocomplete="off" value="with-line"
+                        id="styleElapsedTimeShowWithLine"
+                        :checked="styleElapsedTime == 'with-line'"
+                        type="radio"
+                        class="btn-check"
+                        name="styleElapsedTime"
+                        autocomplete="off"
+                        value="with-line"
                         @input="$emit('update:styleElapsedTime', 'with-line')"
                     />
-                    <label class="btn btn-outline-primary" for="styleElapsedTimeShowWithLine">
+                    <label
+                        class="btn btn-outline-primary"
+                        for="styleElapsedTimeShowWithLine"
+                    >
                         {{ $t("styleElapsedTimeShowWithLine") }}
                     </label>
 
                     <input
-                        id="styleElapsedTimeNone" :checked="styleElapsedTime == 'none'" type="radio" class="btn-check"
-                        name="styleElapsedTime" autocomplete="off" value="none"
+                        id="styleElapsedTimeNone"
+                        :checked="styleElapsedTime == 'none'"
+                        type="radio"
+                        class="btn-check"
+                        name="styleElapsedTime"
+                        autocomplete="off"
+                        value="none"
                         @input="$emit('update:styleElapsedTime', 'none')"
                     />
-                    <label class="btn btn-outline-primary" for="styleElapsedTimeNone">
+                    <label
+                        class="btn btn-outline-primary"
+                        for="styleElapsedTimeNone"
+                    >
                         {{ $t("None") }}
                     </label>
                 </div>
@@ -151,14 +222,13 @@ export default {
 </script>
 
 <style lang="scss" scoped>
-.btn-check:active+.btn-outline-primary,
-.btn-check:checked+.btn-outline-primary,
-.btn-check:hover+.btn-outline-primary {
+.btn-check:active + .btn-outline-primary,
+.btn-check:checked + .btn-outline-primary,
+.btn-check:hover + .btn-outline-primary {
     color: #fff;
 
     .dark & {
         color: #000;
     }
 }
-
 </style>